diff --git a/dashboard/src/features/users/dialogs/mutation/fields/services.tsx b/dashboard/src/features/users/dialogs/mutation/fields/services.tsx
@@ -1,4 +1,7 @@
import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
Checkbox,
FormControl,
FormField,
@@ -6,7 +9,10 @@ import {
FormLabel,
FormMessage,
ScrollArea,
+ Skeleton,
} from "@marzneshin/components";
+import { Link } from "@tanstack/react-router";
+import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import {
useServicesQuery,
type ServiceType,
@@ -29,34 +35,81 @@ interface ServiceCheckboxCardProps {
const ServiceCheckboxCard: FC<ServiceCheckboxCardProps> = ({
service,
field,
-}) => {
+}) => (
+ <div
+ className={cn(
+ "flex mb-2 flex-row gap-2 items-center p-3 rounded-md border",
+ { "bg-secondary": field.value?.includes(service.id) },
+ )}
+ >
+ <Checkbox
+ checked={field.value?.includes(service.id)}
+ onCheckedChange={(checked) =>
+ checked
+ ? field.onChange([...field.value, service.id])
+ : field.onChange(
+ field.value?.filter((value: number) => value !== service.id),
+ )
+ }
+ />
+ <FormLabel className="flex flex-row justify-between items-center w-full">
+ <ServiceCard service={service} />
+ </FormLabel>
+ </div>
+);
+
+const ServiceSkeletons: FC = () => (
+ <>
+ <Skeleton className="w-full h-[2.4rem]" />
+ <Skeleton className="w-full h-[2.4rem]" />
+ <Skeleton className="w-full h-[2.4rem]" />
+ </>
+);
+
+const ServicesList: FC<{ services: ServiceType[] }> = ({ services }) => {
+ const form = useFormContext();
+
return (
- <div
- className={cn(
- "flex mb-2 flex-row gap-2 items-center p-3 rounded-md border",
- { "bg-secondary": field.value?.includes(service.id) },
- )}
- >
- <Checkbox
- checked={field.value?.includes(service.id)}
- onCheckedChange={(checked) => {
- return checked
- ? field.onChange([...field.value, service.id])
- : field.onChange(
- field.value?.filter((value: number) => value !== service.id),
- );
- }}
- />
- <FormLabel className="flex flex-row justify-between items-center w-full">
- <ServiceCard service={service} />
- </FormLabel>
- </div>
+ <ScrollArea className="flex flex-col justify-start px-1 h-full max-h-[20rem]">
+ {services.map((service) => (
+ <FormField
+ key={service.id}
+ control={form.control}
+ name="service_ids"
+ render={({ field }) => (
+ <FormItem key={service.id}>
+ <FormControl>
+ <ServiceCheckboxCard service={service} field={field} />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ ))}
+ </ScrollArea>
+ );
+};
+
+const ServicesAlert: FC = () => {
+ const { t } = useTranslation();
+
+ return (
+ <Alert className="ring-1 ring-destructive-accent">
+ <ExclamationTriangleIcon color="#E5484D" className="mr-2 text-destructive-accent" />
+ <AlertTitle className="font-semibold text-destructive">{t('page.users.services-alert.title')}</AlertTitle>
+ <AlertDescription>
+ {t('page.users.services-alert.desc')}
+ <Link className="m-1 font-semibold text-secondary-foreground" to="/services">
+ {t('page.users.services-alert.click')}
+ </Link>
+ </AlertDescription>
+ </Alert>
);
};
export const ServicesField: FC = () => {
const form = useFormContext();
- const { data } = useServicesQuery({ page: 1, size: 100 });
+ const { data, isFetching } = useServicesQuery({ page: 1, size: 100 });
const { t } = useTranslation();
return (
@@ -66,23 +119,13 @@ export const ServicesField: FC = () => {
render={() => (
<FormItem>
<FormLabel>{t("services")}</FormLabel>
- <ScrollArea className="flex flex-col justify-start px-1 h-full max-h-[20rem]">
- {data.entity.map((service) => (
- <FormField
- key={service.id}
- control={form.control}
- name="service_ids"
- render={({ field }) => (
- <FormItem key={service.id}>
- <FormControl>
- <ServiceCheckboxCard service={service} field={field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- ))}
- </ScrollArea>
+ {isFetching ? (
+ <ServiceSkeletons />
+ ) : data.entity.length !== 0 ? (
+ <ServicesList services={data.entity} />
+ ) : (
+ <ServicesAlert />
+ )}
</FormItem>
)}
/>
diff --git a/dashboard/src/features/users/dialogs/mutation/index.tsx b/dashboard/src/features/users/dialogs/mutation/index.tsx
@@ -71,7 +71,7 @@ export const UsersMutationDialog: FC<UsersMutationDialogProps> = ({
return (
<Dialog open={open} onOpenChange={onOpenChange} defaultOpen={true}>
<DialogContent className="min-w-full h-full md:h-auto md:min-w-[42rem]">
- <ScrollArea className="flex flex-col justify-between h-full p-0">
+ <ScrollArea className="flex flex-col justify-between h-full">
<DialogHeader>
<DialogTitle className="text-primary">
{entity
@@ -80,7 +80,7 @@ export const UsersMutationDialog: FC<UsersMutationDialogProps> = ({
</DialogTitle>
</DialogHeader>
<Form {...form}>
- <form onSubmit={handleSubmit} className="">
+ <form onSubmit={handleSubmit} className=" m-2">
<div className="flex-col grid-cols-2 gap-2 sm:flex md:grid h-full">
<div>
<UsernameField disabled={!!entity?.username} />