gitscope
← marzneshin / v0.5.3 / dashboard/src/features/services/components/form-fields/services.tsx
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/features/services/components/form-fields/services.tsx · TypeScript
Blame Raw
  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/features/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.entity.length === 0}
127                        NotFound={<ServicesAlert />}
128                        Component={<ServicesList services={data.entity} />}
129                    />
130                </FormItem>
131            )}
132        />
133    );
134};