"use client"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@app/components/ui/form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useTranslations } from "next-intl"; import { useActionState } from "react"; import { useForm } from "react-hook-form"; import z from "zod"; import { SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionForm, SettingsSectionHeader, SettingsSectionTitle } from "./Settings"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { build } from "@server/build"; import { validateLocalPath } from "@app/lib/validateLocalPath"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; import type { GetLoginPageBrandingResponse } from "@server/routers/loginPage/types"; import { XIcon } from "lucide-react"; import { useRouter } from "next/navigation"; import { PaidFeaturesAlert } from "./PaidFeaturesAlert"; import { Button } from "./ui/button"; import { Input } from "./ui/input"; export type AuthPageCustomizationProps = { orgId: string; branding: GetLoginPageBrandingResponse | null; }; const AuthPageFormSchema = z.object({ logoUrl: z .string() .optional() .transform((val) => (val === "" ? undefined : val)), logoWidth: z.coerce.number().min(1), logoHeight: z.coerce.number().min(1), orgTitle: z.string().optional(), orgSubtitle: z.string().optional(), resourceTitle: z.string(), resourceSubtitle: z.string().optional(), primaryColor: z .string() .regex(/^#([0-9a-f]{6}|[0-9a-f]{3})$/i) .optional() }); export default function AuthPageBrandingForm({ orgId, branding }: AuthPageCustomizationProps) { const env = useEnvContext(); const api = createApiClient(env); const { isPaidUser } = usePaidStatus(); const router = useRouter(); const [, updateFormAction, isUpdatingBranding] = useActionState( updateBranding, null ); const [, deleteFormAction, isDeletingBranding] = useActionState( deleteBranding, null ); const t = useTranslations(); const form = useForm({ resolver: zodResolver(AuthPageFormSchema), defaultValues: { logoUrl: branding?.logoUrl ?? "", logoWidth: branding?.logoWidth ?? 100, logoHeight: branding?.logoHeight ?? 100, orgTitle: branding?.orgTitle ?? `Log in to {{orgName}}`, orgSubtitle: branding?.orgSubtitle ?? `Log in to {{orgName}}`, resourceTitle: branding?.resourceTitle ?? `Authenticate to access {{resourceName}}`, resourceSubtitle: branding?.resourceSubtitle ?? `Choose your preferred authentication method for {{resourceName}}`, primaryColor: branding?.primaryColor ?? `#f36117` // default pangolin primary color }, disabled: !isPaidUser(tierMatrix.loginPageBranding) }); async function updateBranding() { const isValid = await form.trigger(); const brandingData = form.getValues(); if (!isValid || !isPaidUser(tierMatrix.loginPageBranding)) return; try { const updateRes = await api.put( `/org/${orgId}/login-page-branding`, { ...brandingData } ); if (updateRes.status === 200 || updateRes.status === 201) { router.refresh(); toast({ variant: "default", title: t("success"), description: t("authPageBrandingUpdated") }); } } catch (error) { toast({ variant: "destructive", title: t("authPageErrorUpdate"), description: formatAxiosError( error, t("authPageErrorUpdateMessage") ) }); } } async function deleteBranding() { try { const updateRes = await api.delete( `/org/${orgId}/login-page-branding` ); if (updateRes.status === 200) { router.refresh(); form.reset(); toast({ variant: "default", title: t("success"), description: t("authPageBrandingRemoved") }); form.reset(); } } catch (error) { toast({ variant: "destructive", title: t("authPageErrorUpdate"), description: formatAxiosError( error, t("authPageErrorUpdateMessage") ) }); } } return ( <> {t("authPageBranding")} {t("authPageBrandingDescription")}
( {t("brandingPrimaryColor")}
)} />
( {build === "enterprise" ? t( "brandingLogoURLOrPath" ) : t("brandingLogoURL")} {build === "enterprise" ? t( "brandingLogoPathDescription" ) : t( "brandingLogoURLDescription" )} )} />
( {t("brandingLogoWidth")} )} /> ( {t( "brandingLogoHeight" )} )} />
{build === "saas" || env.env.app.identityProviderMode === "org" ? ( <>
{t( "organizationLoginPageTitle" )} {t( "organizationLoginPageDescription" )}
( {t( "brandingOrgTitle" )} )} /> ( {t( "brandingOrgSubtitle" )} )} />
) : null}
{t("resourceLoginPageTitle")} {t("resourceLoginPageDescription")}
( {t("brandingResourceTitle")} )} /> ( {t( "brandingResourceSubtitle" )} )} />
{branding && (
)}
); }