- 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
270 lines
No EOL
8.5 KiB
TypeScript
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>
|
|
);
|
|
} |