Files
vibn-agent-runner/design-templates/VIBN (2)/vibn-crm/crm-onboarding.jsx

345 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// crm-onboarding.jsx — Cadence CRM · auth + onboarding
// ------------------------------------------------------------
// Full-screen flows that precede the app. Same minimal/light
// aesthetic as the Sidebar app shell. Built on vibn-ai-templates
// components (Button, Field, Input, Card, Badge, Avatar, Icon…).
// ============================================================
// Cadence brand mark — concentric "pulse" rings (a CRM cadence)
const CadenceMark = ({ size = 22 }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden="true">
<rect x="1" y="1" width="22" height="22" rx="7" fill="#5e5cff"/>
<path d="M5 13.5 H8.5 L10.2 8 L13 16 L14.8 11 L16.4 13.5 H19"
stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
);
const onbFont = "'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif";
// ── Shared scaffold: brand top bar + footer, centered slot ───
const OnbScaffold = ({ children, right, maxWidth = 460 }) => (
<div className="vibn-app" style={{
width: "100%", height: "100%", display: "grid",
gridTemplateRows: "auto 1fr auto", fontFamily: onbFont,
background: "var(--bg)", overflow: "hidden",
}}>
<header style={{
display: "flex", justifyContent: "space-between", alignItems: "center",
padding: "20px 28px",
}}>
<div style={{ display: "flex", alignItems: "center", gap: 9, fontWeight: 600, fontSize: 15 }}>
<CadenceMark size={22}/> Cadence
</div>
<div style={{ fontSize: 13, color: "var(--text-2)" }}>{right}</div>
</header>
<main style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: 24, overflowY: "auto" }}>
<div style={{ width: maxWidth, maxWidth: "100%" }}>{children}</div>
</main>
<footer style={{
display: "flex", justifyContent: "space-between", alignItems: "center",
padding: "16px 28px", fontSize: 11, color: "var(--text-3)",
}}>
<span>© 2026 Cadence CRM</span>
<div style={{ display: "flex", gap: 16 }}><span>Privacy</span><span>Terms</span><span>Security</span></div>
</footer>
</div>
);
const SocialButtons = ({ stacked }) => (
<div style={{ display: stacked ? "flex" : "flex", flexDirection: stacked ? "column" : "row", gap: 8 }}>
<Button variant="secondary" full>Continue with Google</Button>
<Button variant="secondary" full>Continue with Microsoft</Button>
</div>
);
// ── 1 · SIGN UP ──────────────────────────────────────────────
const CRMSignUp = () => (
<OnbScaffold right={<>Have an account? <b style={{ color: "var(--text)" }}>Sign in</b></>}>
<Card variant="raised" padding={34}>
<h1 style={{ margin: 0, fontSize: 24, fontWeight: 600, letterSpacing: "-0.02em" }}>
Create your Cadence account
</h1>
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "8px 0 24px" }}>
Free for your whole team for 14 days. No card required.
</p>
<SocialButtons/>
<Divider label="or"/>
<Field label="Work email"><Input value="mira@northwind.io" autofocus/></Field>
<Field label="Full name"><Input placeholder="Mira Reyes"/></Field>
<Field label="Password" hint="At least 10 characters with a number.">
<Input type="password" value="••••••••••" trailingIcon={<Icon name="eye" size={14}/>}/>
</Field>
<Checkbox checked label="I agree to the Terms and Privacy Policy." style={{ margin: "2px 0 18px" }}/>
<Button full size="lg">Create account <Icon name="arrow" size={14}/></Button>
</Card>
</OnbScaffold>
);
// ── 2 · SIGN IN ──────────────────────────────────────────────
const CRMSignIn = () => (
<OnbScaffold right={<>New here? <b style={{ color: "var(--text)" }}>Create an account</b></>}>
<Card variant="raised" padding={34}>
<h1 style={{ margin: 0, fontSize: 24, fontWeight: 600, letterSpacing: "-0.02em" }}>Welcome back</h1>
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "8px 0 24px" }}>
Sign in to the Northwind workspace.
</p>
<SocialButtons/>
<Divider label="or"/>
<Field label="Work email"><Input value="mira@northwind.io" autofocus/></Field>
<div style={{ marginBottom: 16 }}>
<div style={{
display: "flex", justifyContent: "space-between", alignItems: "baseline",
fontSize: 12, fontWeight: 500, marginBottom: 6,
}}>
<span>Password</span>
<span style={{ color: "var(--accent)", cursor: "pointer", fontWeight: 400 }}>Forgot?</span>
</div>
<Input type="password" value="••••••••••" trailingIcon={<Icon name="eye" size={14}/>}/>
</div>
<Checkbox checked label="Keep me signed in for 30 days" style={{ marginBottom: 18 }}/>
<Button full size="lg">Sign in <Icon name="arrow" size={14}/></Button>
<div style={{
marginTop: 18, padding: "10px 14px", borderRadius: "var(--radius)",
background: "var(--surface-2)", border: "1px solid var(--border)",
fontSize: 12, color: "var(--text-2)", display: "flex", alignItems: "center", gap: 10,
}}>
<Icon name="shield" size={14} style={{ color: "var(--accent)" }}/>
<span style={{ flex: 1 }}>Your company uses SSO?</span>
<span style={{ color: "var(--text)", fontWeight: 500, cursor: "pointer" }}>Use SSO </span>
</div>
</Card>
</OnbScaffold>
);
// ── Stepper used across onboarding steps ─────────────────────
const Stepper = ({ step }) => {
const steps = ["Workspace", "About you", "Import", "Invite"];
return (
<div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 24 }}>
{steps.map((s, i) => {
const state = i < step ? "done" : i === step ? "active" : "todo";
return (
<React.Fragment key={s}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<div style={{
width: 22, height: 22, borderRadius: "50%",
background: state === "done" ? "var(--success)" : state === "active" ? "var(--text)" : "transparent",
color: state === "todo" ? "var(--text-3)" : "#fff",
border: state === "todo" ? "1px solid var(--border-strong)" : "none",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 11, fontWeight: 600, flexShrink: 0,
}}>{state === "done" ? <Icon name="checkOnly" size={12} stroke={2.6}/> : i + 1}</div>
<span style={{
fontSize: 12, color: state === "todo" ? "var(--text-3)" : "var(--text)",
fontWeight: state === "active" ? 600 : 400, whiteSpace: "nowrap",
}}>{s}</span>
</div>
{i < steps.length - 1 && <div style={{ flex: 1, height: 1, background: "var(--border)", margin: "0 4px", minWidth: 16 }}/>}
</React.Fragment>
);
})}
</div>
);
};
const Tile = ({ icon, title, sub, selected }) => (
<div style={{
padding: 16, borderRadius: "var(--radius)", cursor: "pointer", textAlign: "left",
border: selected ? "1.5px solid var(--accent)" : "1px solid var(--border)",
background: selected ? "var(--accent-soft)" : "var(--surface)",
boxShadow: selected ? "0 0 0 3px var(--accent-ring)" : "var(--shadow-sm)",
position: "relative",
}}>
<div style={{
width: 32, height: 32, borderRadius: 9, marginBottom: 12,
background: selected ? "var(--accent)" : "var(--surface-alt)",
color: selected ? "#fff" : "var(--text-2)",
display: "flex", alignItems: "center", justifyContent: "center",
}}><Icon name={icon} size={16}/></div>
<div style={{ fontSize: 13, fontWeight: 600 }}>{title}</div>
<div style={{ fontSize: 12, color: "var(--text-3)", marginTop: 2, lineHeight: 1.4 }}>{sub}</div>
{selected && <div style={{
position: "absolute", top: 14, right: 14, width: 18, height: 18, borderRadius: "50%",
background: "var(--accent)", color: "#fff",
display: "flex", alignItems: "center", justifyContent: "center",
}}><Icon name="checkOnly" size={11} stroke={2.6}/></div>}
</div>
);
// ── 3 · ONBOARDING · step 1 — create workspace ───────────────
const CRMOnbWorkspace = () => (
<OnbScaffold right="Step 1 of 4" maxWidth={560}>
<Card variant="raised" padding={36}>
<Stepper step={0}/>
<h1 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: "-0.02em" }}>
Name your workspace
</h1>
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "8px 0 24px" }}>
This is where your team's contacts, companies and deals will live.
</p>
<div style={{ display: "flex", gap: 16, alignItems: "flex-start", marginBottom: 18 }}>
<div style={{
width: 64, height: 64, borderRadius: 14, flexShrink: 0,
background: "var(--surface-alt)", border: "1.5px dashed var(--border-strong)",
display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
color: "var(--text-3)", cursor: "pointer", gap: 2,
}}>
<Icon name="plus" size={18}/>
<span style={{ fontSize: 9 }}>Logo</span>
</div>
<div style={{ flex: 1 }}>
<Field label="Workspace name" style={{ marginBottom: 12 }}>
<Input value="Northwind"/>
</Field>
<Field label="Workspace URL" hint="You can change this later." style={{ marginBottom: 0 }}>
<Input value="northwind" leadingIcon={<span style={{ fontSize: 12, color: "var(--text-3)" }}>cadence.app/</span>}/>
</Field>
</div>
</div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 26 }}>
<span style={{ fontSize: 12, color: "var(--text-3)" }}>You can invite teammates in a moment.</span>
<Button size="lg">Continue <Icon name="arrow" size={14}/></Button>
</div>
</Card>
</OnbScaffold>
);
// ── 4 · ONBOARDING · step 2 — about you ──────────────────────
const CRMOnbAbout = () => (
<OnbScaffold right="Step 2 of 4" maxWidth={620}>
<Card variant="raised" padding={36}>
<Stepper step={1}/>
<h1 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: "-0.02em" }}>
Tell us about your team
</h1>
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "8px 0 22px" }}>
We'll tailor your pipeline stages and views to match.
</p>
<div style={{ fontSize: 12, fontWeight: 600, marginBottom: 10 }}>What will you use Cadence for?</div>
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10, marginBottom: 24 }}>
<Tile icon="target" title="Sales pipeline" sub="Track deals to close" selected/>
<Tile icon="people" title="Relationships" sub="Nurture contacts"/>
<Tile icon="briefcase" title="Recruiting" sub="Manage candidates"/>
<Tile icon="bolt" title="Fundraising" sub="Investors & rounds"/>
<Tile icon="inbox" title="Support" sub="Customer success"/>
<Tile icon="spark" title="Something else" sub="I'll set it up"/>
</div>
<div style={{ fontSize: 12, fontWeight: 600, marginBottom: 10 }}>How big is your team?</div>
<FieldGroup options={["Just me", "210", "1150", "51200", "200+"]} value="210"/>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 28 }}>
<Button variant="ghost"> Back</Button>
<Button size="lg">Continue <Icon name="arrow" size={14}/></Button>
</div>
</Card>
</OnbScaffold>
);
// ── 5 · ONBOARDING · step 3 — import contacts ────────────────
const CRMOnbImport = () => {
const Source = ({ icon, title, sub, badge }) => (
<div style={{
display: "flex", alignItems: "center", gap: 14, padding: 16,
borderRadius: "var(--radius)", border: "1px solid var(--border)",
background: "var(--surface)", cursor: "pointer", boxShadow: "var(--shadow-sm)",
}}>
<div style={{
width: 40, height: 40, borderRadius: 10, flexShrink: 0,
background: "var(--surface-alt)", color: "var(--text-2)",
display: "flex", alignItems: "center", justifyContent: "center",
}}><Icon name={icon} size={18}/></div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 14, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 }}>
{title}{badge && <Badge tone="accent">{badge}</Badge>}
</div>
<div style={{ fontSize: 12, color: "var(--text-3)", marginTop: 2 }}>{sub}</div>
</div>
<Icon name="chevRight" size={16} style={{ color: "var(--text-3)" }}/>
</div>
);
return (
<OnbScaffold right="Step 3 of 4" maxWidth={560}>
<Card variant="raised" padding={36}>
<Stepper step={2}/>
<h1 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: "-0.02em" }}>
Bring your contacts in
</h1>
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "8px 0 22px" }}>
Cadence dedupes and enriches automatically. Nothing is shared.
</p>
<div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
<Source icon="people" title="Google Contacts" sub="Sync 2,400 contacts" badge="Recommended"/>
<Source icon="inbox" title="Connect a mailbox" sub="Build contacts from your sent mail"/>
<Source icon="doc" title="Upload a CSV" sub="Map columns to fields"/>
</div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 26 }}>
<Button variant="ghost">Skip for now</Button>
<Button size="lg">Continue <Icon name="arrow" size={14}/></Button>
</div>
</Card>
</OnbScaffold>
);
};
// ── 6 · ONBOARDING · step 4 — invite team ────────────────────
const CRMOnbInvite = () => {
const Row = ({ email, role, color }) => (
<div style={{
display: "flex", alignItems: "center", gap: 12, padding: "10px 12px",
borderRadius: "var(--radius)", background: "var(--surface-2)", border: "1px solid var(--border)",
}}>
<Avatar name={email} color={color} size={28}/>
<span style={{ flex: 1, fontSize: 13, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{email}</span>
<Select value={role} style={{ padding: "5px 10px" }}/>
<Badge tone="warn" dot>Pending</Badge>
</div>
);
return (
<OnbScaffold right="Step 4 of 4" maxWidth={560}>
<Card variant="raised" padding={36}>
<Stepper step={3}/>
<h1 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: "-0.02em" }}>
Invite your team
</h1>
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "8px 0 22px" }}>
Cadence is better with the people you sell with.
</p>
<div style={{ display: "flex", gap: 8, marginBottom: 16 }}>
<Input placeholder="name@northwind.io, separate with commas" style={{ flex: 1 }} autofocus/>
<Button>Send invites</Button>
</div>
<div style={{ fontSize: 11, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 500, marginBottom: 8 }}>
To be invited · 3
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<Row email="theo@northwind.io" role="Admin" color="#c8e8a8"/>
<Row email="devi@northwind.io" role="Member" color="#a8c8e8"/>
<Row email="sun@northwind.io" role="Member" color="#e8a87c"/>
</div>
<div style={{
marginTop: 18, padding: "12px 14px", borderRadius: "var(--radius)",
border: "1px dashed var(--border-strong)", display: "flex", alignItems: "center", gap: 12,
}}>
<Icon name="link" size={16} style={{ color: "var(--accent)" }}/>
<span style={{ flex: 1, fontSize: 13, fontFamily: "var(--font-mono)", color: "var(--text-2)" }}>cadence.app/join/northwind-7f4a</span>
<Button variant="secondary" size="sm">Copy link</Button>
</div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 26 }}>
<Button variant="ghost">I'll do this later</Button>
<Button size="lg">Enter Cadence <Icon name="arrow" size={14}/></Button>
</div>
</Card>
</OnbScaffold>
);
};
Object.assign(window, {
CadenceMark, CRMSignUp, CRMSignIn,
CRMOnbWorkspace, CRMOnbAbout, CRMOnbImport, CRMOnbInvite,
});