Files
calendar/packages/features/data-table/components/segment/DuplicateSegmentDialog.tsx
T
Eunjae LeeGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2266301469 feat: implement system filter segments for data tables (#22939)
* feat: implement default filter segments for data tables

- Add defaultSegmentId column to UserFilterSegmentPreference table
- Support mixed segment ID types (number for user segments, string for default segments)
- Add DefaultFilterSegment and CombinedFilterSegment types
- Update useSegments hook to handle default segments with date range recalculation
- Modify FilterSegmentSelect to group and display default segments separately
- Add default segments to bookings view (My Bookings, Upcoming Bookings)
- Prevent editing/deleting of default segments in SaveFilterSegmentButton
- Update DataTableProvider to support defaultSegments prop
- Update parsers and repository to handle mixed segment ID types

Implements frontend-only default segments as specified in the requirements.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* chore: update ESLint dependencies to resolve configuration issues

- Update eslint-config-next and @typescript-eslint packages to latest versions
- Fix dependency compatibility issues that were blocking pre-commit hooks

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* revert eslint change

* some changes

* refactor: implement discriminated union for segment types

- Update SegmentIdentifier to use discriminated union with 'custom' vs 'default' types
- Refactor setSegmentId to accept object parameters: { id: string; type: 'default' } | { id: number; type: 'custom' }
- Update type definitions with DefaultFilterSegment, CustomFilterSegment, and CombinedFilterSegment
- Modify useSegments hook to handle new segment type structure
- Update FilterSegmentSelect component to work with discriminated unions
- Refactor database preference handling to store segment type alongside ID
- Add proper type safety throughout the data flow
- Remove description property from DefaultFilterSegment type

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: update unit test to expect discriminated union format for preferredSegmentId

The test was expecting preferredSegmentId to be a number, but after the discriminated union refactor it now returns { id: number, type: 'custom' } for custom segments. Updated the assertion to use toEqual() for deep object comparison.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: correct SegmentIdentifier type to exclude undefined

- Remove null from SegmentIdentifier type definition since null/undefined indicate absence of identifier
- Update DataTableProvider and useSegments to handle SegmentIdentifier | null properly
- Fix type safety while maintaining discriminated union functionality
- Ensure setAndPersistSegmentId accepts null values for clearing segments

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* remove unused segment

* a little update

* feat: add default- prefix to default segment IDs for clearer type identification

- Update 'my_bookings' to 'default-my_bookings'
- Update 'upcoming-bookings' to 'default-upcoming-bookings'
- Makes it easier to verify segment type by looking at the ID string
- Maintains all existing discriminated union functionality

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: rename segment types from default/custom to system/user for better semantics

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: update unit test to expect 'user' type instead of 'custom' in discriminated union

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: complete type renaming from default/custom to system/user across all component files

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: update segment ID prefix from default- to system- to match type naming

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* remove unused segment

* some fixes

* revert schema change

* rename defaultSegmentId to systemSegmentId

* renaming

* fix save button

* remove as any

* clean up prisma migrations

* type fixes

* many fixes

* remove icon property

* fix infinite rendering

* fix race condition

* re-visiting useSegments implementation WIP

* extract useElementByClassName

* add e2e tests

* fix a bug that the created segment was not selected automatically

* add useSegments to /insights and fix e2e test

* fix type error

* fix type error

* apply feedback

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-08-22 10:18:18 +03:00

112 lines
3.0 KiB
TypeScript

import { useSession } from "next-auth/react";
import { useForm } from "react-hook-form";
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/components/dialog";
import { Form, TextField } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
import { useDataTable } from "../../hooks";
import type { CombinedFilterSegment } from "../../lib/types";
type FormValues = {
name: string;
};
export function DuplicateSegmentDialog({
segment,
onClose,
}: {
segment: CombinedFilterSegment;
onClose: () => void;
}) {
const { t } = useLocale();
const form = useForm<FormValues>({
defaultValues: {
name: "",
},
});
const { setSegmentId } = useDataTable();
const utils = trpc.useUtils();
const session = useSession();
const isAdminOrOwner = checkAdminOrOwner(session.data?.user?.org?.role);
const { mutate: createSegment, isPending } = trpc.viewer.filterSegments.create.useMutation({
onSuccess: ({ id }) => {
utils.viewer.filterSegments.list.invalidate();
showToast(t("filter_segment_duplicated"), "success");
setSegmentId({ id, type: "user" });
onClose();
},
onError: () => {
showToast(t("error_duplicating_filter_segment"), "error");
},
});
const handleSubmit = (data: FormValues) => {
if (!segment) {
return;
}
if (segment.type === "user") {
const { type: _type, id: _id, name: _name, team: _team, teamId, ...rest } = segment;
if (segment.scope === "TEAM" && isAdminOrOwner) {
createSegment({
...rest,
teamId: teamId ?? 0,
scope: "TEAM",
name: data.name,
});
} else {
createSegment({
...rest,
scope: "USER",
name: data.name,
});
}
} else if (segment.type === "system") {
const { type: _type, ...rest } = segment;
createSegment({
...rest,
scope: "USER",
name: data.name,
});
}
};
return (
<Dialog
open
onOpenChange={(open) => {
if (!open) {
onClose();
}
}}>
<DialogContent>
<DialogHeader title={t("duplicate_segment")} />
<Form form={form} handleSubmit={handleSubmit}>
<div className="space-y-4">
<TextField
required
data-testid="duplicate-segment-name"
type="text"
label={t("name")}
{...form.register("name")}
/>
<DialogFooter>
<Button color="minimal" onClick={onClose}>
{t("cancel")}
</Button>
<Button type="submit" loading={isPending}>
{t("duplicate")}
</Button>
</DialogFooter>
</div>
</Form>
</DialogContent>
</Dialog>
);
}