Group D fixes
Some checks failed
Build and Push / build (push) Failing after 37s

This commit is contained in:
2026-04-29 22:13:08 +02:00
parent 9c50c9f054
commit 219b4c8365
15 changed files with 914 additions and 55 deletions

View File

@@ -64,6 +64,35 @@ interface WizardProps {
*/
userName?: string;
userEmail?: string;
/**
* Bug 6: when present, the wizard renders in "edit" mode — fields
* are pre-populated from the request, the SOUL.md auto-fetch is
* skipped (we trust the existing values), and the submit button
* PATCHes /api/onboarding/[id] instead of POSTing /api/onboarding.
*
* Per-package secrets are deliberately NOT pre-filled, even if the
* customer originally supplied them — server-side decryption to
* the client would be a security regression. The user re-enters
* any secrets they want to change; if they leave them blank, the
* existing encrypted blob in the DB is preserved by the PATCH
* endpoint.
*/
editingRequest?: {
id: string;
instanceName: string;
agentName: string;
soulMd: string;
agentsMd: string;
packages: string[];
billingAddress: {
company?: string;
street?: string;
city?: string;
postalCode?: string;
country?: string;
};
billingNotes: string;
};
onComplete: () => void;
}
@@ -71,6 +100,7 @@ export function OnboardingWizard({
orgName,
userName,
userEmail,
editingRequest,
onComplete,
}: WizardProps) {
const t = useTranslations("onboarding");
@@ -91,30 +121,55 @@ export function OnboardingWizard({
orgName,
isPersonal,
});
const isEditing = Boolean(editingRequest);
const [step, setStep] = useState<Step>("welcome");
// Edit mode jumps straight to the configure step — the welcome step
// is a first-time onboarding affordance and only adds friction when
// the customer is fixing a typo.
const [step, setStep] = useState<Step>(isEditing ? "configure" : "welcome");
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState("");
const [advancedOpen, setAdvancedOpen] = useState(false);
const [defaultsLoaded, setDefaultsLoaded] = useState(false);
// In edit mode we already have soulMd/agentsMd from the request;
// skip the workspace-defaults round trip that would overwrite them.
const [defaultsLoaded, setDefaultsLoaded] = useState(isEditing);
const [config, setConfig] = useState({
instanceName: "",
agentName: "Assistant",
soulMd: FALLBACK_SOUL.replace("{company}", displayOrgName),
agentsMd: FALLBACK_AGENTS,
packages: [] as string[],
billingAddress: {
// For personal accounts, leave the company field empty — it'll
// appear on invoices. The user can still type something if they
// want to.
company: isPersonal ? "" : displayOrgName,
street: "",
city: "",
postalCode: "",
country: "CH",
},
billingNotes: "",
const [config, setConfig] = useState(() => {
if (editingRequest) {
return {
instanceName: editingRequest.instanceName,
agentName: editingRequest.agentName,
soulMd: editingRequest.soulMd,
agentsMd: editingRequest.agentsMd,
packages: editingRequest.packages,
billingAddress: {
company: editingRequest.billingAddress.company ?? "",
street: editingRequest.billingAddress.street ?? "",
city: editingRequest.billingAddress.city ?? "",
postalCode: editingRequest.billingAddress.postalCode ?? "",
country: editingRequest.billingAddress.country ?? "CH",
},
billingNotes: editingRequest.billingNotes,
};
}
return {
instanceName: "",
agentName: "Assistant",
soulMd: FALLBACK_SOUL.replace("{company}", displayOrgName),
agentsMd: FALLBACK_AGENTS,
packages: [] as string[],
billingAddress: {
// For personal accounts, leave the company field empty — it'll
// appear on invoices. The user can still type something if they
// want to.
company: isPersonal ? "" : displayOrgName,
street: "",
city: "",
postalCode: "",
country: "CH",
},
billingNotes: "",
};
});
// TOOLS.md preview — readonly, auto-generated
@@ -308,8 +363,17 @@ export function OnboardingWizard({
}
}
const res = await fetch("/api/onboarding", {
method: "POST",
// Bug 6: edit mode targets the per-row endpoint with PATCH;
// create mode targets the collection endpoint with POST. Body
// shape is the same — both routes parse it through
// onboardingSchema.
const url = editingRequest
? `/api/onboarding/${encodeURIComponent(editingRequest.id)}`
: "/api/onboarding";
const method = editingRequest ? "PATCH" : "POST";
const res = await fetch(url, {
method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...config,
@@ -1017,7 +1081,11 @@ export function OnboardingWizard({
disabled={submitting}
className="py-2.5 px-6 bg-accent text-white text-sm font-medium rounded-lg hover:bg-accent-dim transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{submitting ? tCommon("loading") : t("submitRequest")}
{submitting
? tCommon("loading")
: isEditing
? t("saveChanges")
: t("submitRequest")}
</button>
</div>
</Card>