mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
feat(onboarding/phase-6): setup wizard polish, seed script alignment, doctor visit slots
- Setup wizard: 3-pane layout with right-side live previews, resume banner, edit/copy icons on team step, AI prompt configuration - Forms: employee-create replaces invite-member (no email invites), clinic form with address/hours/payment, doctor form with visit slots - Seed script: aligned to current SDK schema — doctors created as workspace members (HelixEngage Manager role), visitingHours replaced by doctorVisitSlot entity, clinics seeded, portalUserId linked dynamically, SUB/ORIGIN/GQL configurable via env vars - Pages: clinics + doctors CRUD updated for new schema, team settings with temp password + role assignment - New components: time-picker, day-selector, wizard-right-panes, wizard-layout-context, resume-setup-banner - Removed: invite-member-form (replaced by employee-create-form per no-email-invites rule) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { useContext, type ReactNode } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faArrowLeft, faArrowRight, faCircleCheck } from '@fortawesome/pro-duotone-svg-icons';
|
||||
import { Button } from '@/components/base/buttons/button';
|
||||
import { SETUP_STEP_LABELS, type SetupStepName } from '@/lib/setup-state';
|
||||
import { WizardLayoutContext } from './wizard-layout-context';
|
||||
|
||||
type WizardStepProps = {
|
||||
step: SetupStepName;
|
||||
@@ -14,6 +16,11 @@ type WizardStepProps = {
|
||||
onFinish: () => void;
|
||||
saving?: boolean;
|
||||
children: ReactNode;
|
||||
// Optional content for the wizard shell's right preview pane.
|
||||
// Portaled into the shell's <aside> via WizardLayoutContext when
|
||||
// both are mounted. Each step component declares this inline so
|
||||
// the per-step data fetching stays in one place.
|
||||
rightPane?: ReactNode;
|
||||
};
|
||||
|
||||
// Single-step wrapper. The parent picks which step is active and supplies
|
||||
@@ -31,10 +38,14 @@ export const WizardStep = ({
|
||||
onFinish,
|
||||
saving = false,
|
||||
children,
|
||||
rightPane,
|
||||
}: WizardStepProps) => {
|
||||
const meta = SETUP_STEP_LABELS[step];
|
||||
const { rightPaneEl } = useContext(WizardLayoutContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
{rightPane && rightPaneEl && createPortal(rightPane, rightPaneEl)}
|
||||
<div className="rounded-xl border border-secondary bg-primary p-8 shadow-xs">
|
||||
<div className="mb-6 flex items-start justify-between gap-4">
|
||||
<div>
|
||||
@@ -64,30 +75,55 @@ export const WizardStep = ({
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
{/* One primary action at the bottom — never two
|
||||
competing buttons. Previously the wizard showed
|
||||
Mark complete + Next side-by-side, and users
|
||||
naturally clicked Next (rightmost = "continue"),
|
||||
skipping the save+complete chain entirely. Result
|
||||
was every step staying at 0/6.
|
||||
|
||||
New behaviour: a single button whose label and
|
||||
handler depend on completion state.
|
||||
- !isCompleted, not last → "Save and continue"
|
||||
calls onMarkComplete (which does save +
|
||||
complete + advance via the step component's
|
||||
handleSave). Forces the agent through the
|
||||
completion path.
|
||||
- !isCompleted, last → "Save and finish"
|
||||
same chain, plus onFinish at the end.
|
||||
- isCompleted, not last → "Continue"
|
||||
calls onNext (pure navigation).
|
||||
- isCompleted, last → "Finish setup"
|
||||
calls onFinish.
|
||||
|
||||
Free-form navigation is still available via the
|
||||
left-side step nav, so users can revisit completed
|
||||
steps without re-saving. */}
|
||||
<div className="flex items-center gap-3">
|
||||
{!isCompleted && (
|
||||
{!isCompleted ? (
|
||||
<Button
|
||||
color="primary"
|
||||
size="md"
|
||||
isLoading={saving}
|
||||
showTextWhileLoading
|
||||
onClick={onMarkComplete}
|
||||
iconTrailing={
|
||||
isLast
|
||||
? undefined
|
||||
: ({ className }: { className?: string }) => (
|
||||
<FontAwesomeIcon icon={faArrowRight} className={className} />
|
||||
)
|
||||
}
|
||||
>
|
||||
Mark complete
|
||||
{isLast ? 'Save and finish' : 'Save and continue'}
|
||||
</Button>
|
||||
)}
|
||||
{isLast ? (
|
||||
<Button
|
||||
color="primary"
|
||||
size="md"
|
||||
isDisabled={!isCompleted}
|
||||
onClick={onFinish}
|
||||
>
|
||||
) : isLast ? (
|
||||
<Button color="primary" size="md" onClick={onFinish}>
|
||||
Finish setup
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
color={isCompleted ? 'primary' : 'secondary'}
|
||||
color="primary"
|
||||
size="md"
|
||||
isDisabled={!onNext}
|
||||
onClick={onNext ?? undefined}
|
||||
@@ -95,11 +131,12 @@ export const WizardStep = ({
|
||||
<FontAwesomeIcon icon={faArrowRight} className={className} />
|
||||
)}
|
||||
>
|
||||
Next
|
||||
Continue
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user