nxtgauge-frontend-solid/src/components/dashboard/VerificationSubmissionGuide.tsx
Tracewebstudio Dev eb61206810 feat: dashboard session timer and verification UI improvements
- Add SessionTimer component with 13min warning / 15min idle auto-logout
- Move VerificationSubmissionGuide from ProfilePage to MyDashboardPage
- Remove duplicate VerificationSubmissionGuide from ProfilePage
- Fix 'Go to My Portfolio' button to navigate properly
- Change error messages to 'Service unavailable' for failed widget loads
- Brand color updates for VerificationSubmissionGuide
2026-05-05 17:22:04 +02:00

270 lines
No EOL
8.5 KiB
TypeScript

import { For, Show } from "solid-js";
import { BTN_GHOST, BTN_PRIMARY, CARD, ORANGE, NAVY } from "../DashboardShell";
type Props = {
statusLabel: string;
statusColor: string;
locked: boolean;
approved: boolean;
docRequest?: string | null;
missingBasicLabels: string[];
missingDocLabels: string[];
missingPortfolioLabels?: string[];
canSubmit: boolean;
submitting: boolean;
onSubmit: () => void;
onGoBasic: () => void;
onGoDocuments: () => void;
onGoPortfolio: () => void;
};
export default function VerificationSubmissionGuide(props: Props) {
const portfolioMissing = () => props.missingPortfolioLabels ?? [];
const totalMissing = () => props.missingBasicLabels.length + props.missingDocLabels.length + portfolioMissing().length;
const allDone = () => totalMissing() === 0;
const progress = () => {
const total = 3;
const done = [
props.missingBasicLabels.length === 0,
props.missingDocLabels.length === 0,
portfolioMissing().length === 0,
].filter(Boolean).length;
return Math.round((done / total) * 100);
};
return (
<div style={{ ...CARD, "margin-bottom": "16px", padding: "0", overflow: "hidden" }}>
<div style={{
padding: "20px 24px",
"border-bottom": "1px solid #E5E7EB",
background: "#FFFFFF",
}}>
<div style={{
display: "flex",
"align-items": "center",
"justify-content": "space-between",
gap: "16px",
"flex-wrap": "wrap",
}}>
<div style={{ display: "flex", "align-items": "center", gap: "14px" }}>
<div style={{
position: "relative",
width: "44px",
height: "44px",
}}>
<svg viewBox="0 0 36 36" style={{ width: "44px", height: "44px", transform: "rotate(-90deg)" }}>
<circle cx="18" cy="18" r="15" fill="none" stroke="#E5E7EB" stroke-width="2.5" />
<circle
cx="18" cy="18" r="15" fill="none"
stroke={NAVY}
stroke-width="2.5"
stroke-dasharray={`${progress()} 100`}
stroke-linecap="round"
/>
</svg>
<span style={{
position: "absolute",
inset: "0",
display: "flex",
"align-items": "center",
"justify-content": "center",
"font-size": "12px",
"font-weight": "700",
color: NAVY,
}}>
{progress()}%
</span>
</div>
<div>
<p style={{
margin: "0 0 2px",
"font-size": "14px",
"font-weight": "600",
color: NAVY,
}}>
{props.approved
? "Profile Approved"
: props.locked
? "Verification In Progress"
: allDone()
? "Ready to Submit"
: `${totalMissing()} Item${totalMissing() > 1 ? "s" : ""} Left`}
</p>
<p style={{
margin: "0",
"font-size": "12px",
color: "#6B7280",
}}>
{props.approved
? "Your profile is approved"
: props.locked
? "Verification in progress"
: allDone()
? "Submit for verification"
: "Complete the steps below to submit"}
</p>
</div>
</div>
<Show when={!props.locked && !props.approved}>
<button
type="button"
onClick={props.onSubmit}
disabled={!props.canSubmit || props.submitting}
style={{
...BTN_PRIMARY,
background: ORANGE,
color: "#FFFFFF",
border: "none",
opacity: !props.canSubmit || props.submitting ? "0.5" : "1",
cursor: !props.canSubmit || props.submitting ? "not-allowed" : "pointer",
padding: "10px 20px",
"font-weight": "600",
"border-radius": "8px",
}}
>
{props.submitting ? "Submitting..." : "Submit for Verification"}
</button>
</Show>
</div>
</div>
<Show when={props.docRequest}>
<div style={{
margin: "16px 24px",
background: "#FEF3C7",
padding: "12px 16px",
"border-radius": "8px",
"font-size": "13px",
color: "#92400E",
}}>
<strong>Admin request:</strong> {props.docRequest}
</div>
</Show>
<div style={{ padding: "16px 24px 20px", display: "grid", gap: "10px" }}>
<ChecklistItem
number={1}
title="Complete required basic profile fields"
done={props.missingBasicLabels.length === 0}
missingLabels={props.missingBasicLabels}
onGo={props.onGoBasic}
buttonText="Go to Basic Information"
/>
<ChecklistItem
number={2}
title="Upload clear required documents"
done={props.missingDocLabels.length === 0}
missingLabels={props.missingDocLabels}
onGo={props.onGoDocuments}
buttonText="Go to Documents"
/>
<ChecklistItem
number={3}
title="Add portfolio showcase items"
done={portfolioMissing().length === 0}
missingLabels={portfolioMissing()}
onGo={props.onGoPortfolio}
buttonText="Go to My Portfolio"
/>
</div>
<div style={{
padding: "12px 24px",
background: "#F9FAFB",
"border-top": "1px solid #E5E7EB",
}}>
<p style={{ margin: "0", "font-size": "12px", color: "#6B7280" }}>
💡 Use full-page, readable scans. Match name and address exactly with your profile details.
</p>
</div>
</div>
);
}
function ChecklistItem(props: {
number: number;
title: string;
done: boolean;
missingLabels: string[];
onGo?: () => void;
buttonText?: string;
}) {
return (
<div style={{
display: "flex",
gap: "12px",
padding: "12px 14px",
background: "#FFFFFF",
border: "1px solid #E5E7EB",
"border-radius": "8px",
}}>
<div style={{
width: "24px",
height: "24px",
"border-radius": "50%",
background: props.done ? NAVY : "#E5E7EB",
color: props.done ? "#fff" : "#9CA3AF",
display: "flex",
"align-items": "center",
"justify-content": "center",
"font-size": "12px",
"font-weight": "700",
"flex-shrink": "0",
}}>
<Show when={props.done} fallback={props.number}></Show>
</div>
<div style={{ flex: "1", "min-width": "0" }}>
<p style={{ margin: "0", "font-size": "13px", "font-weight": "500", color: "#111827" }}>
{props.title}
</p>
<Show
when={props.missingLabels.length > 0}
fallback={
<p style={{ margin: "2px 0 0", "font-size": "12px", color: NAVY, "font-weight": "500" }}>
Complete
</p>
}
>
<div style={{ display: "flex", "flex-wrap": "wrap", gap: "6px", "margin-top": "6px" }}>
<For each={props.missingLabels}>
{(label) => (
<span style={{
display: "inline-flex",
"align-items": "center",
padding: "2px 8px",
"border-radius": "4px",
background: "#F3F4F6",
color: "#374151",
"font-size": "11px",
}}>
{label}
</span>
)}
</For>
</div>
<Show when={props.onGo}>
<button
type="button"
onClick={props.onGo}
style={{
...BTN_GHOST,
height: "28px",
"font-size": "11px",
"font-weight": "500",
color: ORANGE,
background: "#FFF7ED",
border: "none",
padding: "0 10px",
"border-radius": "4px",
"margin-top": "8px",
}}
>
{props.buttonText}
</button>
</Show>
</Show>
</div>
</div>
);
}