1import {
2 Alert,
3 AlertDescription,
4 AlertTitle,
5 Checkbox,
6 FormControl,
7 FormField,
8 FormItem,
9 FormLabel,
10 FormMessage,
11 ScrollArea,
12 Skeleton,
13 Awaiting
14} from "@marzneshin/components";
15import { Link } from "@tanstack/react-router";
16import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
17import {
18 useServicesQuery,
19 type ServiceType,
20} from "@marzneshin/features/services";
21import { ServiceCard } from "@marzneshin/modules/users";
22import { cn } from "@marzneshin/utils";
23import type { FC } from "react";
24import {
25 useFormContext,
26 type FieldValues,
27 type ControllerRenderProps,
28} from "react-hook-form";
29import { useTranslation } from "react-i18next";
30
31interface ServiceCheckboxCardProps {
32 service: ServiceType;
33 field: ControllerRenderProps<FieldValues, "service_ids">;
34}
35
36const ServiceCheckboxCard: FC<ServiceCheckboxCardProps> = ({
37 service,
38 field,
39}) => (
40 <div
41 className={cn(
42 "flex mb-2 flex-row gap-2 items-center p-3 rounded-md border",
43 { "bg-secondary": field.value?.includes(service.id) },
44 )}
45 >
46 <Checkbox
47 checked={field.value?.includes(service.id)}
48 onCheckedChange={(checked) =>
49 checked
50 ? field.onChange([...field.value, service.id])
51 : field.onChange(
52 field.value?.filter((value: number) => value !== service.id),
53 )
54 }
55 />
56 <FormLabel className="flex flex-row justify-between items-center w-full">
57 <ServiceCard service={service} />
58 </FormLabel>
59 </div>
60);
61
62const ServiceSkeletons: FC = () => (
63 <>
64 <Skeleton className="w-full h-[2.4rem]" />
65 <Skeleton className="w-full h-[2.4rem]" />
66 <Skeleton className="w-full h-[2.4rem]" />
67 </>
68);
69
70const ServicesList: FC<{ services: ServiceType[] }> = ({ services }) => {
71 const form = useFormContext();
72
73 return (
74 <ScrollArea className="flex flex-col justify-start px-1 h-full max-h-[20rem]">
75 {services.map((service) => (
76 <FormField
77 key={service.id}
78 control={form.control}
79 name="service_ids"
80 render={({ field }) => (
81 <FormItem key={service.id}>
82 <FormControl>
83 <ServiceCheckboxCard service={service} field={field} />
84 </FormControl>
85 <FormMessage />
86 </FormItem>
87 )}
88 />
89 ))}
90 </ScrollArea>
91 );
92};
93
94const ServicesAlert: FC = () => {
95 const { t } = useTranslation();
96
97 return (
98 <Alert className="ring-1 ring-destructive-accent">
99 <ExclamationTriangleIcon color="#E5484D" className="mr-2 text-destructive-accent" />
100 <AlertTitle className="font-semibold text-destructive">{t('page.users.services-alert.title')}</AlertTitle>
101 <AlertDescription>
102 {t('page.users.services-alert.desc')}
103 <Link className="m-1 font-semibold text-secondary-foreground" to="/services">
104 {t('page.users.services-alert.click')}
105 </Link>
106 </AlertDescription>
107 </Alert>
108 );
109};
110
111export const ServicesField: FC = () => {
112 const form = useFormContext();
113 const { data, isFetching } = useServicesQuery({ page: 1, size: 100 });
114 const { t } = useTranslation();
115
116 return (
117 <FormField
118 control={form.control}
119 name="service_ids"
120 render={() => (
121 <FormItem>
122 <FormLabel>{t("services")}</FormLabel>
123 <Awaiting
124 isFetching={isFetching}
125 Skeleton={<ServiceSkeletons />}
126 isNotFound={data.entities.length === 0}
127 NotFound={<ServicesAlert />}
128 Component={<ServicesList services={data.entities} />}
129 />
130 </FormItem>
131 )}
132 />
133 );
134};