From 02cd2cfb1705e8c0ac9e6f32cf40abe6daf601a7 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Thu, 13 Nov 2025 02:18:52 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20save=20and=20update=20branding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- messages/en-US.json | 5 + .../loginPage/deleteLoginPageBranding.ts | 2 +- .../routers/loginPage/getLoginPageBranding.ts | 2 +- .../loginPage/upsertLoginPageBranding.ts | 2 +- .../settings/general/auth-page/page.tsx | 14 +- src/components/AuthPageBrandingForm.tsx | 180 +++++++++++++++--- 6 files changed, 166 insertions(+), 39 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 7b395d0a..2531350e 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1737,6 +1737,11 @@ "authPageDomain": "Auth Page Domain", "authPageBranding": "Branding", "authPageBrandingDescription": "Configure the branding for the auth page for your organization", + "authPageBrandingUpdated": "Auth page Branding updated successfully", + "authPageBrandingRemoved": "Auth page Branding removed successfully", + "authPageBrandingRemoveTitle": "Remove Auth Page Branding", + "authPageBrandingQuestionRemove": "Are you sure you want to remove the branding for Auth Pages ?", + "authPageBrandingDeleteConfirm": "Confirm Delete Branding", "brandingLogoURL": "Logo URL", "brandingLogoWidth": "Width (px)", "brandingLogoHeight": "Height (px)", diff --git a/server/private/routers/loginPage/deleteLoginPageBranding.ts b/server/private/routers/loginPage/deleteLoginPageBranding.ts index 03bdf805..1fb243b0 100644 --- a/server/private/routers/loginPage/deleteLoginPageBranding.ts +++ b/server/private/routers/loginPage/deleteLoginPageBranding.ts @@ -102,7 +102,7 @@ export async function deleteLoginPageBranding( success: true, error: false, message: "Login page branding deleted successfully", - status: HttpCode.CREATED + status: HttpCode.OK }); } catch (error) { logger.error(error); diff --git a/server/private/routers/loginPage/getLoginPageBranding.ts b/server/private/routers/loginPage/getLoginPageBranding.ts index e0670545..262e9ce8 100644 --- a/server/private/routers/loginPage/getLoginPageBranding.ts +++ b/server/private/routers/loginPage/getLoginPageBranding.ts @@ -92,7 +92,7 @@ export async function getLoginPageBranding( success: true, error: false, message: "Login page branding retrieved successfully", - status: HttpCode.CREATED + status: HttpCode.OK }); } catch (error) { logger.error(error); diff --git a/server/private/routers/loginPage/upsertLoginPageBranding.ts b/server/private/routers/loginPage/upsertLoginPageBranding.ts index 51aa7392..e553d14d 100644 --- a/server/private/routers/loginPage/upsertLoginPageBranding.ts +++ b/server/private/routers/loginPage/upsertLoginPageBranding.ts @@ -143,7 +143,7 @@ export async function upsertLoginPageBranding( message: existingLoginPageBranding ? "Login page branding updated successfully" : "Login page branding created successfully", - status: HttpCode.CREATED + status: existingLoginPageBranding ? HttpCode.OK : HttpCode.CREATED }); } catch (error) { logger.error(error); diff --git a/src/app/[orgId]/settings/general/auth-page/page.tsx b/src/app/[orgId]/settings/general/auth-page/page.tsx index a0c883f0..0030afe1 100644 --- a/src/app/[orgId]/settings/general/auth-page/page.tsx +++ b/src/app/[orgId]/settings/general/auth-page/page.tsx @@ -1,7 +1,8 @@ import AuthPageBrandingForm from "@app/components/AuthPageBrandingForm"; import AuthPageSettings from "@app/components/private/AuthPageSettings"; import { SettingsContainer } from "@app/components/Settings"; -import { priv } from "@app/lib/api"; +import { internal, priv } from "@app/lib/api"; +import { authCookieHeader } from "@app/lib/api/cookies"; import { getCachedSubscription } from "@app/lib/api/getCachedSubscription"; import { pullEnv } from "@app/lib/pullEnv"; import { build } from "@server/build"; @@ -37,8 +38,9 @@ export default async function AuthPage(props: AuthPageProps) { let loginPage: GetLoginPageResponse | null = null; try { - const res = await priv.get>( - `/org/${orgId}/login-page` + const res = await internal.get>( + `/org/${orgId}/login-page`, + await authCookieHeader() ); if (res.status === 200) { loginPage = res.data.data; @@ -47,9 +49,9 @@ export default async function AuthPage(props: AuthPageProps) { let loginPageBranding: GetLoginPageBrandingResponse | null = null; try { - const res = await priv.get>( - `/org/${orgId}/login-page-branding` - ); + const res = await internal.get< + AxiosResponse + >(`/org/${orgId}/login-page-branding`, await authCookieHeader()); if (res.status === 200) { loginPageBranding = res.data.data; } diff --git a/src/components/AuthPageBrandingForm.tsx b/src/components/AuthPageBrandingForm.tsx index 14474f12..39cac35a 100644 --- a/src/components/AuthPageBrandingForm.tsx +++ b/src/components/AuthPageBrandingForm.tsx @@ -1,7 +1,7 @@ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; -import * as React from "react"; +import { useActionState, useState } from "react"; import { useForm } from "react-hook-form"; import z from "zod"; import { @@ -22,18 +22,25 @@ import { SettingsSectionTitle } from "./Settings"; import { useTranslations } from "next-intl"; -import AuthPageSettings, { - AuthPageSettingsRef -} from "./private/AuthPageSettings"; -import type { - GetLoginPageBrandingResponse, - GetLoginPageResponse -} from "@server/routers/loginPage/types"; + +import type { GetLoginPageBrandingResponse } from "@server/routers/loginPage/types"; import { Input } from "./ui/input"; -import { XIcon } from "lucide-react"; +import { Trash2, XIcon } from "lucide-react"; import { Button } from "./ui/button"; import { Separator } from "./ui/separator"; -import { build } from "@server/build"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import { useRouter } from "next/navigation"; +import { toast } from "@app/hooks/useToast"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "./Credenza"; export type AuthPageCustomizationProps = { orgId: string; @@ -74,7 +81,21 @@ export default function AuthPageBrandingForm({ orgId, branding }: AuthPageCustomizationProps) { - const [, formAction, isSubmitting] = React.useActionState(onSubmit, null); + const env = useEnvContext(); + const api = createApiClient(env); + + const router = useRouter(); + + const [, updateFormAction, isUpdatingBranding] = useActionState( + updateBranding, + null + ); + const [, deleteFormAction, isDeletingBranding] = useActionState( + deleteBranding, + null + ); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const t = useTranslations(); const form = useForm({ @@ -94,14 +115,70 @@ export default function AuthPageBrandingForm({ } }); - async function onSubmit() { - console.log({ - dirty: form.formState.isDirty - }); + async function updateBranding() { const isValid = await form.trigger(); + const brandingData = form.getValues(); if (!isValid) return; - // ... + try { + // Update or existing auth page domain + const updateRes = await api.put( + `/org/${orgId}/login-page-branding`, + { + ...brandingData + } + ); + + if (updateRes.status === 200 || updateRes.status === 201) { + // update the data from the API + 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 { + // Update or existing auth page domain + const updateRes = await api.delete( + `/org/${orgId}/login-page-branding` + ); + + if (updateRes.status === 200) { + // update the data from the API + router.refresh(); + form.reset(); + setIsDeleteModalOpen(false); + + toast({ + variant: "default", + title: t("success"), + description: t("authPageBrandingRemoved") + }); + } + } catch (error) { + toast({ + variant: "destructive", + title: t("authPageErrorUpdate"), + description: formatAxiosError( + error, + t("authPageErrorUpdateMessage") + ) + }); + } } return ( @@ -120,7 +197,7 @@ export default function AuthPageBrandingForm({
@@ -293,22 +370,65 @@ export default function AuthPageBrandingForm({ -
- {/* {branding && ( - - )} */} + + + + + {t("authPageBrandingRemoveTitle")} + + + +

{t("authPageBrandingQuestionRemove")}

+
+ {t("cannotbeUndone")} +
+ +
+ + + + + + +
+
+ +
+ {branding && ( + + )}