diff --git a/dashboard/src/api/client.ts b/dashboard/src/api/client.ts index b01c8fd..16d5426 100644 --- a/dashboard/src/api/client.ts +++ b/dashboard/src/api/client.ts @@ -584,6 +584,11 @@ export const api = { { method: "POST", body: JSON.stringify(body) }, ), + resetDashboard: () => + request>("/api/dashboard/setup/reset", { + method: "POST", + }), + completeSetup: (body: Record) => request< ApiResponse<{ setupComplete: boolean; botRestart?: BotRestartResult }> diff --git a/dashboard/src/components/app-sidebar.tsx b/dashboard/src/components/app-sidebar.tsx index 978eaa5..00014c6 100644 --- a/dashboard/src/components/app-sidebar.tsx +++ b/dashboard/src/components/app-sidebar.tsx @@ -80,8 +80,8 @@ const sections: { { title: "Ticket Panel", url: "/configs?file=ticket-panel", icon: Ticket01Icon }, { title: "AI Assistant", url: "/configs?file=supportbot-ai", icon: AiBrain01Icon }, { title: "Messages", url: "/configs?file=messages", icon: Message01Icon }, - { title: "Commands", url: "/configs?file=commands", icon: CommandIcon }, { title: "Staff Roles", url: "/configs?file=supportbot&tab=Roles", icon: UserMultiple02Icon }, + { title: "Commands", url: "/configs?file=commands", icon: CommandIcon }, ], }, { diff --git a/dashboard/src/components/settings/dashboard-branding-settings.tsx b/dashboard/src/components/settings/dashboard-branding-settings.tsx index 06a2461..df67340 100644 --- a/dashboard/src/components/settings/dashboard-branding-settings.tsx +++ b/dashboard/src/components/settings/dashboard-branding-settings.tsx @@ -6,6 +6,15 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useBranding } from "@/context/BrandingContext"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; function readFileAsDataUrl(file: File): Promise { return new Promise((resolve, reject) => { @@ -20,22 +29,23 @@ interface DashboardBrandingSettingsProps { canEdit: boolean; } -export function DashboardBrandingDangerZone({ canEdit }: DashboardBrandingSettingsProps) { - const { setBrandingLocal } = useBranding(); +export function DashboardDangerZone({ canEdit }: DashboardBrandingSettingsProps) { const [saving, setSaving] = useState(false); const [message, setMessage] = useState(""); + const [open, setOpen] = useState(false); - async function resetAll() { + async function resetDashboard() { if (!canEdit) return; setSaving(true); setMessage(""); try { - const res = await api.resetBranding(); - if (res.data) setBrandingLocal(res.data); - setMessage(res.message || "Reset to defaults."); + const res = await api.resetDashboard(); + setMessage(res.message || "Dashboard reset successfully."); + setTimeout(() => { + window.location.href = "/setup"; + }, 1500); } catch (err) { setMessage(err instanceof Error ? err.message : "Reset failed"); - } finally { setSaving(false); } } @@ -45,17 +55,34 @@ export function DashboardBrandingDangerZone({ canEdit }: DashboardBrandingSettin return (

- Reset all branding text and favicon to their original defaults. + Reset the dashboard to factory settings. This clears the configuration and returns you to the initial setup screen.

- - {message &&

{message}

} + + + + + + + + Are you absolutely sure? + + This action cannot be undone. This will reset the dashboard to setup mode. + You will need to reconnect the Discord OAuth and reconfigure the dashboard owners. + + + {message &&

{message}

} + + + + +
+
); } @@ -126,13 +153,6 @@ export function DashboardBrandingSettings({ canEdit }: DashboardBrandingSettings } } - } finally { - setUploading(false); - } - } - } - } - const textDirty = title.trim() !== branding.title || pageTitle.trim() !== branding.pageTitle; @@ -253,8 +273,6 @@ export function DashboardBrandingSettings({ canEdit }: DashboardBrandingSettings - - ); } diff --git a/dashboard/src/components/setup/setup-wizard.tsx b/dashboard/src/components/setup/setup-wizard.tsx index f636e39..83a0c56 100644 --- a/dashboard/src/components/setup/setup-wizard.tsx +++ b/dashboard/src/components/setup/setup-wizard.tsx @@ -26,6 +26,7 @@ export const SETUP_STEP_IDS = [ "oauth", "redirectUri", "owners", + "emeraldApi", ] as const; export type SetupStepId = (typeof SETUP_STEP_IDS)[number]; @@ -66,6 +67,12 @@ export const SETUP_STEPS: { subtitle: "Who can access this panel", icon: UserMultiple02Icon, }, + { + id: "emeraldApi", + title: "Emerald API", + subtitle: "One-click addons", + icon: Key01Icon, + }, ]; function validationKeyForStep(id: SetupStepId): string { @@ -636,3 +643,56 @@ export function SetupNav({ ); } + +export function EmeraldApiKeyStep({ + values, + setValues, + validation, + validating, + onValidate, +}: { + values: SetupFormValues; + setValues: (v: SetupFormValues) => void; + validation: Record; + validating: boolean; + onValidate: () => void; +}) { + return ( +
+

+ An Emerald Services API key is required to use the one-click addon installer in the dashboard. You can get yours from the{" "} + + Emerald Services Marketplace + . +

+
+ + setValues({ ...values, emeraldApiKey: e.target.value })} + placeholder="em_..." + className="h-11 font-mono text-sm" + /> +
+ +
+ +
+
+ ); +} diff --git a/dashboard/src/lib/setup-types.ts b/dashboard/src/lib/setup-types.ts index 040b6ca..5e6863c 100644 --- a/dashboard/src/lib/setup-types.ts +++ b/dashboard/src/lib/setup-types.ts @@ -35,4 +35,5 @@ export interface SetupFormValues { clientSecret: string; redirectUri: string; ownerUserIds: string; + emeraldApiKey: string; } diff --git a/dashboard/src/pages/Addons.tsx b/dashboard/src/pages/Addons.tsx index dbe5c8e..e2bdb6f 100644 --- a/dashboard/src/pages/Addons.tsx +++ b/dashboard/src/pages/Addons.tsx @@ -83,24 +83,16 @@ export default function Addons() {

Addons

- Install addons from the{" "} + Requires an API key from{" "} - Emerald Services Marketplace - - . Enable addons under{" "} - - Bot config → General → Addons - - . Installed addon configs appear in the left sidebar under{" "} - Addons. + emeraldsrv.dev + {" "} + to browse and install addons. Configs will appear in the sidebar once installed.

+ {canUpdate && ( + + )} @@ -190,19 +199,22 @@ export default function Settings() { + + )} - {canUpdate && ( + {activeTab === "danger" && canUpdate && ( Danger Zone + + Destructive actions that reset or permanently alter your dashboard. + - + )} - - )} {activeTab === "api" && ( diff --git a/dashboard/src/pages/Setup.tsx b/dashboard/src/pages/Setup.tsx index 2f7815f..9c49e36 100644 --- a/dashboard/src/pages/Setup.tsx +++ b/dashboard/src/pages/Setup.tsx @@ -15,6 +15,7 @@ import { SetupProgress, SetupStepCard, SetupWizardShell, + EmeraldApiKeyStep, type SetupStepId, } from "@/components/setup/setup-wizard"; import { useBranding } from "@/context/BrandingContext"; @@ -37,6 +38,7 @@ function parseOwnerIds(raw: string) { function validationKeyForStep(id: SetupStepId): string { if (id === "oauth") return "oauth"; if (id === "owners") return "owners"; + if (id === "emeraldApi") return "emeraldApi"; return id; } @@ -53,6 +55,7 @@ export default function Setup() { clientSecret: "", redirectUri: "", ownerUserIds: "", + emeraldApiKey: "", }); const [validation, setValidation] = useState>({}); const [validating, setValidating] = useState(false); @@ -76,6 +79,7 @@ export default function Setup() { clientSecret: values.clientSecret, redirectUri: values.redirectUri, ownerUserIds: parseOwnerIds(values.ownerUserIds), + emeraldApiKey: values.emeraldApiKey, }), [values], ); @@ -163,6 +167,10 @@ export default function Setup() { return ( void validateCurrentStep()} /> ); + case "emeraldApi": + return ( + void validateCurrentStep()} /> + ); } } @@ -184,7 +192,7 @@ export default function Setup() { {statusError ? (