save and update branding

This commit is contained in:
Fred KISSIE
2025-11-13 02:18:52 +01:00
parent d218a4bbc3
commit 02cd2cfb17
6 changed files with 166 additions and 39 deletions

View File

@@ -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)",

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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<AxiosResponse<GetLoginPageResponse>>(
`/org/${orgId}/login-page`
const res = await internal.get<AxiosResponse<GetLoginPageResponse>>(
`/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<AxiosResponse<GetLoginPageBrandingResponse>>(
`/org/${orgId}/login-page-branding`
);
const res = await internal.get<
AxiosResponse<GetLoginPageBrandingResponse>
>(`/org/${orgId}/login-page-branding`, await authCookieHeader());
if (res.status === 200) {
loginPageBranding = res.data.data;
}

View File

@@ -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({
<SettingsSectionForm>
<Form {...form}>
<form
action={formAction}
action={updateFormAction}
id="auth-page-branding-form"
className="flex flex-col gap-8 items-stretch"
>
@@ -293,22 +370,65 @@ export default function AuthPageBrandingForm({
</SettingsSectionForm>
</SettingsSectionBody>
<div className="flex justify-end gap-2 mt-6">
{/* {branding && (
<Button
type="submit"
form="auth-page-branding-form"
loading={isSubmitting}
disabled={isSubmitting}
>
{t("saveSettings")}
</Button>
)} */}
<Credenza
open={isDeleteModalOpen}
onOpenChange={setIsDeleteModalOpen}
>
<CredenzaContent>
<CredenzaHeader>
<CredenzaTitle>
{t("authPageBrandingRemoveTitle")}
</CredenzaTitle>
</CredenzaHeader>
<CredenzaBody className="mb-0 space-y-0 flex flex-col gap-1">
<p>{t("authPageBrandingQuestionRemove")}</p>
<div className="font-bold text-destructive">
{t("cannotbeUndone")}
</div>
<form
action={deleteFormAction}
id="confirm-delete-branding-form"
className="sr-only"
></form>
</CredenzaBody>
<CredenzaFooter>
<CredenzaClose asChild>
<Button variant="outline">{t("close")}</Button>
</CredenzaClose>
<Button
variant={"destructive"}
type="submit"
form="confirm-delete-branding-form"
loading={isDeletingBranding}
disabled={isDeletingBranding}
>
{t("authPageBrandingDeleteConfirm")}
</Button>
</CredenzaFooter>
</CredenzaContent>
</Credenza>
<div className="flex justify-end gap-2 mt-6 items-center">
{branding && (
<Button
variant="destructive"
type="button"
loading={isUpdatingBranding || isDeletingBranding}
disabled={isUpdatingBranding || isDeletingBranding}
onClick={() => {
setIsDeleteModalOpen(true);
}}
className="gap-1"
>
{t("removeAuthPageBranding")}
<Trash2 size="14" />
</Button>
)}
<Button
type="submit"
form="auth-page-branding-form"
loading={isSubmitting}
disabled={isSubmitting}
loading={isUpdatingBranding || isDeletingBranding}
disabled={isUpdatingBranding || isDeletingBranding}
>
{t("saveAuthPageBranding")}
</Button>