From e2addc9cdaed39c6d985e62d894bea10c4f9db7b Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Fri, 12 Sep 2025 16:16:55 -0700 Subject: [PATCH] Expert mode --- .../[projectId]/expert-mode/page-client.tsx | 151 ++++++++++++++++++ .../projects/[projectId]/expert-mode/page.tsx | 10 ++ 2 files changed, 161 insertions(+) create mode 100644 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/expert-mode/page-client.tsx create mode 100644 apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/expert-mode/page.tsx diff --git a/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/expert-mode/page-client.tsx b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/expert-mode/page-client.tsx new file mode 100644 index 000000000..133f73982 --- /dev/null +++ b/apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/expert-mode/page-client.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { Alert, Button, Card, CardContent, CardHeader, CardTitle, Input, Textarea, Typography } from "@stackframe/stack-ui"; +import React from "react"; +import { PageLayout } from "../page-layout"; +import { useAdminApp } from "../use-admin-app"; + +export default function PageClient() { + const [authorized, setAuthorized] = React.useState(false); + + if (!authorized) { + return setAuthorized(true)} />; + } + + return ; +} + +function Gate(props: { onAuthorized: () => void }) { + const [value, setValue] = React.useState(""); + + const tryEnter = () => { + if (value === "expert-mode") { + props.onAuthorized(); + } + }; + + return ( +
+
+ are you an expert? +
+ setValue(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter") tryEnter(); }} + autoFocus + /> + +
+
+
+ ); +} + +function ExpertContent() { + const app = useAdminApp(); + const project = app.useProject(); + const completeConfig = project.useConfig(); + + const [jsonInput, setJsonInput] = React.useState(""); + const [busy, setBusy] = React.useState(false); + const [error, setError] = React.useState(null); + const [success, setSuccess] = React.useState(null); + + const handleSubmit = async () => { + setError(null); + setSuccess(null); + let parsed: any; + try { + parsed = jsonInput.trim() ? JSON.parse(jsonInput) : {}; + } catch (e: any) { + setError("Invalid JSON. Please fix and try again."); + return; + } + setBusy(true); + try { + await project.updateConfig(parsed as any); + setSuccess("Configuration override applied successfully."); + setJsonInput(""); + } catch (e: any) { + setError(e?.message ?? "Failed to update configuration."); + } finally { + setBusy(false); + } + }; + + return ( + + +
+ Warning: Advanced internal page + + This page is not intended for standard use. It exposes internal configuration for visibility and quick experiments. Be careful: changes here can impact your project behavior. + +
+
+ +
+ {/* Current Config */} + + + Current complete config (read-only) + + +
+
+                {JSON.stringify(completeConfig, null, 2)}
+              
+
+
+
+ + {/* Update Config Override */} + + + Update Config Overrides + + + + Paste a JSON object representing config overrides. Keep it minimal — only include keys you want to change. + + + {error && ( + {error} + )} + {success && ( + {success} + )} + +