+
+ {[1, 2, 3].map((bar) => (
+
+ ))}
+
+
+ {`Step ${step} of ${INVITE_STEP_COUNT}`}
+
+
+ {title}
+
+
+ {subtitle}
+
+
+ );
+}
diff --git a/src/app/invite/_components/invite-wizard.tsx b/src/app/invite/_components/invite-wizard.tsx
new file mode 100644
index 00000000..1dba3457
--- /dev/null
+++ b/src/app/invite/_components/invite-wizard.tsx
@@ -0,0 +1,137 @@
+import { useState } from "react";
+import { toast } from "react-toastify";
+
+import { inviteErrorCode } from "@/domain/team-member-invite/api/team-member-invite-api";
+import type { TeamMemberInviteDetails } from "@/domain/team-member-invite/types/team-member-invite";
+import {
+ useCompleteRegistration,
+ useSendInviteCode,
+ useVerifyInviteCode,
+} from "@/domain/team-member-invite/use-cases/use-team-member-invite";
+
+import { InviteCodeStep } from "./invite-code-step";
+import { InvitePasswordStep } from "./invite-password-step";
+import { InviteReviewStep } from "./invite-review-step";
+import type { InviteStep } from "./invite-step-header";
+
+export type InviteWizardProps = {
+ token: string;
+ details: TeamMemberInviteDetails;
+ /** Called when the server reports the invite can no longer be used. */
+ onInviteInvalid: () => void;
+};
+
+export function InviteWizard({ token, details, onInviteInvalid }: InviteWizardProps) {
+ const [step, setStep] = useState