267 lines
16 KiB
React
267 lines
16 KiB
React
/* "Before" screens — faithful recreations of the current Bootstrap views. */
|
||
|
||
/* =======================
|
||
DASHBOARD — BEFORE
|
||
======================= */
|
||
function DashboardBefore() {
|
||
const kpis = [
|
||
{ n: 4, label: 'Overdue Invoices', sub: '$3,240', color: '#eb3349', icon: 'bi-exclamation-circle' },
|
||
{ n: 12, label: "Today's Jobs", color: '#11998e', icon: 'bi-calendar-check-fill' },
|
||
{ n: 7, label: 'Overdue Jobs', color: '#f7971e', icon: 'bi-exclamation-triangle-fill' },
|
||
{ n: 3, label: "Today's Appointments", color: '#4facfe', icon: 'bi-calendar-event-fill' },
|
||
{ n: 8, label: 'Low Stock', color: '#a855f7', icon: 'bi-box-seam-fill' },
|
||
{ n: 2, label: 'Maintenance Due', color: '#667eea', icon: 'bi-tools' },
|
||
];
|
||
return (
|
||
<div style={{ display: 'flex', background: '#f4f5f7' }}>
|
||
<SidebarBefore />
|
||
<div style={{ flex: 1, minWidth: 0 }}>
|
||
<TopbarBefore title="Dashboard" />
|
||
<div style={{ padding: '24px 28px' }}>
|
||
{/* gradient welcome banner */}
|
||
<div style={{
|
||
background: 'linear-gradient(180deg,#1a1a2e,#16213e,#0f3460)',
|
||
borderRadius: 12, padding: '22px 26px', marginBottom: 22, color: '#fff',
|
||
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||
}}>
|
||
<div>
|
||
<div style={{ fontSize: 18, fontWeight: 700, marginBottom: 4 }}>Welcome Back!</div>
|
||
<div style={{ fontSize: 13, color: 'rgba(255,255,255,0.75)' }}>
|
||
Here's your shop overview for April 18, 2026 · <span style={{ opacity: 0.85 }}><i className="bi bi-rocket-takeoff" /> What's New</span>
|
||
</div>
|
||
<div style={{ fontSize: 12, color: 'rgba(255,255,255,0.6)', marginTop: 8 }}>
|
||
<i className="bi bi-lightbulb" style={{ color: 'rgba(255,220,80,0.8)' }} /> Tip: Use job templates to speed up quoting for repeat customers.
|
||
</div>
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 28, textAlign: 'right' }}>
|
||
<div><div style={{ fontSize: 11, opacity: 0.75 }}>Collected April 2026</div><div style={{ fontWeight: 700, fontSize: 17 }}>$48,290</div></div>
|
||
<div><div style={{ fontSize: 11, opacity: 0.75 }}>Outstanding A/R</div><div style={{ fontWeight: 700, fontSize: 17 }}>$12,450</div></div>
|
||
<div><div style={{ fontSize: 11, opacity: 0.75 }}>Active Customers</div><div style={{ fontWeight: 700, fontSize: 17 }}>84</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* rainbow KPIs */}
|
||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(6,1fr)', gap: 12, marginBottom: 22 }}>
|
||
{kpis.map((k, i) => (
|
||
<div key={i} style={{
|
||
background: '#fff', borderRadius: 10, padding: 14,
|
||
borderTop: `4px solid ${k.color}`,
|
||
boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
|
||
}}>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
||
<div>
|
||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
|
||
<div style={{ fontSize: 28, fontWeight: 700, color: k.color, lineHeight: 1 }}>{k.n}</div>
|
||
{k.sub && <div style={{ fontSize: 11, color: '#6b7280' }}>{k.sub}</div>}
|
||
</div>
|
||
<div style={{ fontSize: 11, color: '#6b7280', marginTop: 4 }}>{k.label}</div>
|
||
</div>
|
||
<i className={`bi ${k.icon}`} style={{ color: k.color, opacity: 0.5, fontSize: 22 }} />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* body cards */}
|
||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 16 }}>
|
||
<div style={{ background: '#fff', borderRadius: 10, boxShadow: '0 1px 3px rgba(0,0,0,0.06)' }}>
|
||
<div style={{ padding: '14px 18px', borderBottom: '1px solid #e5e7eb', fontWeight: 600 }}>Recent Jobs</div>
|
||
<div style={{ padding: 6 }}>
|
||
{['J-2041 · Fence gate · Rodriguez', 'J-2040 · Wheel set · D&T Auto', 'J-2039 · Patio table · Mercer', 'J-2038 · Trailer frame · Bluegrass', 'J-2037 · Bumper · Heritage'].map((r, i) => (
|
||
<div key={i} style={{ padding: '10px 12px', borderBottom: i < 4 ? '1px solid #f3f4f6' : 'none', display: 'flex', justifyContent: 'space-between', fontSize: 13, color: '#374151' }}>
|
||
<span>{r}</span>
|
||
<span style={{ background: '#fef3c7', color: '#92400e', padding: '2px 8px', borderRadius: 4, fontSize: 10.5, fontWeight: 600 }}>IN PROGRESS</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div style={{ background: '#fff', borderRadius: 10, boxShadow: '0 1px 3px rgba(0,0,0,0.06)' }}>
|
||
<div style={{ padding: '14px 18px', borderBottom: '1px solid #e5e7eb', fontWeight: 600 }}>A/R Aging</div>
|
||
<div style={{ padding: 18 }}>
|
||
{[['Current', 8420, '#11998e'], ['1–30 days', 2100, '#4facfe'], ['31–60 days', 1430, '#f7971e'], ['60+ days', 500, '#eb3349']].map(([l, v, c], i) => (
|
||
<div key={i} style={{ marginBottom: 10 }}>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}><span>{l}</span><span style={{ fontWeight: 600 }}>${v.toLocaleString()}</span></div>
|
||
<div style={{ height: 6, borderRadius: 99, background: '#f3f4f6', overflow: 'hidden' }}>
|
||
<div style={{ height: '100%', width: `${(v/10000)*100}%`, background: c }} />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/* =======================
|
||
JOBS INDEX — BEFORE
|
||
======================= */
|
||
function JobsIndexBefore() {
|
||
const jobs = [
|
||
['J-2041', 'Fence gate — Anodized black', 'Rodriguez Metal', 'Apr 19', 'IN PROGRESS', 'HIGH', '$780'],
|
||
['J-2040', 'Wheel set (4) — Gloss white', 'D&T Auto', 'Apr 19', 'QUEUED', 'NORMAL', '$420'],
|
||
['J-2039', 'Patio table — Matte black', 'Mercer Residential', 'Apr 20', 'CURING', 'NORMAL', '$210'],
|
||
['J-2038', 'Trailer frame — Zinc primer', 'Bluegrass Trailers', 'Apr 22', 'IN PROGRESS', 'LOW', '$1,640'],
|
||
['J-2037', 'Bumper — Satin bronze', 'Heritage Restoration', 'Apr 22', 'READY FOR PICKUP', 'NORMAL', '$340'],
|
||
['J-2036', 'Railing — Hammered copper', 'Summit Builders', 'Apr 23', 'QUEUED', 'HIGH', '$980'],
|
||
];
|
||
const statusStyle = (s) => ({
|
||
'IN PROGRESS': { bg: '#fef3c7', fg: '#92400e' },
|
||
'QUEUED': { bg: '#dbeafe', fg: '#1e40af' },
|
||
'CURING': { bg: '#fee2e2', fg: '#991b1b' },
|
||
'READY FOR PICKUP': { bg: '#d1fae5', fg: '#065f46' },
|
||
}[s]);
|
||
const prioStyle = (p) => ({
|
||
'HIGH': { bg: '#fef3c7', fg: '#92400e' },
|
||
'NORMAL': { bg: '#dbeafe', fg: '#1e40af' },
|
||
'LOW': { bg: '#e5e7eb', fg: '#4b5563' },
|
||
}[p]);
|
||
return (
|
||
<div style={{ display: 'flex', background: '#f4f5f7' }}>
|
||
<SidebarBefore />
|
||
<div style={{ flex: 1, minWidth: 0 }}>
|
||
<TopbarBefore title="Jobs" />
|
||
<div style={{ padding: '24px 28px' }}>
|
||
{/* stat cards */}
|
||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 16, marginBottom: 22 }}>
|
||
{[
|
||
['Total Jobs', '47', 'bi-briefcase', '#dbeafe', '#1e40af'],
|
||
['In Progress', '12', 'bi-hourglass-split', '#fef3c7', '#92400e'],
|
||
['Completed', '28', 'bi-check-circle', '#d1fae5', '#065f46'],
|
||
['Total Value', '$18,420', 'bi-currency-dollar', '#fee2e2', '#991b1b'],
|
||
].map(([l, v, ic, b, f], i) => (
|
||
<div key={i} style={{ background: '#fff', borderRadius: 10, padding: 18, boxShadow: '0 1px 3px rgba(0,0,0,0.06)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||
<div>
|
||
<div style={{ fontSize: 12, color: '#6b7280', marginBottom: 3 }}>{l}</div>
|
||
<div style={{ fontSize: 24, fontWeight: 700 }}>{v}</div>
|
||
</div>
|
||
<div style={{ width: 48, height: 48, borderRadius: '50%', background: b, color: f, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||
<i className={`bi ${ic}`} style={{ fontSize: 20 }} />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* table card */}
|
||
<div style={{ background: '#fff', borderRadius: 10, boxShadow: '0 1px 3px rgba(0,0,0,0.06)' }}>
|
||
<div style={{ padding: '14px 18px', borderBottom: '1px solid #e5e7eb', display: 'flex', justifyContent: 'space-between', gap: 12 }}>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', border: '1.5px solid #9ca3af', borderRadius: 6, padding: '4px 10px', width: 320, gap: 8 }}>
|
||
<i className="bi bi-search" style={{ color: '#9ca3af' }} />
|
||
<input placeholder="Search jobs…" style={{ border: 'none', outline: 'none', flex: 1, fontSize: 13 }} />
|
||
</div>
|
||
<button style={{ background: '#4f46e5', color: '#fff', border: 'none', borderRadius: 6, padding: '0 14px', fontSize: 13, fontWeight: 500 }}><i className="bi bi-search" /></button>
|
||
<button style={{ background: '#fff', color: '#374151', border: '1.5px solid #9ca3af', borderRadius: 6, padding: '6px 12px', fontSize: 13 }}>Filters</button>
|
||
</div>
|
||
<button style={{ background: '#4f46e5', color: '#fff', border: 'none', borderRadius: 6, padding: '8px 14px', fontSize: 13, fontWeight: 500 }}><i className="bi bi-plus-lg" /> New Job</button>
|
||
</div>
|
||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
|
||
<thead>
|
||
<tr>
|
||
{['Job #', 'Description', 'Customer', 'Due', 'Status', 'Priority', 'Total', ''].map((h, i) => (
|
||
<th key={i} style={{ padding: '10px 14px', textAlign: 'left', background: '#f3f4f6', color: '#6b7280', fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', borderBottom: '2px solid #e5e7eb' }}>{h}</th>
|
||
))}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{jobs.map((j, i) => {
|
||
const st = statusStyle(j[4]); const pr = prioStyle(j[5]);
|
||
return (
|
||
<tr key={i} style={{ borderBottom: '1px solid #f3f4f6' }}>
|
||
<td style={{ padding: '12px 14px', fontWeight: 600, color: '#4f46e5' }}>{j[0]}</td>
|
||
<td style={{ padding: '12px 14px' }}>{j[1]}</td>
|
||
<td style={{ padding: '12px 14px' }}>{j[2]}</td>
|
||
<td style={{ padding: '12px 14px' }}>{j[3]}</td>
|
||
<td style={{ padding: '12px 14px' }}><span style={{ background: st.bg, color: st.fg, padding: '4px 10px', borderRadius: 6, fontSize: 10.5, fontWeight: 600 }}>{j[4]}</span></td>
|
||
<td style={{ padding: '12px 14px' }}><span style={{ background: pr.bg, color: pr.fg, padding: '4px 10px', borderRadius: 6, fontSize: 10.5, fontWeight: 600 }}>{j[5]}</span></td>
|
||
<td style={{ padding: '12px 14px', fontWeight: 600 }}>{j[6]}</td>
|
||
<td style={{ padding: '12px 14px', color: '#6b7280' }}><i className="bi bi-three-dots" /></td>
|
||
</tr>
|
||
);
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/* =======================
|
||
JOBS BOARD — BEFORE
|
||
======================= */
|
||
function JobsBoardBefore() {
|
||
const cols = [
|
||
{ title: 'INTAKE', count: 3, tint: '#dbeafe', accent: '#1e40af', cards: [
|
||
{ id: 'J-2049', cust: 'Pine Ridge Metal', desc: 'Gate hardware set', due: 'Apr 24', prio: '#6c757d', powder: 'Gloss black' },
|
||
{ id: 'J-2048', cust: 'Highway Fabworks', desc: 'Railing brackets', due: 'Apr 25', prio: '#ffc107', powder: 'Matte white' },
|
||
{ id: 'J-2047', cust: 'Crestline Custom', desc: 'Bumper set', due: 'Apr 26', prio: '#6c757d', powder: 'Satin bronze' },
|
||
]},
|
||
{ title: 'PREP', count: 2, tint: '#fef3c7', accent: '#92400e', cards: [
|
||
{ id: 'J-2045', cust: 'Rodriguez Metal', desc: 'Fence gate', due: 'Apr 19', prio: '#dc3545', powder: 'Anodized black' },
|
||
{ id: 'J-2044', cust: 'D&T Auto', desc: 'Wheel set (4)', due: 'Apr 19', prio: '#ffc107', powder: 'Gloss white' },
|
||
]},
|
||
{ title: 'COATING', count: 3, tint: '#fee2e2', accent: '#991b1b', cards: [
|
||
{ id: 'J-2042', cust: 'Mercer', desc: 'Patio table', due: 'Apr 20', prio: '#6c757d', powder: 'Matte black' },
|
||
{ id: 'J-2041', cust: 'Bluegrass', desc: 'Trailer frame', due: 'Apr 22', prio: '#198754', powder: 'Zinc primer' },
|
||
{ id: 'J-2040', cust: 'Summit', desc: 'Railing', due: 'Apr 23', prio: '#dc3545', powder: 'Hammered copper' },
|
||
]},
|
||
{ title: 'CURING', count: 2, tint: '#e0e7ff', accent: '#3730a3', cards: [
|
||
{ id: 'J-2038', cust: 'Heritage', desc: 'Bumper', due: 'Apr 22', prio: '#6c757d', powder: 'Satin bronze' },
|
||
{ id: 'J-2037', cust: 'Parkside', desc: 'Chair set (6)', due: 'Apr 22', prio: '#ffc107', powder: 'Gloss red' },
|
||
]},
|
||
{ title: 'READY', count: 1, tint: '#d1fae5', accent: '#065f46', cards: [
|
||
{ id: 'J-2034', cust: 'Lakeside Marina', desc: 'Railing kit', due: 'Apr 18', prio: '#198754', powder: 'Matte white' },
|
||
]},
|
||
];
|
||
return (
|
||
<div style={{ display: 'flex', background: '#f4f5f7' }}>
|
||
<SidebarBefore />
|
||
<div style={{ flex: 1, minWidth: 0 }}>
|
||
<TopbarBefore title="Jobs Board" />
|
||
<div style={{ padding: '24px 28px' }}>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 14 }}>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<button style={{ background: '#fff', border: '1.5px solid #9ca3af', borderRadius: 6, padding: '6px 12px', fontSize: 13 }}>All customers</button>
|
||
<button style={{ background: '#fff', border: '1.5px solid #9ca3af', borderRadius: 6, padding: '6px 12px', fontSize: 13 }}>All priorities</button>
|
||
</div>
|
||
<button style={{ background: '#4f46e5', color: '#fff', border: 'none', borderRadius: 6, padding: '8px 14px', fontSize: 13, fontWeight: 500 }}>+ New Job</button>
|
||
</div>
|
||
|
||
<div style={{ display: 'flex', gap: 10, overflow: 'hidden' }}>
|
||
{cols.map((c, i) => (
|
||
<div key={i} style={{ width: 220, background: '#e9ecef', borderRadius: 8, overflow: 'hidden', flexShrink: 0 }}>
|
||
<div style={{ padding: '10px 12px', display: 'flex', justifyContent: 'space-between', fontSize: 12, fontWeight: 600 }}>
|
||
<span style={{ background: c.tint, color: c.accent, padding: '3px 8px', borderRadius: 4, fontSize: 10.5, letterSpacing: '0.04em' }}>{c.title}</span>
|
||
<span style={{ color: '#6b7280', fontSize: 11 }}>{c.count}</span>
|
||
</div>
|
||
<div style={{ padding: '4px 8px 12px' }}>
|
||
{c.cards.map((card, j) => (
|
||
<div key={j} style={{
|
||
background: '#fff', borderRadius: 5, border: '1px solid #e5e7eb',
|
||
borderLeft: `4px solid ${card.prio}`, padding: '9px 10px', marginBottom: 7, fontSize: 12,
|
||
}}>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
|
||
<span style={{ fontWeight: 600, color: '#4f46e5', fontSize: 11.5 }}>{card.id}</span>
|
||
<span style={{ color: '#6b7280', fontSize: 10.5 }}>{card.due}</span>
|
||
</div>
|
||
<div style={{ fontWeight: 500, marginBottom: 2, lineHeight: 1.3 }}>{card.desc}</div>
|
||
<div style={{ color: '#6b7280', fontSize: 11, marginBottom: 4 }}>{card.cust}</div>
|
||
<div style={{ color: '#6b7280', fontSize: 10.5 }}><i className="bi bi-droplet-fill" style={{ marginRight: 3 }} /> {card.powder}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
Object.assign(window, { DashboardBefore, JobsIndexBefore, JobsBoardBefore });
|