1167 lines
66 KiB
HTML
1167 lines
66 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Pelagia Marine Portal – Wireframes</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@400;600;700&family=Special+Elite&display=swap" rel="stylesheet">
|
||
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
||
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
||
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
||
<script type="text/babel" src="design-canvas.jsx"></script>
|
||
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body { background: #f5f0e8; font-family: 'Caveat', cursive; }
|
||
|
||
/* Sketchy global style */
|
||
.wf { font-family: 'Caveat', cursive; }
|
||
.wf-label { font-size: 11px; font-family: 'Caveat', cursive; color: #888; letter-spacing: 0.05em; text-transform: uppercase; }
|
||
|
||
/* Sketch box */
|
||
.sk-box {
|
||
border: 2px solid #222;
|
||
border-radius: 3px;
|
||
background: #fff;
|
||
position: relative;
|
||
}
|
||
.sk-box.filled { background: #e8e4dc; }
|
||
.sk-box.dark { background: #222; }
|
||
.sk-box.accent { background: #d4e9f7; border-color: #3a7cbf; }
|
||
.sk-box.warn { background: #fdf3d0; border-color: #c8971a; }
|
||
.sk-box.success { background: #d8f0e0; border-color: #2a8a50; }
|
||
.sk-box.danger { background: #fde0e0; border-color: #c03030; }
|
||
|
||
.sk-btn {
|
||
display: inline-block;
|
||
border: 2px solid #222;
|
||
border-radius: 3px;
|
||
padding: 5px 14px;
|
||
font-family: 'Caveat', cursive;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
cursor: default;
|
||
background: #fff;
|
||
}
|
||
.sk-btn.primary { background: #222; color: #fff; }
|
||
.sk-btn.accent { background: #3a7cbf; color: #fff; border-color: #3a7cbf; }
|
||
.sk-btn.sm { font-size: 13px; padding: 3px 10px; }
|
||
.sk-btn.warn { background: #c8971a; color: #fff; border-color: #c8971a; }
|
||
.sk-btn.success { background: #2a8a50; color: #fff; border-color: #2a8a50; }
|
||
.sk-btn.danger { background: #c03030; color: #fff; border-color: #c03030; }
|
||
|
||
.sk-input {
|
||
border: 2px solid #aaa;
|
||
border-radius: 2px;
|
||
background: #fafaf8;
|
||
padding: 4px 8px;
|
||
font-family: 'Caveat', cursive;
|
||
font-size: 14px;
|
||
color: #555;
|
||
width: 100%;
|
||
}
|
||
|
||
.sk-tag {
|
||
display: inline-block;
|
||
border: 1.5px solid #aaa;
|
||
border-radius: 20px;
|
||
padding: 2px 10px;
|
||
font-size: 12px;
|
||
font-family: 'Caveat', cursive;
|
||
background: #f5f0e8;
|
||
}
|
||
.sk-tag.blue { border-color: #3a7cbf; color: #3a7cbf; background: #d4e9f7; }
|
||
.sk-tag.green { border-color: #2a8a50; color: #2a8a50; background: #d8f0e0; }
|
||
.sk-tag.orange { border-color: #c8971a; color: #c8971a; background: #fdf3d0; }
|
||
.sk-tag.red { border-color: #c03030; color: #c03030; background: #fde0e0; }
|
||
.sk-tag.gray { border-color: #888; color: #666; background: #eee; }
|
||
|
||
.sk-divider { border: none; border-top: 1.5px dashed #ccc; margin: 8px 0; }
|
||
|
||
/* Squiggly underline annotation */
|
||
.annotation {
|
||
font-family: 'Caveat', cursive;
|
||
font-size: 12px;
|
||
color: #3a7cbf;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* Sidebar nav item */
|
||
.nav-item {
|
||
display: flex; align-items: center; gap: 8px;
|
||
padding: 7px 12px;
|
||
font-family: 'Caveat', cursive;
|
||
font-size: 15px;
|
||
border-left: 3px solid transparent;
|
||
cursor: default;
|
||
border-radius: 2px;
|
||
}
|
||
.nav-item.active { background: #e8e4dc; border-left-color: #222; font-weight: 700; }
|
||
.nav-item:not(.active) { color: #555; }
|
||
|
||
/* Table rows */
|
||
.sk-table { width: 100%; border-collapse: collapse; font-family: 'Caveat', cursive; font-size: 13px; }
|
||
.sk-table th { border-bottom: 2px solid #222; padding: 4px 8px; text-align: left; font-size: 12px; text-transform: uppercase; color: #555; }
|
||
.sk-table td { border-bottom: 1px dashed #ccc; padding: 5px 8px; vertical-align: middle; }
|
||
.sk-table tr:last-child td { border-bottom: none; }
|
||
|
||
/* Stat card */
|
||
.stat-card { border: 2px solid #222; border-radius: 3px; background: #fff; padding: 10px 14px; }
|
||
.stat-num { font-size: 32px; font-weight: 700; line-height: 1; }
|
||
.stat-lbl { font-size: 12px; color: #888; margin-top: 2px; }
|
||
|
||
/* Avatar chip */
|
||
.avatar { width: 28px; height: 28px; border-radius: 50%; background: #222; border: 2px solid #222; display: inline-flex; align-items: center; justify-content: center; color: #fff; font-size: 12px; font-weight: 700; flex-shrink: 0; }
|
||
|
||
/* Screen label for annotation arrows */
|
||
.screen-note {
|
||
position: absolute;
|
||
font-family: 'Caveat', cursive;
|
||
font-size: 12px;
|
||
color: #3a7cbf;
|
||
pointer-events: none;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<script type="text/babel">
|
||
const { useState } = React;
|
||
|
||
/* ─── SHARED SHELL ─────────────────────────────────────────── */
|
||
function Shell({ role, activeNav, children, headerRight }) {
|
||
const roleColors = {
|
||
admin: '#8b3fbe', technical: '#3a7cbf', accounts: '#2a8a50',
|
||
manning: '#c8971a', supplies: '#c03030', manager: '#555'
|
||
};
|
||
const color = roleColors[role] || '#222';
|
||
const navItems = {
|
||
admin: ['Dashboard','Purchase Orders','Users','Settings'],
|
||
technical: ['Dashboard','New PO','My Orders'],
|
||
accounts: ['Dashboard','Payment Queue','All Orders'],
|
||
manning: ['Dashboard','New PO','My Orders'],
|
||
supplies: ['Dashboard','Inventory','Orders'],
|
||
manager: ['Dashboard','Approvals','Purchase Orders','Reports'],
|
||
};
|
||
const items = navItems[role] || [];
|
||
return (
|
||
<div style={{ display:'flex', flexDirection:'column', height:'100%', background:'#fafaf8', border:'2px solid #222', borderRadius:4, overflow:'hidden' }}>
|
||
{/* Top bar */}
|
||
<div style={{ display:'flex', alignItems:'center', gap:10, padding:'8px 14px', borderBottom:'2px solid #222', background:'#222' }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:6 }}>
|
||
{/* anchor icon placeholder */}
|
||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none"><circle cx="9" cy="9" r="8" stroke="#fff" strokeWidth="2"/><line x1="9" y1="4" x2="9" y2="14" stroke="#fff" strokeWidth="2"/><line x1="5" y1="12" x2="13" y2="12" stroke="#fff" strokeWidth="1.5"/><circle cx="9" cy="4" r="1.5" fill="#fff"/></svg>
|
||
<span style={{ color:'#fff', fontWeight:700, fontSize:16 }}>PELAGIA MARINE</span>
|
||
</div>
|
||
<div style={{ flex:1 }}/>
|
||
<span className="sk-tag" style={{ background:'rgba(255,255,255,0.1)', border:`1.5px solid ${color}`, color:'#fff', fontSize:11 }}>{role.toUpperCase()}</span>
|
||
{headerRight}
|
||
<div className="avatar">{role[0].toUpperCase()}</div>
|
||
</div>
|
||
<div style={{ display:'flex', flex:1, overflow:'hidden' }}>
|
||
{/* Sidebar */}
|
||
<div style={{ width:130, borderRight:'2px solid #222', background:'#f5f0e8', padding:'10px 0', flexShrink:0 }}>
|
||
{items.map(item => (
|
||
<div key={item} className={`nav-item${item === activeNav ? ' active' : ''}`}>
|
||
<span style={{ fontSize:13 }}>{item}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
{/* Content */}
|
||
<div style={{ flex:1, overflow:'auto', padding:16 }}>
|
||
{children}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 1: LOGIN ────────────────────────────────────────── */
|
||
function LoginScreen() {
|
||
return (
|
||
<div style={{ display:'flex', flexDirection:'column', height:'100%', background:'#fafaf8', position:'relative' }}>
|
||
{/* Ocean texture stripe */}
|
||
<div style={{ height:6, background:'repeating-linear-gradient(90deg,#3a7cbf 0,#3a7cbf 12px,#5a9cd0 12px,#5a9cd0 24px)' }}/>
|
||
<div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center' }}>
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:20, width:260 }}>
|
||
{/* Logo block */}
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:6 }}>
|
||
<div style={{ width:56, height:56, border:'3px solid #222', borderRadius:4, display:'flex', alignItems:'center', justifyContent:'center', background:'#222' }}>
|
||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||
<circle cx="16" cy="10" r="4" stroke="#fff" strokeWidth="2"/>
|
||
<line x1="16" y1="14" x2="16" y2="26" stroke="#fff" strokeWidth="2"/>
|
||
<path d="M8 22 Q16 28 24 22" stroke="#fff" strokeWidth="2" fill="none"/>
|
||
<line x1="10" y1="26" x2="22" y2="26" stroke="#fff" strokeWidth="1.5"/>
|
||
</svg>
|
||
</div>
|
||
<div style={{ textAlign:'center' }}>
|
||
<div style={{ fontSize:20, fontWeight:700, letterSpacing:1 }}>PELAGIA MARINE</div>
|
||
<div style={{ fontSize:12, color:'#888', letterSpacing:2 }}>COMPANY PORTAL</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Login card */}
|
||
<div className="sk-box" style={{ width:'100%', padding:20, display:'flex', flexDirection:'column', gap:12 }}>
|
||
<div style={{ fontSize:15, fontWeight:700 }}>Sign In</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">Employee ID or Email</span>
|
||
<div className="sk-input" style={{ color:'#bbb' }}>e.g. EMP-0042</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">Password</span>
|
||
<div className="sk-input" style={{ color:'#bbb' }}>••••••••</div>
|
||
</div>
|
||
<div style={{ display:'flex', alignItems:'center', gap:6 }}>
|
||
<div style={{ width:14, height:14, border:'2px solid #aaa', borderRadius:2, flexShrink:0 }}/>
|
||
<span style={{ fontSize:13, color:'#666' }}>Remember this device</span>
|
||
</div>
|
||
<div className="sk-btn primary" style={{ textAlign:'center', marginTop:4 }}>Sign In →</div>
|
||
<div style={{ textAlign:'center', fontSize:12, color:'#aaa' }}>Forgot password? Contact IT</div>
|
||
</div>
|
||
|
||
{/* Role badge hint */}
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:6 }}>
|
||
<span style={{ fontSize:12, color:'#aaa' }}>Access is role-based:</span>
|
||
<div style={{ display:'flex', gap:6, flexWrap:'wrap', justifyContent:'center' }}>
|
||
{['Admin','Accounts','Technical','Manning','Supplies','Manager'].map(r => (
|
||
<span key={r} className="sk-tag" style={{ fontSize:11 }}>{r}</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* annotation */}
|
||
<div style={{ position:'absolute', bottom:12, right:16, fontSize:12, color:'#3a7cbf', fontStyle:'italic', fontFamily:'Caveat' }}>
|
||
single entry point, role resolved on login →
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 2: DASHBOARD (role-aware) ─────────────────────── */
|
||
function DashboardScreen({ role }) {
|
||
const configs = {
|
||
technical: {
|
||
stats: [{ n:'3', l:'My Open POs' },{ n:'1', l:'Awaiting Manager' },{ n:'7', l:'Fully Approved (30d)' }],
|
||
quick: 'New Purchase Order',
|
||
recentLabel: 'My Recent Orders',
|
||
rows: [
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', vessel:'MV Kestrel', amount:'$4,200', status:'mgr-review' },
|
||
{ id:'PO-2038', item:'Safety Harnesses ×12', vessel:'MV Falcon', amount:'$1,800', status:'payment' },
|
||
{ id:'PO-2031', item:'Anchor Chain Shackles', vessel:'MV Osprey', amount:'$650', status:'approved' },
|
||
]
|
||
},
|
||
accounts: {
|
||
stats: [{ n:'8', l:'Ready for Payment' },{ n:'$184k', l:'Payment Queue Value' },{ n:'47', l:'Paid (30d)' }],
|
||
quick: 'Process Payments',
|
||
recentLabel: 'Manager-Approved — Awaiting Payment',
|
||
rows: [
|
||
{ id:'PO-2042', item:'Dredge Cutter Head Parts', vessel:'MV Kestrel', amount:'$38,500', status:'payment' },
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', vessel:'MV Kestrel', amount:'$4,200', status:'payment' },
|
||
{ id:'PO-2039', item:'Navigation Radar Unit', vessel:'MV Osprey', amount:'$21,000', status:'payment' },
|
||
]
|
||
},
|
||
manager: {
|
||
stats: [{ n:'12', l:'Awaiting My Approval' },{ n:'$1.2M', l:'Spend (YTD)' },{ n:'194', l:'Total POs (YTD)' }],
|
||
quick: 'Review Approvals',
|
||
recentLabel: 'Pending My Review',
|
||
rows: [
|
||
{ id:'PO-2042', item:'Dredge Cutter Head Parts', vessel:'MV Kestrel', amount:'$38,500', status:'mgr-review' },
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', vessel:'MV Kestrel', amount:'$4,200', status:'payment' },
|
||
{ id:'PO-2038', item:'Safety Harnesses ×12', vessel:'MV Falcon', amount:'$1,800', status:'approved' },
|
||
]
|
||
},
|
||
admin: {
|
||
stats: [{ n:'6', l:'Active Roles' },{ n:'23', l:'Users' },{ n:'12', l:'Pending POs' }],
|
||
quick: 'Manage Users',
|
||
recentLabel: 'System Activity',
|
||
rows: [
|
||
{ id:'PO-2042', item:'Dredge Cutter Head Parts', vessel:'MV Kestrel', amount:'$38,500', status:'pending' },
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', vessel:'MV Kestrel', amount:'$4,200', status:'approved' },
|
||
{ id:'PO-2038', item:'Safety Harnesses ×12', vessel:'MV Falcon', amount:'$1,800', status:'approved' },
|
||
]
|
||
},
|
||
manning: {
|
||
stats: [{ n:'2', l:'My Open POs' },{ n:'0', l:'Pending Approval' },{ n:'5', l:'Approved (30d)' }],
|
||
quick: 'New Purchase Order',
|
||
recentLabel: 'My Recent Orders',
|
||
rows: [
|
||
{ id:'PO-2035', item:'Crew Uniforms ×20', vessel:'MV Falcon', amount:'$2,100', status:'approved' },
|
||
{ id:'PO-2030', item:'Safety Boots ×10', vessel:'MV Osprey', amount:'$900', status:'approved' },
|
||
]
|
||
},
|
||
};
|
||
const cfg = configs[role] || configs.technical;
|
||
const statusTag = (s) => {
|
||
if (s === 'mgr-review') return <span className="sk-tag orange">Mgr. Review</span>;
|
||
if (s === 'payment') return <span className="sk-tag blue">Awaiting Payment</span>;
|
||
if (s === 'approved') return <span className="sk-tag green">Paid ✓</span>;
|
||
if (s === 'edits') return <span className="sk-tag red">Edits Requested</span>;
|
||
if (s === 'rejected') return <span className="sk-tag red">Rejected</span>;
|
||
return <span className="sk-tag gray">{s}</span>;
|
||
};
|
||
return (
|
||
<Shell role={role} activeNav="Dashboard">
|
||
<div style={{ display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>Welcome back, {role.charAt(0).toUpperCase()+role.slice(1)} 👋</div>
|
||
{(role === 'technical' || role === 'manning' || role === 'admin') && (
|
||
<div className="sk-btn accent">{cfg.quick}</div>
|
||
)}
|
||
{role === 'accounts' && <div className="sk-btn warn">{cfg.quick}</div>}
|
||
{role === 'manager' && <div className="sk-btn" style={{ border:'2px solid #555' }}>{cfg.quick}</div>}
|
||
</div>
|
||
|
||
{/* Stats row */}
|
||
<div style={{ display:'flex', gap:10 }}>
|
||
{cfg.stats.map((s, i) => (
|
||
<div key={i} className="stat-card" style={{ flex:1 }}>
|
||
<div className="stat-num">{s.n}</div>
|
||
<div className="stat-lbl">{s.l}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Recent table */}
|
||
<div className="sk-box" style={{ padding:12 }}>
|
||
<div style={{ fontSize:14, fontWeight:700, marginBottom:8 }}>{cfg.recentLabel}</div>
|
||
<table className="sk-table">
|
||
<thead>
|
||
<tr>
|
||
<th>PO #</th><th>Item</th><th>Vessel</th><th>Amount</th><th>Status</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{cfg.rows.map(r => (
|
||
<tr key={r.id}>
|
||
<td style={{ color:'#3a7cbf', fontWeight:600 }}>{r.id}</td>
|
||
<td>{r.item}</td>
|
||
<td>{r.vessel}</td>
|
||
<td style={{ fontWeight:600 }}>{r.amount}</td>
|
||
<td>{statusTag(r.status)}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{role === 'manager' && (
|
||
<div style={{ display:'flex', gap:10 }}>
|
||
<div className="sk-box" style={{ flex:1, padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:8 }}>Spend by Vessel (placeholder)</div>
|
||
<div style={{ height:70, background:'repeating-linear-gradient(0deg,#e8e4dc 0px,#e8e4dc 2px,transparent 2px,transparent 10px)', display:'flex', alignItems:'flex-end', gap:6, padding:'0 8px 0' }}>
|
||
{[55,80,40,95,60,70,50].map((h,i)=>(
|
||
<div key={i} style={{ flex:1, height:`${h}%`, background:'#3a7cbf', opacity:0.7, borderRadius:'2px 2px 0 0' }}/>
|
||
))}
|
||
</div>
|
||
<div style={{ fontSize:11, color:'#aaa', marginTop:4, textAlign:'center' }}>last 7 vessels</div>
|
||
</div>
|
||
<div className="sk-box" style={{ flex:1, padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:8 }}>Monthly Spend (placeholder)</div>
|
||
<div style={{ height:70, background:'repeating-linear-gradient(0deg,#e8e4dc 0px,#e8e4dc 2px,transparent 2px,transparent 10px)', display:'flex', alignItems:'flex-end', gap:6, padding:'0 8px 0' }}>
|
||
{[45,60,80,55,90,75,85,65,95,70,80,60].map((h,i)=>(
|
||
<div key={i} style={{ flex:1, height:`${h}%`, background:'#c8971a', opacity:0.7, borderRadius:'2px 2px 0 0' }}/>
|
||
))}
|
||
</div>
|
||
<div style={{ fontSize:11, color:'#aaa', marginTop:4, textAlign:'center' }}>Jan → Dec</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 3: NEW PURCHASE ORDER FORM ─────────────────────── */
|
||
function NewPOScreen({ role }) {
|
||
return (
|
||
<Shell role={role} activeNav="New PO">
|
||
<div style={{ display:'flex', gap:16 }}>
|
||
{/* Main form */}
|
||
<div style={{ flex:2, display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:10 }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>New Purchase Order</div>
|
||
<span className="sk-tag gray">Draft</span>
|
||
</div>
|
||
|
||
{/* Section: Order Info */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, borderBottom:'1.5px solid #eee', paddingBottom:6 }}>Order Information</div>
|
||
<div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10 }}>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">PO Number (auto)</span>
|
||
<div className="sk-input" style={{ color:'#aaa', background:'#f5f0e8' }}>PO-2043</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">Date</span>
|
||
<div className="sk-input" style={{ color:'#aaa', background:'#f5f0e8' }}>28 Apr 2026</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4, gridColumn:'span 2' }}>
|
||
<span className="wf-label">Vessel *</span>
|
||
<div className="sk-input" style={{ color:'#bbb' }}>Select vessel…</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4, gridColumn:'span 2' }}>
|
||
<span className="wf-label">Account ID *</span>
|
||
<div className="sk-input" style={{ color:'#bbb' }}>e.g. ACC-2210</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Section: Line Items */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:10 }}>
|
||
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', borderBottom:'1.5px solid #eee', paddingBottom:6 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>Line Items</div>
|
||
<div className="sk-btn sm">+ Add Item</div>
|
||
</div>
|
||
{/* Header */}
|
||
<div style={{ display:'grid', gridTemplateColumns:'2fr 1fr 80px 80px 30px', gap:6 }}>
|
||
<span className="wf-label">Item Description</span>
|
||
<span className="wf-label">Item Code</span>
|
||
<span className="wf-label">Qty</span>
|
||
<span className="wf-label">Unit Cost</span>
|
||
<span/>
|
||
</div>
|
||
{/* Row 1 */}
|
||
{[
|
||
{ desc:'Hydraulic Pump Seal Kit', code:'ENG-4421', qty:'2', cost:'$2,100' },
|
||
{ desc:'Stainless Bolts M16 ×50', code:'FAB-1102', qty:'5', cost:'$120' },
|
||
].map((row, i) => (
|
||
<div key={i} style={{ display:'grid', gridTemplateColumns:'2fr 1fr 80px 80px 30px', gap:6, alignItems:'center' }}>
|
||
<div className="sk-input">{row.desc}</div>
|
||
<div className="sk-input">{row.code}</div>
|
||
<div className="sk-input">{row.qty}</div>
|
||
<div className="sk-input">{row.cost}</div>
|
||
<div style={{ color:'#c03030', cursor:'default', fontSize:16, textAlign:'center' }}>✕</div>
|
||
</div>
|
||
))}
|
||
<hr className="sk-divider"/>
|
||
<div style={{ display:'flex', justifyContent:'flex-end', gap:20, fontSize:14 }}>
|
||
<span style={{ color:'#888' }}>Subtotal</span>
|
||
<span style={{ fontWeight:700 }}>$4,440</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Vendor */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:10, borderColor:'#5c2a5c' }}>
|
||
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', borderBottom:'1.5px solid #eee', paddingBottom:6 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>Vendor <span style={{ color:'#888', fontWeight:400, fontSize:12 }}>(optional — can be added later)</span></div>
|
||
<span className="sk-tag" style={{ fontSize:11, borderColor:'#5c2a5c', color:'#5c2a5c', background:'#f8f0ff' }}>NEW</span>
|
||
</div>
|
||
<div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10 }}>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">Vendor ID</span>
|
||
<div className="sk-input" style={{ color:'#bbb' }}>e.g. VND-0042</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">Vendor Name</span>
|
||
<div className="sk-input" style={{ color:'#bbb' }}>Search or enter vendor…</div>
|
||
</div>
|
||
</div>
|
||
<div className="annotation" style={{ fontSize:11 }}>If blank, manager may request Vendor ID before approving</div>
|
||
</div>
|
||
|
||
{/* Notes */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:6 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, borderBottom:'1.5px solid #eee', paddingBottom:6 }}>Notes / Justification</div>
|
||
<div className="sk-input" style={{ minHeight:60, color:'#bbb' }}>Add any notes for the approver…</div>
|
||
</div>
|
||
|
||
{/* Actions */}
|
||
<div style={{ display:'flex', gap:10, justifyContent:'flex-end' }}>
|
||
<div className="sk-btn">Save Draft</div>
|
||
<div className="sk-btn accent">Submit for Approval →</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Side panel: PDF upload & summary */}
|
||
<div style={{ flex:1, display:'flex', flexDirection:'column', gap:12 }}>
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:10 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>Attach Documents</div>
|
||
<div className="sk-box filled" style={{ padding:20, textAlign:'center', borderStyle:'dashed', display:'flex', flexDirection:'column', alignItems:'center', gap:6 }}>
|
||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none"><rect x="6" y="3" width="16" height="22" rx="2" stroke="#888" strokeWidth="2"/><line x1="10" y1="10" x2="18" y2="10" stroke="#888" strokeWidth="1.5"/><line x1="10" y1="14" x2="18" y2="14" stroke="#888" strokeWidth="1.5"/><line x1="10" y1="18" x2="15" y2="18" stroke="#888" strokeWidth="1.5"/></svg>
|
||
<span style={{ fontSize:12, color:'#888' }}>Drop PDF / Quote here</span>
|
||
<div className="sk-btn sm">Browse…</div>
|
||
</div>
|
||
<div style={{ display:'flex', alignItems:'center', gap:8, fontSize:12 }}>
|
||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="1" width="12" height="14" rx="1" stroke="#c03030" strokeWidth="1.5"/><line x1="5" y1="6" x2="11" y2="6" stroke="#c03030" strokeWidth="1"/><line x1="5" y1="9" x2="11" y2="9" stroke="#c03030" strokeWidth="1"/></svg>
|
||
<span>Supplier_Quote_Apr2026.pdf</span>
|
||
<span style={{ color:'#c03030', marginLeft:'auto' }}>✕</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* PO Summary */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:8 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>PO Summary</div>
|
||
{[['Items','2'],['Vessel','MV Kestrel'],['Account','ACC-2210'],['Total','$4,440']].map(([k,v]) => (
|
||
<div key={k} style={{ display:'flex', justifyContent:'space-between', fontSize:13 }}>
|
||
<span style={{ color:'#888' }}>{k}</span>
|
||
<span style={{ fontWeight:600 }}>{v}</span>
|
||
</div>
|
||
))}
|
||
<hr className="sk-divider"/>
|
||
<div className="annotation" style={{ fontSize:11 }}>⟶ Submitted POs go to Manager for approval first</div>
|
||
</div>
|
||
|
||
{/* Approval flow visual */}
|
||
<div className="sk-box" style={{ padding:12, display:'flex', flexDirection:'column', gap:6 }}>
|
||
<div style={{ fontSize:12, fontWeight:700, color:'#888' }}>Approval Flow</div>
|
||
{[
|
||
{ label:'Draft', active:true },
|
||
{ label:'Submitted', active:true },
|
||
{ label:'Manager Review', active:false, note:'Reject / Ask Edits / Approve' },
|
||
{ label:'Accounts Payment', active:false, note:'Payment processing' },
|
||
{ label:'Complete', active:false },
|
||
].map((step, i) => (
|
||
<div key={step.label} style={{ display:'flex', alignItems:'flex-start', gap:8 }}>
|
||
<div style={{ width:20, height:20, border:'2px solid', borderColor: step.active ? '#3a7cbf' : '#ccc', borderRadius:'50%', display:'flex', alignItems:'center', justifyContent:'center', fontSize:10, fontWeight:700, color: step.active ? '#3a7cbf':'#ccc', flexShrink:0, marginTop:1 }}>{i+1}</div>
|
||
<div>
|
||
<div style={{ fontSize:12, color: step.active ? '#222':'#aaa' }}>{step.label}</div>
|
||
{step.note && <div style={{ fontSize:10, color:'#aaa' }}>{step.note}</div>}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 4: MANAGER – PENDING APPROVALS ──────────────── */
|
||
function ManagerApprovalsScreen() {
|
||
const orders = [
|
||
{ id:'PO-2042', item:'Dredge Cutter Head Parts', code:'ENG-8801', vessel:'MV Kestrel', account:'ACC-1140', submitter:'J. Santos (Tech)', amount:'$38,500', date:'28 Apr', docs:true },
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', code:'ENG-4421', vessel:'MV Kestrel', account:'ACC-1140', submitter:'J. Santos (Tech)', amount:'$4,200', date:'27 Apr', docs:true },
|
||
{ id:'PO-2039', item:'Navigation Radar Unit', code:'NAV-0212', vessel:'MV Osprey', account:'ACC-2290', submitter:'R. Cruz (Tech)', amount:'$21,000', date:'26 Apr', docs:false },
|
||
{ id:'PO-2037', item:'Crew Uniforms ×20', code:'MAN-3301', vessel:'MV Falcon', account:'ACC-3310', submitter:'M. Reyes (Manning)', amount:'$2,100', date:'25 Apr', docs:true },
|
||
{ id:'PO-2033', item:'Anchor Chain Shackles', code:'FAB-0044', vessel:'MV Osprey', account:'ACC-2290', submitter:'D. Lee (Tech)', amount:'$650', date:'24 Apr', docs:false },
|
||
];
|
||
return (
|
||
<Shell role="manager" activeNav="Approvals">
|
||
<div style={{ display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:10 }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>Pending My Approval</div>
|
||
<span className="sk-tag orange">12 awaiting</span>
|
||
</div>
|
||
<div style={{ display:'flex', gap:8, alignItems:'center' }}>
|
||
<div style={{ flex:1, display:'flex', alignItems:'center', gap:8, border:'2px solid #aaa', borderRadius:3, padding:'5px 10px', background:'#fff' }}>
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="#888" strokeWidth="1.5"/><line x1="9" y1="9" x2="13" y2="13" stroke="#888" strokeWidth="1.5"/></svg>
|
||
<span style={{ fontFamily:'Caveat', fontSize:14, color:'#aaa' }}>Search by item, code, vessel, account, submitter…</span>
|
||
</div>
|
||
<div style={{ display:'flex', gap:6 }}>
|
||
<div className="sk-btn sm">Vessel ▾</div>
|
||
<div className="sk-btn sm">Account ▾</div>
|
||
<div className="sk-btn sm">Date ▾</div>
|
||
</div>
|
||
</div>
|
||
<div className="annotation" style={{ marginTop:-6 }}>full-text search across all PO fields ↑</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:10 }}>
|
||
{orders.map((o, idx) => (
|
||
<div key={o.id} className="sk-box" style={{ padding:12, display:'flex', gap:12, alignItems:'flex-start' }}>
|
||
{idx === 0 && <div style={{ position:'absolute', top:0, left:0, width:4, height:'100%', background:'#c8971a', borderRadius:'2px 0 0 2px' }}/>}
|
||
<div style={{ flex:1, display:'flex', flexDirection:'column', gap:4 }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||
<span style={{ fontWeight:700, color:'#3a7cbf' }}>{o.id}</span>
|
||
<span style={{ fontWeight:600, fontSize:15 }}>{o.item}</span>
|
||
<span className="sk-tag gray" style={{ fontSize:11 }}>{o.code}</span>
|
||
{o.docs && <span className="sk-tag blue" style={{ fontSize:11 }}>📄 PDF</span>}
|
||
</div>
|
||
<div style={{ display:'flex', gap:16, fontSize:13, color:'#666' }}>
|
||
<span>⚓ {o.vessel}</span>
|
||
<span>🗂 {o.account}</span>
|
||
<span>👤 {o.submitter}</span>
|
||
<span>📅 {o.date}</span>
|
||
</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'flex-end', gap:8, flexShrink:0 }}>
|
||
<span style={{ fontWeight:700, fontSize:16 }}>{o.amount}</span>
|
||
<div style={{ display:'flex', gap:6 }}>
|
||
<div className="sk-btn sm">View →</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ fontSize:12, color:'#aaa', textAlign:'center' }}>showing 5 of 12 pending — load more ↓</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 5: MANAGER – ORDER DETAIL / REVIEW ──────────── */
|
||
function ManagerOrderDetailScreen() {
|
||
const [action, setAction] = React.useState(null);
|
||
return (
|
||
<Shell role="manager" activeNav="Approvals">
|
||
<div style={{ display:'flex', gap:16 }}>
|
||
<div style={{ flex:2, display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ fontSize:13, color:'#888' }}>← Approvals / <span style={{ color:'#222' }}>PO-2042</span></div>
|
||
<div style={{ display:'flex', alignItems:'center', gap:10 }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>PO-2042 — Dredge Cutter Head Parts</div>
|
||
<span className="sk-tag orange">Awaiting Manager</span>
|
||
</div>
|
||
|
||
<div className="sk-box" style={{ padding:12, display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:10 }}>
|
||
{[['Vessel','MV Kestrel'],['Account ID','ACC-1140'],['Submitted By','J. Santos'],['Date','28 Apr 2026'],['Department','Technical'],['Total','$38,500']].map(([k,v]) => (
|
||
<div key={k} style={{ display:'flex', flexDirection:'column', gap:2 }}>
|
||
<span className="wf-label">{k}</span>
|
||
<span style={{ fontWeight:600, fontSize:14 }}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="sk-box" style={{ padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:8 }}>Line Items</div>
|
||
<table className="sk-table">
|
||
<thead><tr><th>Item</th><th>Code</th><th>Qty</th><th>Unit</th><th>Total</th></tr></thead>
|
||
<tbody>
|
||
{[
|
||
{ item:'Cutter Head Wear Plate', code:'ENG-8801A', qty:4, unit:'$7,500', total:'$30,000' },
|
||
{ item:'Cutter Arm Bearing Set', code:'ENG-8801B', qty:2, unit:'$4,000', total:'$8,000' },
|
||
{ item:'Mounting Hardware Kit', code:'FAB-0091', qty:1, unit:'$500', total:'$500' },
|
||
].map(r => (
|
||
<tr key={r.code}>
|
||
<td>{r.item}</td><td style={{ color:'#888' }}>{r.code}</td><td>{r.qty}</td><td>{r.unit}</td><td style={{ fontWeight:600 }}>{r.total}</td>
|
||
</tr>
|
||
))}
|
||
<tr><td colSpan={4} style={{ textAlign:'right', color:'#888' }}>Total</td><td style={{ fontWeight:700 }}>$38,500</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div className="sk-box" style={{ padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:6 }}>Submitter Notes</div>
|
||
<div style={{ fontSize:14, color:'#555', lineHeight:1.5 }}>
|
||
Cutter head parts are due for replacement per March 2026 inspection. Vessel returns to port 5 May — parts must be on hand for immediate installation.
|
||
</div>
|
||
</div>
|
||
|
||
{/* Vendor Review — NEW */}
|
||
<div className="sk-box" style={{ padding:12, borderColor:'#5c2a5c', background:'#fdf8ff' }}>
|
||
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:8 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>Vendor Details</div>
|
||
<div style={{ display:'flex', gap:6 }}>
|
||
<span className="sk-tag" style={{ fontSize:11, borderColor:'#5c2a5c', color:'#5c2a5c', background:'#f8f0ff' }}>VND-0042</span>
|
||
<span className="sk-tag" style={{ fontSize:11, borderColor:'#5c2a5c', color:'#5c2a5c', background:'#f8f0ff' }}>NEW</span>
|
||
</div>
|
||
</div>
|
||
<div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:10, fontSize:13, marginBottom:10 }}>
|
||
{[['Vendor','Sea-Tech Marine Supplies'],['Contact','Mr. A. Tan'],['Country','Singapore']].map(([k,v]) => (
|
||
<div key={k}>
|
||
<span className="wf-label">{k}</span>
|
||
<div style={{ fontWeight:600, marginTop:2 }}>{v}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ display:'flex', gap:8, alignItems:'center' }}>
|
||
<div className="sk-btn sm success">Vendor Verified ✓</div>
|
||
<span style={{ color:'#aaa', fontSize:12 }}>— or —</span>
|
||
<div className="sk-btn sm" style={{ borderColor:'#c8971a', color:'#c8971a' }}>Request Vendor ID from Submitter</div>
|
||
</div>
|
||
<div className="annotation" style={{ marginTop:6, fontSize:11 }}>Requesting sends PO back to VENDOR_ID_PENDING state, notifies submitter</div>
|
||
</div>
|
||
|
||
{/* Manager comment box — always shown */}
|
||
<div className="sk-box warn" style={{ padding:12, display:'flex', flexDirection:'column', gap:6 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>Manager Comment <span style={{ fontWeight:400, color:'#aaa' }}>(sent back to submitter)</span></div>
|
||
<div className="sk-input" style={{ minHeight:54, color:'#bbb', background:'#fff' }}>Add a comment — required for Reject and Ask for Edits…</div>
|
||
</div>
|
||
|
||
{/* 4-action bar */}
|
||
<div style={{ display:'flex', flexDirection:'column', gap:8 }}>
|
||
<div style={{ fontSize:12, color:'#888', fontWeight:700 }}>Manager Decision</div>
|
||
<div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr 1fr', gap:8 }}>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<div className="sk-btn danger" style={{ textAlign:'center', fontSize:13 }}>Reject</div>
|
||
<div style={{ fontSize:10, color:'#aaa', textAlign:'center' }}>Closes PO. Notifies submitter with comment.</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<div className="sk-btn" style={{ textAlign:'center', fontSize:13, borderColor:'#c8971a', color:'#c8971a' }}>Ask for Edits</div>
|
||
<div style={{ fontSize:10, color:'#aaa', textAlign:'center' }}>Returns to submitter. Comment required.</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<div className="sk-btn success" style={{ textAlign:'center', fontSize:13 }}>Approve ✓</div>
|
||
<div style={{ fontSize:10, color:'#aaa', textAlign:'center' }}>Sends to Accounts for payment.</div>
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<div className="sk-btn" style={{ textAlign:'center', fontSize:13, borderColor:'#2a8a50', color:'#2a8a50' }}>Approve + Note</div>
|
||
<div style={{ fontSize:10, color:'#aaa', textAlign:'center' }}>Approved with comment to Accounts.</div>
|
||
</div>
|
||
</div>
|
||
<div className="annotation">all decisions notify the submitter instantly ↑</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Side panel */}
|
||
<div style={{ flex:1, display:'flex', flexDirection:'column', gap:12 }}>
|
||
<div className="sk-box" style={{ padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:8 }}>Attached Document</div>
|
||
<div style={{ height:160, background:'repeating-linear-gradient(135deg,#e8e4dc 0px,#e8e4dc 10px,#f5f0e8 10px,#f5f0e8 20px)', display:'flex', alignItems:'center', justifyContent:'center', border:'1.5px dashed #aaa', borderRadius:2 }}>
|
||
<div style={{ textAlign:'center' }}>
|
||
<div style={{ fontSize:28 }}>📄</div>
|
||
<div style={{ fontSize:11, color:'#888' }}>Supplier_Quote_Apr2026.pdf</div>
|
||
<div className="sk-btn sm" style={{ marginTop:6, display:'inline-block' }}>Open PDF</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Status trail */}
|
||
<div className="sk-box" style={{ padding:12, display:'flex', flexDirection:'column', gap:8 }}>
|
||
<div style={{ fontSize:13, fontWeight:700 }}>PO Timeline</div>
|
||
{[
|
||
{ label:'Created', sub:'J. Santos · 28 Apr 09:14', done:true },
|
||
{ label:'Submitted', sub:'J. Santos · 28 Apr 09:22', done:true },
|
||
{ label:'Manager Review', sub:'Awaiting decision', done:false, active:true },
|
||
{ label:'Accounts Payment', sub:'—', done:false },
|
||
{ label:'Complete', sub:'—', done:false },
|
||
].map((s,i) => (
|
||
<div key={i} style={{ display:'flex', gap:8, alignItems:'flex-start' }}>
|
||
<div style={{ width:16, height:16, borderRadius:'50%', border:'2px solid', borderColor: s.done ? '#2a8a50' : s.active ? '#c8971a' : '#ccc', background: s.done ? '#2a8a50' : '#fff', flexShrink:0, marginTop:2 }}/>
|
||
<div>
|
||
<div style={{ fontSize:12, fontWeight: s.active ? 700:400, color: s.done||s.active ? '#222':'#aaa' }}>{s.label}</div>
|
||
<div style={{ fontSize:10, color:'#aaa' }}>{s.sub}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 6: ACCOUNTS – PAYMENT QUEUE ─────────────────── */
|
||
function AccountsPaymentScreen() {
|
||
const orders = [
|
||
{ id:'PO-2040', item:'Fuel Pump Assembly', code:'ENG-5510', vessel:'MV Kestrel', account:'ACC-1140', amount:'$12,800', approved:'Mgr. Chen · 27 Apr', note:'Urgent — vessel departs 2 May', docs:true },
|
||
{ id:'PO-2038', item:'Safety Harnesses ×12', code:'MAN-2201', vessel:'MV Falcon', account:'ACC-3310', amount:'$1,800', approved:'Mgr. Chen · 27 Apr', note:'', docs:true },
|
||
{ id:'PO-2035', item:'Navigation Charts 2026', code:'NAV-0055', vessel:'MV Osprey', account:'ACC-2290', amount:'$420', approved:'Mgr. Chen · 26 Apr', note:'', docs:false },
|
||
{ id:'PO-2031', item:'GPS Transponder', code:'NAV-0212', vessel:'MV Kestrel', account:'ACC-1140', amount:'$5,800', approved:'Mgr. Chen · 25 Apr', note:'Approved with note: verify supplier warranty', docs:true },
|
||
];
|
||
return (
|
||
<Shell role="accounts" activeNav="Payment Queue">
|
||
<div style={{ display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:10 }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>Payment Queue</div>
|
||
<span className="sk-tag blue">8 ready</span>
|
||
<span style={{ fontSize:12, color:'#888', marginLeft:4 }}>Manager-approved POs awaiting payment processing</span>
|
||
</div>
|
||
|
||
<div style={{ display:'flex', gap:8, alignItems:'center' }}>
|
||
<div style={{ flex:1, display:'flex', alignItems:'center', gap:8, border:'2px solid #aaa', borderRadius:3, padding:'5px 10px', background:'#fff' }}>
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="#888" strokeWidth="1.5"/><line x1="9" y1="9" x2="13" y2="13" stroke="#888" strokeWidth="1.5"/></svg>
|
||
<span style={{ fontFamily:'Caveat', fontSize:14, color:'#aaa' }}>Search by item, vessel, account…</span>
|
||
</div>
|
||
<div className="sk-btn sm">Vessel ▾</div>
|
||
<div className="sk-btn sm">Account ▾</div>
|
||
</div>
|
||
|
||
<div style={{ display:'flex', flexDirection:'column', gap:10 }}>
|
||
{orders.map((o) => (
|
||
<div key={o.id} className="sk-box" style={{ padding:12, display:'flex', gap:12, alignItems:'flex-start' }}>
|
||
<div style={{ flex:1, display:'flex', flexDirection:'column', gap:4 }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||
<span style={{ fontWeight:700, color:'#3a7cbf' }}>{o.id}</span>
|
||
<span style={{ fontWeight:600, fontSize:15 }}>{o.item}</span>
|
||
<span className="sk-tag gray" style={{ fontSize:11 }}>{o.code}</span>
|
||
{o.docs && <span className="sk-tag blue" style={{ fontSize:11 }}>📄 PDF</span>}
|
||
</div>
|
||
<div style={{ display:'flex', gap:16, fontSize:13, color:'#666' }}>
|
||
<span>⚓ {o.vessel}</span>
|
||
<span>🗂 {o.account}</span>
|
||
<span style={{ color:'#2a8a50' }}>✓ {o.approved}</span>
|
||
</div>
|
||
{o.note && (
|
||
<div style={{ fontSize:12, color:'#c8971a', background:'#fdf3d0', border:'1px solid #c8971a', borderRadius:2, padding:'2px 8px', display:'inline-block', marginTop:2 }}>
|
||
💬 Manager note: {o.note}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'flex-end', gap:8, flexShrink:0 }}>
|
||
<span style={{ fontWeight:700, fontSize:16 }}>{o.amount}</span>
|
||
<div style={{ display:'flex', gap:6 }}>
|
||
<div className="sk-btn sm">View</div>
|
||
<div className="sk-btn sm success">Mark Paid ✓</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="annotation">Accounts only sees manager-approved POs — no approval decisions here ↑</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 7: MY ORDERS – SUBMITTER STATUS TRACKER ─────── */
|
||
function MyOrdersScreen({ role }) {
|
||
const orders = [
|
||
{ id:'PO-2042', item:'Dredge Cutter Head Parts', vessel:'MV Kestrel', amount:'$38,500', status:'vendor-pending', date:'28 Apr', comment:'Please provide a Vendor ID before we can proceed with approval.' },
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', vessel:'MV Kestrel', amount:'$4,200', status:'edits', date:'27 Apr', comment:'Please attach updated supplier quote — previous one is expired.' },
|
||
{ id:'PO-2038', item:'Safety Harnesses ×12', vessel:'MV Falcon', amount:'$1,800', status:'paid-delivered', date:'25 Apr', comment:'Approved. Ensure ISO-certified supplier.' },
|
||
{ id:'PO-2031', item:'Anchor Chain Shackles', vessel:'MV Osprey', amount:'$650', status:'approved', date:'22 Apr', comment:null },
|
||
{ id:'PO-2028', item:'Fire Suppression Refill', vessel:'MV Osprey', amount:'$380', status:'rejected', date:'18 Apr', comment:'Budget exceeded for ACC-2290 this quarter. Re-submit next period.' },
|
||
];
|
||
const statusTag = (s) => {
|
||
if (s === 'mgr-review') return <span className="sk-tag orange">Mgr. Review</span>;
|
||
if (s === 'vendor-pending') return <span className="sk-tag" style={{ borderColor:'#5c2a5c', color:'#5c2a5c', background:'#f8f0ff' }}>Vendor ID Needed</span>;
|
||
if (s === 'edits') return <span className="sk-tag red">Edits Requested</span>;
|
||
if (s === 'payment') return <span className="sk-tag blue">Awaiting Payment</span>;
|
||
if (s === 'paid-delivered') return <span className="sk-tag" style={{ borderColor:'#1a6a3a', color:'#1a6a3a', background:'#e0f4ea' }}>Confirm Receipt !</span>;
|
||
if (s === 'approved') return <span className="sk-tag green">Closed ✓</span>;
|
||
if (s === 'rejected') return <span className="sk-tag red">Rejected</span>;
|
||
};
|
||
return (
|
||
<Shell role={role} activeNav="My Orders">
|
||
<div style={{ display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>My Purchase Orders</div>
|
||
|
||
{/* Status filter tabs */}
|
||
<div style={{ display:'flex', gap:0, borderBottom:'2px solid #222' }}>
|
||
{[['All','5'],['Edits Needed','1'],['In Progress','2'],['Complete','2']].map(([l,c], i) => (
|
||
<div key={l} style={{ padding:'5px 14px', borderBottom: i===0 ? '3px solid #222':'3px solid transparent', marginBottom:-2, fontFamily:'Caveat', fontSize:13, fontWeight: i===0 ? 700:400, cursor:'default' }}>
|
||
{l} <span style={{ fontSize:11, color:'#888' }}>({c})</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div style={{ display:'flex', flexDirection:'column', gap:10 }}>
|
||
{orders.map((o) => (
|
||
<div key={o.id} className="sk-box" style={{ padding:12, display:'flex', flexDirection:'column', gap:8, borderColor: o.status === 'edits' ? '#c8971a' : o.status === 'rejected' ? '#c03030' : '#222', background: o.status === 'edits' ? '#fffdf5' : o.status === 'rejected' ? '#fff8f8' : '#fff' }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||
<span style={{ fontWeight:700, color:'#3a7cbf' }}>{o.id}</span>
|
||
<span style={{ fontWeight:600 }}>{o.item}</span>
|
||
<span style={{ color:'#888', fontSize:13 }}>— {o.vessel}</span>
|
||
<span style={{ marginLeft:'auto', fontWeight:700 }}>{o.amount}</span>
|
||
{statusTag(o.status)}
|
||
</div>
|
||
|
||
{/* Progress track */}
|
||
<div style={{ display:'flex', gap:0, alignItems:'center' }}>
|
||
{['Submitted','Mgr. Review','Accounts Pmt','Paid','Closed'].map((step, i) => {
|
||
const stepIdx = { 'mgr-review':1, 'vendor-pending':1, 'edits':1, 'payment':2, 'paid-delivered':3, 'approved':4, 'rejected':1 }[o.status] ?? 0;
|
||
const done = i < stepIdx;
|
||
const active = i === stepIdx;
|
||
const isRejected = o.status === 'rejected' && i === 1;
|
||
return (
|
||
<React.Fragment key={step}>
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:2 }}>
|
||
<div style={{ width:18, height:18, borderRadius:'50%', border:'2px solid', borderColor: isRejected ? '#c03030' : done ? '#2a8a50' : active ? '#c8971a' : '#ccc', background: isRejected ? '#c03030' : done ? '#2a8a50' : '#fff', display:'flex', alignItems:'center', justifyContent:'center' }}>
|
||
{done && <span style={{ color:'#fff', fontSize:9, fontWeight:700 }}>✓</span>}
|
||
{isRejected && <span style={{ color:'#fff', fontSize:9, fontWeight:700 }}>✕</span>}
|
||
</div>
|
||
<span style={{ fontSize:9, color: done||active ? '#444':'#bbb', whiteSpace:'nowrap' }}>{step}</span>
|
||
</div>
|
||
{i < 3 && <div style={{ flex:1, height:2, background: done ? '#2a8a50' : '#e0e0e0', marginBottom:14 }}/>}
|
||
</React.Fragment>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Manager comment — shown when there's feedback */}
|
||
{o.comment && (
|
||
<div style={{ display:'flex', gap:8, alignItems:'flex-start', background: o.status === 'edits' ? '#fdf3d0' : o.status === 'rejected' ? '#fde0e0' : '#d8f0e0', border:`1.5px solid ${o.status === 'edits' ? '#c8971a' : o.status === 'rejected' ? '#c03030' : '#2a8a50'}`, borderRadius:3, padding:'8px 10px' }}>
|
||
<span style={{ fontSize:13, fontWeight:700, flexShrink:0 }}>Manager:</span>
|
||
<span style={{ fontSize:13, color:'#444' }}>{o.comment}</span>
|
||
</div>
|
||
)}
|
||
|
||
<div style={{ display:'flex', gap:8, justifyContent:'flex-end' }}>
|
||
<div className="sk-btn sm">View Details</div>
|
||
{o.status === 'edits' && <div className="sk-btn sm accent">Edit & Resubmit →</div>}
|
||
{o.status === 'vendor-pending' && <div className="sk-btn sm" style={{ borderColor:'#5c2a5c', color:'#5c2a5c' }}>Provide Vendor ID →</div>}
|
||
{o.status === 'paid-delivered' && <div className="sk-btn sm success">Confirm Receipt & Close →</div>}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="annotation">submitter sees live status + manager comments at every stage ↑</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 6: MANAGER HISTORICAL VIEW ─────────────────────── */
|
||
function ManagerHistoryScreen() {
|
||
const rows = [
|
||
{ id:'PO-2042', item:'Dredge Cutter Head Parts', vessel:'MV Kestrel', account:'ACC-1140', amount:'$38,500', status:'mgr-review', submitter:'J. Santos', date:'28 Apr' },
|
||
{ id:'PO-2041', item:'Hydraulic Pump Seal Kit', vessel:'MV Kestrel', account:'ACC-1140', amount:'$4,200', status:'payment', submitter:'J. Santos', date:'27 Apr' },
|
||
{ id:'PO-2039', item:'Navigation Radar Unit', vessel:'MV Osprey', account:'ACC-2290', amount:'$21,000', status:'payment', submitter:'R. Cruz', date:'26 Apr' },
|
||
{ id:'PO-2037', item:'Crew Uniforms ×20', vessel:'MV Falcon', account:'ACC-3310', amount:'$2,100', status:'approved', submitter:'M. Reyes', date:'25 Apr' },
|
||
{ id:'PO-2033', item:'Anchor Chain Shackles', vessel:'MV Osprey', account:'ACC-2290', amount:'$650', status:'rejected', submitter:'D. Lee', date:'24 Apr' },
|
||
{ id:'PO-2031', item:'GPS Transponder', vessel:'MV Kestrel', account:'ACC-1140', amount:'$5,800', status:'approved', submitter:'J. Santos', date:'22 Apr' },
|
||
{ id:'PO-2028', item:'Engine Room Extinguisher', vessel:'MV Falcon', account:'ACC-3310', amount:'$3,200', status:'approved', submitter:'M. Reyes', date:'20 Apr' },
|
||
];
|
||
const st = (s) => {
|
||
if (s === 'mgr-review') return <span className="sk-tag orange">Mgr. Review</span>;
|
||
if (s === 'payment') return <span className="sk-tag blue">Awaiting Payment</span>;
|
||
if (s === 'approved') return <span className="sk-tag green">Paid ✓</span>;
|
||
if (s === 'rejected') return <span className="sk-tag red">Rejected</span>;
|
||
return <span className="sk-tag gray">{s}</span>;
|
||
};
|
||
return (
|
||
<Shell role="manager" activeNav="Purchase Orders">
|
||
<div style={{ display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>All Purchase Orders</div>
|
||
<div style={{ display:'flex', gap:6 }}>
|
||
<div className="sk-btn sm">Export CSV</div>
|
||
<div className="sk-btn sm">Export PDF</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Filters */}
|
||
<div style={{ display:'flex', gap:8, flexWrap:'wrap' }}>
|
||
<div style={{ display:'flex', alignItems:'center', gap:8, border:'2px solid #aaa', borderRadius:3, padding:'5px 10px', background:'#fff', flex:2 }}>
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="#888" strokeWidth="1.5"/><line x1="9" y1="9" x2="13" y2="13" stroke="#888" strokeWidth="1.5"/></svg>
|
||
<span style={{ fontFamily:'Caveat', fontSize:14, color:'#aaa' }}>Search…</span>
|
||
</div>
|
||
<div className="sk-btn sm">Status ▾</div>
|
||
<div className="sk-btn sm">Vessel ▾</div>
|
||
<div className="sk-btn sm">Account ▾</div>
|
||
<div className="sk-btn sm">Date Range ▾</div>
|
||
<div className="sk-btn sm">Submitter ▾</div>
|
||
</div>
|
||
|
||
{/* Status tabs */}
|
||
<div style={{ display:'flex', gap:0, borderBottom:'2px solid #222' }}>
|
||
{[['All','194'],['Mgr. Review','12'],['Awaiting Payment','28'],['Paid','136'],['Rejected','18']].map(([label, count], i) => (
|
||
<div key={label} style={{ padding:'6px 16px', borderBottom: i === 0 ? '3px solid #222':'3px solid transparent', marginBottom:-2, fontFamily:'Caveat', fontSize:14, fontWeight: i === 0 ? 700:400, cursor:'default' }}>
|
||
{label} <span style={{ fontSize:11, color:'#888' }}>({count})</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<table className="sk-table">
|
||
<thead>
|
||
<tr>
|
||
<th>PO #</th><th>Item</th><th>Vessel</th><th>Account</th><th>Submitter</th><th>Amount</th><th>Date</th><th>Status</th><th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{rows.map(r => (
|
||
<tr key={r.id}>
|
||
<td style={{ color:'#3a7cbf', fontWeight:600 }}>{r.id}</td>
|
||
<td>{r.item}</td>
|
||
<td>{r.vessel}</td>
|
||
<td style={{ color:'#888' }}>{r.account}</td>
|
||
<td>{r.submitter}</td>
|
||
<td style={{ fontWeight:600 }}>{r.amount}</td>
|
||
<td style={{ color:'#888' }}>{r.date}</td>
|
||
<td>{st(r.status)}</td>
|
||
<td><span style={{ fontSize:12, color:'#3a7cbf', cursor:'default' }}>View →</span></td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
|
||
<div style={{ fontSize:12, color:'#aaa', textAlign:'center' }}>Showing 7 of 194 total orders</div>
|
||
|
||
<div className="annotation">Manager can see everything — pending & historic — with full filter/export ↑</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── SCREEN 8: RECEIPT CONFIRMATION ────────────────────── */
|
||
function ReceiptConfirmScreen() {
|
||
return (
|
||
<Shell role="technical" activeNav="My Orders">
|
||
<div style={{ display:'flex', gap:16 }}>
|
||
<div style={{ flex:2, display:'flex', flexDirection:'column', gap:14 }}>
|
||
<div style={{ fontSize:13, color:'#888' }}>← My Orders / <span style={{ color:'#222' }}>PO-2038</span></div>
|
||
<div style={{ display:'flex', alignItems:'center', gap:10 }}>
|
||
<div style={{ fontSize:18, fontWeight:700 }}>Confirm Receipt — PO-2038</div>
|
||
<span className="sk-tag" style={{ borderColor:'#1a6a3a', color:'#1a6a3a', background:'#e0f4ea' }}>Paid — Awaiting Confirmation</span>
|
||
</div>
|
||
|
||
{/* PO Summary read-only */}
|
||
<div className="sk-box" style={{ padding:12, background:'#f9f8f5', display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:10 }}>
|
||
{[['Item','Safety Harnesses ×12'],['Vessel','MV Falcon'],['Account','ACC-3310'],['Vendor','SafeWork Marine Pte'],['Amount Paid','$1,800.00'],['Paid On','27 Apr 2026']].map(([k,v]) => (
|
||
<div key={k}>
|
||
<span className="wf-label">{k}</span>
|
||
<div style={{ fontWeight:600, fontSize:13, marginTop:2 }}>{v}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Manager approval note */}
|
||
<div style={{ background:'#d8f0e0', border:'1.5px solid #2a8a50', borderRadius:3, padding:'8px 12px', fontSize:13, color:'#1a5a30' }}>
|
||
✓ <strong>Manager note:</strong> Ensure ISO-certified supplier. — Mgr. Chen, 25 Apr
|
||
</div>
|
||
|
||
{/* Receipt upload */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:10 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, borderBottom:'1.5px solid #eee', paddingBottom:6 }}>Upload Receipt / Invoice *</div>
|
||
<div className="sk-box filled" style={{ padding:18, textAlign:'center', borderStyle:'dashed', display:'flex', flexDirection:'column', alignItems:'center', gap:6 }}>
|
||
<svg width="26" height="26" viewBox="0 0 28 28" fill="none"><rect x="6" y="3" width="16" height="22" rx="2" stroke="#888" strokeWidth="2"/><line x1="10" y1="10" x2="18" y2="10" stroke="#888" strokeWidth="1.5"/><line x1="10" y1="14" x2="18" y2="14" stroke="#888" strokeWidth="1.5"/><line x1="10" y1="18" x2="15" y2="18" stroke="#888" strokeWidth="1.5"/></svg>
|
||
<span style={{ fontSize:12, color:'#888' }}>Drop receipt / delivery note / invoice PDF</span>
|
||
<div className="sk-btn sm">Browse…</div>
|
||
</div>
|
||
<div style={{ display:'flex', alignItems:'center', gap:8, padding:'6px 8px', background:'#f0ede6', borderRadius:2, fontSize:12 }}>
|
||
<span>📄</span>
|
||
<span style={{ flex:1 }}>Invoice_SafeWork_Apr2026.pdf</span>
|
||
<span style={{ color:'#c03030', cursor:'default' }}>✕</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Delivery confirmation */}
|
||
<div className="sk-box" style={{ padding:14, display:'flex', flexDirection:'column', gap:10 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, borderBottom:'1.5px solid #eee', paddingBottom:6 }}>Delivery Confirmation *</div>
|
||
<label style={{ display:'flex', gap:10, alignItems:'flex-start', cursor:'pointer' }}>
|
||
<div style={{ width:18, height:18, border:'2px solid #222', borderRadius:3, background:'#222', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, marginTop:1 }}>
|
||
<span style={{ color:'#fff', fontSize:10, fontWeight:700 }}>✓</span>
|
||
</div>
|
||
<span style={{ fontSize:14, lineHeight:1.4 }}>I confirm that the goods/services for this PO have been received in satisfactory condition.</span>
|
||
</label>
|
||
<div style={{ display:'flex', flexDirection:'column', gap:4 }}>
|
||
<span className="wf-label">Delivery Notes (optional)</span>
|
||
<div className="sk-input" style={{ minHeight:48, color:'#bbb' }}>Condition, partial delivery, discrepancies…</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ display:'flex', gap:10, justifyContent:'flex-end' }}>
|
||
<div className="sk-btn">Save Draft</div>
|
||
<div className="sk-btn success">Confirm Receipt & Close PO ✓</div>
|
||
</div>
|
||
<div className="annotation">Once confirmed → PO moves to CLOSED. All parties notified by email. ↑</div>
|
||
</div>
|
||
|
||
{/* Side: full timeline */}
|
||
<div style={{ flex:1, display:'flex', flexDirection:'column', gap:12 }}>
|
||
<div className="sk-box" style={{ padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:10 }}>PO Timeline</div>
|
||
{[
|
||
{ label:'Created', sub:'J. Santos · 25 Apr 08:30', done:true },
|
||
{ label:'Submitted', sub:'J. Santos · 25 Apr 09:00', done:true },
|
||
{ label:'Manager Approved', sub:'Mgr. Chen · 25 Apr 14:22', done:true },
|
||
{ label:'Sent for Payment', sub:'Accounts · 26 Apr 10:00', done:true },
|
||
{ label:'Payment Processed', sub:'Accounts · 27 Apr 11:05', done:true },
|
||
{ label:'Receipt Confirmation', sub:'Awaiting your action ⬅', done:false, active:true },
|
||
{ label:'Closed', sub:'—', done:false },
|
||
].map((s,i) => (
|
||
<div key={i} style={{ display:'flex', gap:8, alignItems:'flex-start', marginBottom:8 }}>
|
||
<div style={{ display:'flex', flexDirection:'column', alignItems:'center' }}>
|
||
<div style={{ width:16, height:16, borderRadius:'50%', border:'2px solid', borderColor: s.done ? '#2a8a50' : s.active ? '#1a6a3a' : '#ccc', background: s.done ? '#2a8a50' : '#fff', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
|
||
{s.done && <span style={{ color:'#fff', fontSize:8, fontWeight:700 }}>✓</span>}
|
||
{s.active && <span style={{ color:'#1a6a3a', fontSize:8, fontWeight:700 }}>●</span>}
|
||
</div>
|
||
{i < 6 && <div style={{ width:2, height:14, background: s.done ? '#2a8a50':'#e0e0e0', marginTop:2 }}/>}
|
||
</div>
|
||
<div style={{ paddingTop:1 }}>
|
||
<div style={{ fontSize:12, fontWeight: s.active ? 700:400, color: s.done||s.active ? '#222':'#aaa' }}>{s.label}</div>
|
||
<div style={{ fontSize:10, color: s.active ? '#1a6a3a':'#aaa' }}>{s.sub}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="sk-box" style={{ padding:12 }}>
|
||
<div style={{ fontSize:13, fontWeight:700, marginBottom:6 }}>What happens next?</div>
|
||
<div style={{ fontSize:12, color:'#555', lineHeight:1.6 }}>
|
||
After you confirm receipt:<br/>
|
||
• PO status → <strong>CLOSED</strong><br/>
|
||
• Receipt stored on the PO record<br/>
|
||
• Manager + Accounts notified<br/>
|
||
• PO visible in Manager history
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Shell>
|
||
);
|
||
}
|
||
|
||
/* ─── MAIN APP ───────────────────────────────────────────────── */
|
||
function App() {
|
||
const { useState: us } = React;
|
||
const [role, setRole] = us('technical');
|
||
|
||
// Tweaks panel
|
||
window.addEventListener('message', (e) => {
|
||
if (e.data?.type === '__activate_edit_mode') setShowTweaks(true);
|
||
if (e.data?.type === '__deactivate_edit_mode') setShowTweaks(false);
|
||
});
|
||
const [showTweaks, setShowTweaks] = us(false);
|
||
React.useEffect(() => {
|
||
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
|
||
}, []);
|
||
|
||
return (
|
||
<>
|
||
<DesignCanvas title="Pelagia Marine Portal — Wireframes">
|
||
{/* Row 1: Entry & Dashboards */}
|
||
<DCSection id="s1" title="Authentication & Dashboards">
|
||
<DCArtboard id="a1" label="Login Screen" width={320} height={480}>
|
||
<LoginScreen/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a2" label="Dashboard — Technical" width={640} height={480}>
|
||
<DashboardScreen role="technical"/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a3" label="Dashboard — Accounts" width={640} height={480}>
|
||
<DashboardScreen role="accounts"/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a4" label="Dashboard — Manager" width={640} height={480}>
|
||
<DashboardScreen role="manager"/>
|
||
</DCArtboard>
|
||
</DCSection>
|
||
|
||
{/* Row 2: Submitting & Tracking */}
|
||
<DCSection id="s2" title="Submit & Track — Technical / Manning">
|
||
<DCArtboard id="a5" label="New PO Form (+ Vendor)" width={720} height={660}>
|
||
<NewPOScreen role="technical"/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a6" label="My Orders — Status Tracker" width={700} height={600}>
|
||
<MyOrdersScreen role="technical"/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a11" label="Receipt Confirmation & Close PO" width={720} height={640}>
|
||
<ReceiptConfirmScreen/>
|
||
</DCArtboard>
|
||
</DCSection>
|
||
|
||
{/* Row 3: Manager approval */}
|
||
<DCSection id="s3" title="Manager Approval Flow">
|
||
<DCArtboard id="a7" label="Pending Approvals — Manager" width={680} height={580}>
|
||
<ManagerApprovalsScreen/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a8" label="PO Review & Decision — Manager (+ Vendor)" width={760} height={720}>
|
||
<ManagerOrderDetailScreen/>
|
||
</DCArtboard>
|
||
</DCSection>
|
||
|
||
{/* Row 4: Accounts payment & history */}
|
||
<DCSection id="s4" title="Accounts Payment & History">
|
||
<DCArtboard id="a9" label="Payment Queue — Accounts" width={700} height={560}>
|
||
<AccountsPaymentScreen/>
|
||
</DCArtboard>
|
||
<DCArtboard id="a10" label="All POs — Manager History" width={720} height={560}>
|
||
<ManagerHistoryScreen/>
|
||
</DCArtboard>
|
||
</DCSection>
|
||
</DesignCanvas>
|
||
|
||
{/* Tweaks Panel */}
|
||
{showTweaks && (
|
||
<TweaksPanel onClose={() => {
|
||
setShowTweaks(false);
|
||
window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*');
|
||
}}>
|
||
<TweakSection label="Preview Role">
|
||
<TweakRadio
|
||
label="Role"
|
||
options={['technical','accounts','manager','admin','manning']}
|
||
value={role}
|
||
onChange={v => setRole(v)}
|
||
/>
|
||
</TweakSection>
|
||
</TweaksPanel>
|
||
)}
|
||
</>
|
||
);
|
||
}
|
||
|
||
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
|
||
</script>
|
||
|
||
<div id="root"></div>
|
||
</body>
|
||
</html>
|