This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user