feat(join): add flow overlay scaffolding for submit→redirect states
This commit is contained in:
parent
a22a576bff
commit
5a4c09f988
1 changed files with 155 additions and 0 deletions
|
|
@ -412,6 +412,86 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Flow overlay: covers the page from form submit through redirect -->
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-if="flowState !== 'idle'" class="join-flow-overlay">
|
||||||
|
<div class="join-flow-card">
|
||||||
|
<div class="join-flow-step">{{ flowStepLabel }}</div>
|
||||||
|
|
||||||
|
<!-- Progress states -->
|
||||||
|
<template
|
||||||
|
v-if="[
|
||||||
|
'creating-customer',
|
||||||
|
'opening-payment',
|
||||||
|
'processing-payment',
|
||||||
|
'creating-subscription',
|
||||||
|
].includes(flowState)"
|
||||||
|
>
|
||||||
|
<h2 class="join-flow-heading">
|
||||||
|
{{
|
||||||
|
flowState === "creating-customer"
|
||||||
|
? "Creating your account..."
|
||||||
|
: flowState === "opening-payment"
|
||||||
|
? "Opening secure payment..."
|
||||||
|
: flowState === "processing-payment"
|
||||||
|
? "Confirming your card..."
|
||||||
|
: "Activating your membership..."
|
||||||
|
}}
|
||||||
|
</h2>
|
||||||
|
<p class="join-flow-body">
|
||||||
|
Please don't close this window. This usually takes a few seconds.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Success state -->
|
||||||
|
<template v-if="flowState === 'success'">
|
||||||
|
<h2 class="join-flow-heading">Welcome to Ghost Guild!</h2>
|
||||||
|
<DashedBox :hoverable="false">
|
||||||
|
<div class="section-label" style="margin-bottom: 12px">
|
||||||
|
Membership Details
|
||||||
|
</div>
|
||||||
|
<dl class="details-list">
|
||||||
|
<div class="details-row">
|
||||||
|
<dt>Name</dt><dd>{{ form.name }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="details-row">
|
||||||
|
<dt>Email</dt><dd>{{ form.email }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="details-row">
|
||||||
|
<dt>Circle</dt><dd class="capitalize">{{ form.circle }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="details-row">
|
||||||
|
<dt>Contribution</dt><dd>{{ selectedTier.label }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</DashedBox>
|
||||||
|
<p class="join-flow-body" style="margin-top: 16px">
|
||||||
|
We've sent a confirmation email to {{ form.email }}. Redirecting
|
||||||
|
you to your dashboard...
|
||||||
|
</p>
|
||||||
|
<div class="button-row" style="margin-top: 20px">
|
||||||
|
<NuxtLink to="/welcome" class="form-submit">
|
||||||
|
Go to Dashboard Now
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Error state -->
|
||||||
|
<template v-if="flowState === 'error'">
|
||||||
|
<h2 class="join-flow-heading">We couldn't complete your signup</h2>
|
||||||
|
<div v-if="errorMessage" class="error-box">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</div>
|
||||||
|
<div class="button-row" style="margin-top: 20px">
|
||||||
|
<button class="btn" @click="closeFlowOverlay">
|
||||||
|
Back to form
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -457,6 +537,14 @@ const errorMessage = ref("");
|
||||||
const successMessage = ref("");
|
const successMessage = ref("");
|
||||||
const cadence = ref("monthly"); // 'monthly' | 'annual'
|
const cadence = ref("monthly"); // 'monthly' | 'annual'
|
||||||
|
|
||||||
|
// Flow overlay state — drives the post-submit full-viewport UI.
|
||||||
|
// 'idle' = overlay hidden; user is editing the form.
|
||||||
|
// 'creating-customer' | 'opening-payment' | 'processing-payment'
|
||||||
|
// | 'creating-subscription' = progress states, overlay shows a spinner + label.
|
||||||
|
// 'success' = overlay shows confirmation, auto-redirect is queued.
|
||||||
|
// 'error' = overlay shows error + Retry/Back buttons.
|
||||||
|
const flowState = ref("idle");
|
||||||
|
|
||||||
// Helcim state
|
// Helcim state
|
||||||
const customerId = ref(null);
|
const customerId = ref(null);
|
||||||
const customerCode = ref(null);
|
const customerCode = ref(null);
|
||||||
|
|
@ -519,6 +607,23 @@ const selectedTier = computed(() => {
|
||||||
return getContributionTierByValue(form.contributionTier);
|
return getContributionTierByValue(form.contributionTier);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const flowStepLabel = computed(() => {
|
||||||
|
switch (flowState.value) {
|
||||||
|
case "creating-customer":
|
||||||
|
case "opening-payment":
|
||||||
|
return "Step 2 of 3 — Payment";
|
||||||
|
case "processing-payment":
|
||||||
|
case "creating-subscription":
|
||||||
|
return "Step 2 of 3 — Finalizing";
|
||||||
|
case "success":
|
||||||
|
return "Step 3 of 3 — Welcome";
|
||||||
|
case "error":
|
||||||
|
return "Something went wrong";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Step 1: Create customer
|
// Step 1: Create customer
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (isSubmitting.value || !isFormValid.value) return;
|
if (isSubmitting.value || !isFormValid.value) return;
|
||||||
|
|
@ -678,6 +783,11 @@ const goBack = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeFlowOverlay = () => {
|
||||||
|
flowState.value = "idle";
|
||||||
|
errorMessage.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
currentStep.value = 1;
|
currentStep.value = 1;
|
||||||
|
|
@ -1180,4 +1290,49 @@ onUnmounted(() => {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.join-flow-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 50;
|
||||||
|
background: rgba(42, 32, 21, 0.72); /* --parch @ 72% */
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-flow-card {
|
||||||
|
background: var(--bg);
|
||||||
|
border: 1px dashed var(--border);
|
||||||
|
padding: 32px;
|
||||||
|
max-width: 520px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-flow-step {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-dim);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-flow-heading {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--text-bright);
|
||||||
|
margin: 0 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-flow-body {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue