restyle client regen credentials

This commit is contained in:
miloschwartz
2025-12-06 11:47:50 -05:00
parent 3f4fae8f09
commit 1d303feca2

View File

@@ -1,27 +1,37 @@
"use client"; "use client";
import RegenerateCredentialsModal from "@app/components/RegenerateCredentialsModal"; import { useState } from "react";
import { SecurityFeaturesAlert } from "@app/components/SecurityFeaturesAlert";
import { import {
SettingsContainer, SettingsContainer,
SettingsSection, SettingsSection,
SettingsSectionBody, SettingsSectionBody,
SettingsSectionDescription, SettingsSectionDescription,
SettingsSectionFooter,
SettingsSectionHeader, SettingsSectionHeader,
SettingsSectionTitle SettingsSectionTitle
} from "@app/components/Settings"; } from "@app/components/Settings";
import { Button } from "@app/components/ui/button"; import { Button } from "@app/components/ui/button";
import { useClientContext } from "@app/hooks/useClientContext"; import { createApiClient, formatAxiosError } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext"; import { useEnvContext } from "@app/hooks/useEnvContext";
import { toast } from "@app/hooks/useToast";
import { useParams, useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { PickClientDefaultsResponse } from "@server/routers/client";
import { useClientContext } from "@app/hooks/useClientContext";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext";
import { toast } from "@app/hooks/useToast";
import { createApiClient } from "@app/lib/api";
import { build } from "@server/build"; import { build } from "@server/build";
import { PickClientDefaultsResponse } from "@server/routers/client"; import { SecurityFeaturesAlert } from "@app/components/SecurityFeaturesAlert";
import { useTranslations } from "next-intl"; import {
import { useParams, useRouter } from "next/navigation"; InfoSection,
import { useState } from "react"; InfoSectionContent,
InfoSections,
InfoSectionTitle
} from "@app/components/InfoSection";
import CopyToClipboard from "@app/components/CopyToClipboard";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
import { InfoIcon } from "lucide-react";
export default function CredentialsPage() { export default function CredentialsPage() {
const { env } = useEnvContext(); const { env } = useEnvContext();
@@ -34,6 +44,11 @@ export default function CredentialsPage() {
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const [clientDefaults, setClientDefaults] = const [clientDefaults, setClientDefaults] =
useState<PickClientDefaultsResponse | null>(null); useState<PickClientDefaultsResponse | null>(null);
const [currentOlmId, setCurrentOlmId] = useState<string | null>(null);
const [regeneratedSecret, setRegeneratedSecret] = useState<string | null>(
null
);
const [showCredentialsAlert, setShowCredentialsAlert] = useState(false);
const { licenseStatus, isUnlocked } = useLicenseStatusContext(); const { licenseStatus, isUnlocked } = useLicenseStatusContext();
const subscription = useSubscriptionStatusContext(); const subscription = useSubscriptionStatusContext();
@@ -46,6 +61,7 @@ export default function CredentialsPage() {
}; };
const handleConfirmRegenerate = async () => { const handleConfirmRegenerate = async () => {
try {
const res = await api.get(`/org/${orgId}/pick-client-defaults`); const res = await api.get(`/org/${orgId}/pick-client-defaults`);
if (res && res.status === 200) { if (res && res.status === 200) {
const data = res.data.data; const data = res.data.data;
@@ -59,63 +75,136 @@ export default function CredentialsPage() {
if (rekeyRes && rekeyRes.status === 200) { if (rekeyRes && rekeyRes.status === 200) {
const rekeyData = rekeyRes.data.data; const rekeyData = rekeyRes.data.data;
if (rekeyData && rekeyData.olmId) {
setCurrentOlmId(rekeyData.olmId);
setRegeneratedSecret(data.olmSecret);
setClientDefaults({ setClientDefaults({
...data, ...data,
olmId: rekeyData.olmId, olmId: rekeyData.olmId
}); });
setShowCredentialsAlert(true);
}
} }
toast({ toast({
title: t("credentialsSaved"), title: t("credentialsSaved"),
description: t("credentialsSavedDescription") description: t("credentialsSavedDescription")
}); });
}
router.refresh(); } catch (error) {
toast({
variant: "destructive",
title: t("error") || "Error",
description:
formatAxiosError(error) ||
t("credentialsRegenerateError") ||
"Failed to regenerate credentials"
});
} }
}; };
const getCredentials = () => { const getConfirmationString = () => {
if (clientDefaults) { return client?.name || client?.clientId?.toString() || "My client";
return {
Id: clientDefaults.olmId,
Secret: clientDefaults.olmSecret
};
}
return undefined;
}; };
const displayOlmId = currentOlmId || clientDefaults?.olmId || null;
const displaySecret = regeneratedSecret || null;
return ( return (
<> <>
<SettingsContainer> <SettingsContainer>
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
<SettingsSectionTitle> <SettingsSectionTitle>
{t("generatedcredentials")} {t("clientOlmCredentials")}
</SettingsSectionTitle> </SettingsSectionTitle>
<SettingsSectionDescription> <SettingsSectionDescription>
{t("regenerateCredentials")} {t("clientOlmCredentialsDescription")}
</SettingsSectionDescription> </SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<SecurityFeaturesAlert /> <InfoSections cols={3}>
<InfoSection>
<InfoSectionTitle>
{t("olmEndpoint")}
</InfoSectionTitle>
<InfoSectionContent>
<CopyToClipboard
text={env.app.dashboardUrl}
/>
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("olmId")}
</InfoSectionTitle>
<InfoSectionContent>
{displayOlmId ? (
<CopyToClipboard text={displayOlmId} />
) : (
<span>{"••••••••••••••••"}</span>
)}
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>
{t("olmSecretKey")}
</InfoSectionTitle>
<InfoSectionContent>
{displaySecret ? (
<CopyToClipboard text={displaySecret} />
) : (
<span>{"••••••••••••••••"}</span>
)}
</InfoSectionContent>
</InfoSection>
</InfoSections>
{showCredentialsAlert && displaySecret && (
<Alert variant="neutral" className="mt-4">
<InfoIcon className="h-4 w-4" />
<AlertTitle className="font-semibold">
{t("clientCredentialsSave")}
</AlertTitle>
<AlertDescription>
{t("clientCredentialsSaveDescription")}
</AlertDescription>
</Alert>
)}
</SettingsSectionBody>
<SettingsSectionFooter>
<Button <Button
onClick={() => setModalOpen(true)} onClick={() => setModalOpen(true)}
disabled={isSecurityFeatureDisabled()} disabled={isSecurityFeatureDisabled()}
> >
{t("regeneratecredentials")} {t("regenerateCredentialsButton")}
</Button> </Button>
</SettingsSectionBody> </SettingsSectionFooter>
</SettingsSection> </SettingsSection>
</SettingsContainer> </SettingsContainer>
<RegenerateCredentialsModal <ConfirmDeleteDialog
open={modalOpen} open={modalOpen}
onOpenChange={setModalOpen} setOpen={(val) => {
type="client-olm" setModalOpen(val);
onConfirmRegenerate={handleConfirmRegenerate} // Prevent modal from reopening during refresh
dashboardUrl={env.app.dashboardUrl} if (!val) {
credentials={getCredentials()} setTimeout(() => {
router.refresh();
}, 150);
}
}}
dialog={
<div className="space-y-2">
<p>{t("regenerateCredentialsConfirmation")}</p>
<p>{t("regenerateCredentialsWarning")}</p>
</div>
}
buttonText={t("regenerateCredentialsButton")}
onConfirm={handleConfirmRegenerate}
string={getConfirmationString()}
title={t("regenerateCredentials")}
warningText={t("cannotbeUndone")}
/> />
</> </>
); );