diff --git a/apps/web/package.json b/apps/web/package.json index fd4faa0c1..3f485d9bd 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -42,6 +42,7 @@ "react-hotkeys-hook": "^4.4.1", "react-icons": "^4.11.0", "react-rnd": "^10.4.1", + "remeda": "^1.27.1", "sharp": "0.33.1", "ts-pattern": "^5.0.5", "typescript": "5.2.2", diff --git a/apps/web/src/app/(dashboard)/admin/nav.tsx b/apps/web/src/app/(dashboard)/admin/nav.tsx index 089861069..b0d652283 100644 --- a/apps/web/src/app/(dashboard)/admin/nav.tsx +++ b/apps/web/src/app/(dashboard)/admin/nav.tsx @@ -1,11 +1,11 @@ 'use client'; -import { HTMLAttributes } from 'react'; +import type { HTMLAttributes } from 'react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import { BarChart3, FileStack, User2, Wallet2 } from 'lucide-react'; +import { BarChart3, FileStack, Settings, User2, Wallet2 } from 'lucide-react'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -78,6 +78,20 @@ export const AdminNav = ({ className, ...props }: AdminNavProps) => { Subscriptions + + ); }; diff --git a/apps/web/src/app/(dashboard)/admin/site-settings/banner-form.tsx b/apps/web/src/app/(dashboard)/admin/site-settings/banner-form.tsx new file mode 100644 index 000000000..351e146ff --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/site-settings/banner-form.tsx @@ -0,0 +1,200 @@ +'use client'; + +import { useRouter } from 'next/navigation'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import type { z } from 'zod'; + +import type { TSiteSettingsBannerSchema } from '@documenso/lib/server-only/site-settings/schemas/banner'; +import { + SITE_SETTINGS_BANNER_ID, + ZSiteSettingsBannerSchema, +} from '@documenso/lib/server-only/site-settings/schemas/banner'; +import { TRPCClientError } from '@documenso/trpc/client'; +import { trpc as trpcReact } from '@documenso/trpc/react'; +import { Button } from '@documenso/ui/primitives/button'; +import { ColorPicker } from '@documenso/ui/primitives/color-picker'; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@documenso/ui/primitives/form/form'; +import { Switch } from '@documenso/ui/primitives/switch'; +import { Textarea } from '@documenso/ui/primitives/textarea'; +import { useToast } from '@documenso/ui/primitives/use-toast'; + +const ZBannerFormSchema = ZSiteSettingsBannerSchema; + +type TBannerFormSchema = z.infer; + +export type BannerFormProps = { + banner?: TSiteSettingsBannerSchema; +}; + +export function BannerForm({ banner }: BannerFormProps) { + const router = useRouter(); + const { toast } = useToast(); + + const form = useForm({ + resolver: zodResolver(ZBannerFormSchema), + defaultValues: { + id: SITE_SETTINGS_BANNER_ID, + enabled: banner?.enabled ?? false, + data: { + content: banner?.data?.content ?? '', + bgColor: banner?.data?.bgColor ?? '#000000', + textColor: banner?.data?.textColor ?? '#FFFFFF', + }, + }, + }); + + const enabled = form.watch('enabled'); + + const { mutateAsync: updateSiteSetting, isLoading: isUpdateSiteSettingLoading } = + trpcReact.admin.updateSiteSetting.useMutation(); + + const onBannerUpdate = async ({ id, enabled, data }: TBannerFormSchema) => { + try { + await updateSiteSetting({ + id, + enabled, + data, + }); + + toast({ + title: 'Banner Updated', + description: 'Your banner has been updated successfully.', + duration: 5000, + }); + + router.refresh(); + } catch (err) { + if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { + toast({ + title: 'An error occurred', + description: err.message, + variant: 'destructive', + }); + } else { + toast({ + title: 'An unknown error occurred', + variant: 'destructive', + description: + 'We encountered an unknown error while attempting to update the banner. Please try again later.', + }); + } + } + }; + + return ( +
+

Site Banner

+

+ The site banner is a message that is shown at the top of the site. It can be used to display + important information to your users. +

+ +
+ +
+ ( + + Enabled + + +
+ +
+
+
+ )} + /> + +
+ ( + + Background Color + + +
+ +
+
+ + +
+ )} + /> + + ( + + Text Color + + +
+ +
+
+ + +
+ )} + /> +
+
+ +
+ ( + + Content + + +