Files
mek-tech-consulting/dashboard.ejs
T

817 lines
80 KiB
Plaintext
Raw Normal View History

<!DOCTYPE html><html lang="nl"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Dashboard — Mek-Tech Enterprise</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/css/style.css"><link rel="stylesheet" href="/css/neon.css"><link rel="stylesheet" href="/css/dynamic.css"><link rel="icon" href="/img/favicon.svg"><script src="https://unpkg.com/lucide@latest"></script>
<style>
:root{--sb-w:210px;--hdr-h:50px}
html,body{height:100%;margin:0;padding:0;scroll-behavior:smooth}
body{background:var(--bg);font-family:'Inter',-apple-system,sans-serif}
.app-layout{display:flex;min-height:100vh;padding-top:var(--hdr-h)}
.sidebar{position:fixed;top:var(--hdr-h);left:0;width:var(--sb-w);height:calc(100vh - var(--hdr-h));background:var(--bg-nav);border-right:1px solid var(--border);display:flex;flex-direction:column;z-index:50;overflow-y:auto;transition:all .3s ease}
.main-area{flex:1;margin-left:var(--sb-w);padding:1.5rem 2rem 3rem;max-width:1300px}
@media(max-width:768px){.sidebar{width:56px}.sidebar .sb-brand p,.sidebar .sb-sep,.sidebar .sb-item span:not(.sb-icon),.sidebar .sb-badge{display:none}.sidebar .sb-item{justify-content:center;padding:.5rem}.main-area{margin-left:56px;padding:1rem}}@media(max-width:480px){.sidebar{width:48px}.main-area{margin-left:48px;padding:.75rem}}
.sidebar .sb-brand{padding:1rem 1.1rem;border-bottom:1px solid var(--border);background:linear-gradient(135deg,rgba(255,255,255,.04),rgba(0,212,255,.08))}
.sidebar .sb-brand h2{font-size:.82rem;font-weight:700;color:var(--text);letter-spacing:-.2px;display:flex;align-items:center;gap:.4rem}
.sidebar .sb-brand h2 .brand-dot{width:7px;height:7px;border-radius:50%;background:var(--accent);animation:pulse-glow 2s ease-in-out infinite}
.sidebar .sb-brand p{font-size:.6rem;color:var(--text-dim);margin-top:1px}
.sb-nav{flex:1;padding:.4rem 0}
.sb-sep{font-size:.55rem;color:var(--text-dim);text-transform:uppercase;letter-spacing:.6px;padding:.6rem 1rem .2rem;font-weight:600}
.sb-item{display:flex;align-items:center;gap:.55rem;padding:.5rem .9rem;margin:1px .45rem;border-radius:8px;cursor:pointer;font-size:.78rem;color:var(--text-muted);transition:all .18s cubic-bezier(.4,0,.2,1);border:none;background:none;width:calc(100% - .9rem);text-align:left;font-family:inherit;position:relative;overflow:hidden;text-decoration:none}
.sb-item::after{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent);border-radius:0 4px 4px 0;transform:translateX(-3px);transition:transform .2s ease;opacity:0}
.sb-item:hover{color:var(--text);background:var(--border-light)}
.sb-item.active{color:var(--accent);background:rgba(0,212,255,.08);font-weight:600}
.sb-item.active::after{transform:translateX(0);opacity:1}
.sb-item .sb-icon{width:20px;text-align:center;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.sb-item .sb-icon svg{width:17px;height:17px;transition:transform .2s ease}
.sb-item:hover .sb-icon svg{transform:scale(1.1)}
.sb-item .sb-badge{margin-left:auto;background:var(--border-light);padding:.05rem .5rem;border-radius:999px;font-size:.58rem;font-weight:600}
.sb-item .sb-badge.warn{background:var(--red-bg);color:var(--red);animation:pulse 2s ease-in-out infinite}
.sb-item .sb-badge.ok{background:var(--green-bg);color:var(--green)}
.page-hdr{margin-bottom:1.5rem}.page-hdr h1{font-size:1.5rem;color:var(--text);font-weight:800;letter-spacing:-.4px}.page-hdr p{font-size:.78rem;color:var(--text-muted);margin-top:.1rem}
/* KPIs premium */
.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.55rem;margin-bottom:1.25rem}@media(max-width:640px){.kpi-row{grid-template-columns:repeat(2,1fr);gap:.4rem}}
.kpi-card{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:.9rem 1.1rem;display:flex;flex-direction:column;gap:.15rem;position:relative;overflow:hidden;transition:all .25s cubic-bezier(.4,0,.2,1);cursor:pointer}
.kpi-card:hover{transform:translateY(-2px);box-shadow:0 6px 25px rgba(0,0,0,.1),0 0 0 1px rgba(0,212,255,.15);border-color:rgba(0,212,255,.3)}
.kpi-card:hover .kpi-icon svg{opacity:.15;transform:scale(1.1)}
.kpi-card:active{transform:translateY(0)}
.kpi-card .kpi-num{font-size:1.7rem;font-weight:800;color:var(--text);line-height:1.1;letter-spacing:-.5px;transition:color .3s}
.kpi-card .kpi-label{font-size:.64rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;font-weight:600}
.kpi-card .kpi-sub{font-size:.62rem;color:var(--text-dim)}
.kpi-card .kpi-icon{position:absolute;right:.75rem;top:50%;transform:translateY(-50%);transition:all .25s ease}
.kpi-card .kpi-icon svg{width:2.2rem;height:2.2rem;opacity:.06;transition:all .25s ease}
.kpi-card.accent .kpi-num{color:var(--accent)}.kpi-card.green .kpi-num{color:var(--green)}.kpi-card.orange .kpi-num{color:var(--orange)}.kpi-card.purple .kpi-num{color:var(--purple)}.kpi-card.red .kpi-num{color:var(--red)}.kpi-card.cyan .kpi-num{color:var(--cyan)}
.kpi-blink{position:relative}
.kpi-blink::after{content:'';position:absolute;top:.5rem;right:.5rem;width:7px;height:7px;border-radius:50%;background:var(--green);animation:pulse-glow 2s ease-in-out infinite}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:.85rem}@media(max-width:960px){.grid-2{grid-template-columns:1fr}}
.card{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1rem 1.1rem;transition:all .2s ease;overflow-x:auto}
.card:hover{border-color:rgba(0,212,255,.15)}
.card-title{font-size:.66rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:.7rem;display:flex;justify-content:space-between;align-items:center;font-weight:600}
.card-title .ct-btn{font-size:.6rem;color:var(--accent);text-decoration:none;border:none;background:none;cursor:pointer;font-family:inherit;text-transform:none;letter-spacing:0;font-weight:500;transition:opacity .2s}
.card-title .ct-btn:hover{opacity:.8}
.tab-panel{display:none}.tab-panel.active{display:block}
.data-table{min-width:400px}
.grid-2 .card:only-child{grid-column:1/-1}
/* Client grid */
.client-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:.6rem}@media(max-width:640px){.client-grid{grid-template-columns:1fr}}
.client-card{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1.1rem;text-decoration:none;color:inherit;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}
.client-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--accent),var(--purple));opacity:0;transition:opacity .25s}
.client-card:hover{border-color:rgba(0,212,255,.3);transform:translateY(-2px);box-shadow:0 4px 20px rgba(0,0,0,.08)}
.client-card:hover::before{opacity:1}
.client-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem}
.client-card-header h4{font-size:.9rem;color:var(--text);font-weight:600}
.client-meta{font-size:.78rem;color:var(--text-muted)}.client-meta.warn{color:var(--orange)}.client-meta.small{font-size:.68rem}
.status-badge{display:inline-block;padding:.15rem .55rem;border-radius:999px;font-size:.62rem;font-weight:600;border:1px solid transparent}
.status-badge.lead{border-color:var(--accent);color:var(--accent)}
.status-badge.active{border-color:var(--green);color:var(--green)}
.status-badge.paused{border-color:var(--orange);color:var(--orange)}
.status-badge.archived{border-color:var(--text-dim);color:var(--text-dim)}
.status-badge.planned,.status-badge.sent{border-color:var(--accent);color:var(--accent)}
.status-badge.in_progress{border-color:var(--green);color:var(--green)}
.status-badge.completed,.status-badge.paid{border-color:var(--purple);color:var(--purple)}
.status-badge.on_hold{border-color:var(--orange);color:var(--orange)}
.status-badge.todo{border-color:var(--text-dim);color:var(--text-dim)}
.status-badge.done{border-color:var(--green);color:var(--green)}
.status-badge.overdue{border-color:var(--red);color:var(--red)}
.status-badge.draft{border-color:var(--text-dim);color:var(--text-dim)}
.status-badge.cancelled{border-color:var(--orange);color:var(--orange)}
.engagement-type{display:inline-block;padding:.1rem .45rem;border-radius:4px;font-size:.62rem;font-weight:600}
.engagement-type.assessment{background:var(--cyan-bg);color:var(--cyan)}
.engagement-type.architecture{background:var(--purple-bg);color:var(--purple)}
.engagement-type.implementation{background:var(--green-bg);color:var(--green)}
.engagement-type.monitoring{background:var(--orange-bg);color:var(--orange)}
.engagement-type.consulting{background:var(--pink-bg);color:var(--pink)}
.engagement-type.training{background:var(--red-bg);color:var(--red)}
.data-table{width:100%;border-collapse:collapse;border-radius:8px;overflow:hidden}
.data-table th.num,.data-table td.num{text-align:right;font-variant-numeric:tabular-nums}
@media(max-width:768px){.data-table{font-size:.65rem}.data-table th,.data-table td{padding:.3rem .4rem}.data-table td{max-width:120px!important}}
.data-table th{text-align:left;padding:.55rem .8rem;font-size:.66rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;border-bottom:1px solid var(--border);background:var(--bg);font-weight:600}
.data-table td{padding:.55rem .8rem;border-bottom:1px solid var(--border-light);font-size:.78rem}
.data-table tr{transition:background .15s}
.data-table tr:hover td{background:var(--border-light)}
.link{color:var(--accent);text-decoration:none;font-weight:500}.link:hover{text-decoration:underline}
.inline-form{display:flex;gap:.35rem;flex-wrap:wrap;align-items:flex-end;margin-bottom:.85rem}
.inline-form input,.inline-form select{padding:.38rem .55rem;font-size:.74rem;width:auto;min-width:115px}
.inline-form .btn{padding:.38rem .7rem;font-size:.72rem}
.advice-list{display:flex;flex-direction:column;gap:.4rem}
.advice-card{display:flex;align-items:flex-start;gap:.5rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:.6rem .8rem;border-left:4px solid var(--border);transition:all .2s ease}
.advice-card:hover{border-color:rgba(0,212,255,.2)}
.advice-card.critical{border-left-color:var(--red)}.advice-card.high{border-left-color:var(--orange)}.advice-card.medium{border-left-color:var(--accent)}.advice-card.low{border-left-color:var(--green)}
.advice-prio{font-size:.55rem;font-weight:700;text-transform:uppercase;padding:.08rem .38rem;border-radius:4px;white-space:nowrap}
.advice-prio.critical{background:var(--red-bg);color:var(--red)}.advice-prio.high{background:var(--orange-bg);color:var(--orange)}.advice-prio.medium{background:var(--cyan-bg);color:var(--cyan)}.advice-prio.low{background:var(--green-bg);color:var(--green)}
.advice-body{flex:1}.advice-body p{font-size:.78rem;color:var(--text);line-height:1.4}
.advice-action{font-size:.62rem;color:var(--accent);text-decoration:none;font-weight:600;white-space:nowrap;margin-left:auto;align-self:center}
.advice-action:hover{text-decoration:underline}
.advice-meta{font-size:.62rem;color:var(--text-muted);margin-top:.15rem}
.consult-hero{background:linear-gradient(135deg,rgba(0,212,255,.06),rgba(168,85,247,.06));border:1px solid rgba(0,212,255,.15);border-radius:12px;padding:1rem 1.1rem;margin-bottom:1rem;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.consult-hero h2{font-size:.95rem;font-weight:800;margin:0 0 .2rem;background:linear-gradient(135deg,var(--text),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.consult-hero p{font-size:.72rem;color:var(--text-muted);margin:0;max-width:520px;line-height:1.45}
.consult-hero .ch-btns{display:flex;gap:.4rem;flex-wrap:wrap}
.consult-hero .ch-btn{display:inline-flex;align-items:center;gap:.3rem;padding:.45rem .85rem;border-radius:8px;font-size:.7rem;font-weight:600;text-decoration:none;border:1px solid rgba(0,212,255,.35);background:rgba(0,212,255,.1);color:var(--accent);transition:all .15s}
.consult-hero .ch-btn:hover{box-shadow:0 0 20px rgba(0,212,255,.2);transform:translateY(-1px)}
.consult-hero .ch-btn.sec{background:transparent;border-color:var(--border);color:var(--text-muted)}
.consult-hero .ch-btn.sec:hover{color:var(--text);border-color:rgba(0,212,255,.25)}
.toolkit-section{margin-bottom:1rem}
.toolkit-section-hdr{display:flex;align-items:center;gap:.5rem;margin-bottom:.55rem}
.toolkit-section-hdr h3{font-size:.72rem;font-weight:700;color:var(--text);margin:0}
.toolkit-section-hdr span{font-size:.6rem;color:var(--text-muted)}
.toolkit-section-hdr .ts-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.toolkit-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:.4rem}
.tool-card{display:flex;flex-direction:column;gap:.2rem;padding:.55rem .6rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;text-decoration:none;color:inherit;transition:all .18s;min-height:72px}
.tool-card:hover{border-color:rgba(0,212,255,.35);transform:translateY(-2px);box-shadow:0 4px 16px rgba(0,212,255,.08)}
.tool-card .tc-icon{width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center;margin-bottom:.1rem}
.tool-card .tc-label{font-size:.68rem;font-weight:600;color:var(--text);line-height:1.2}
.tool-card .tc-desc{font-size:.58rem;color:var(--text-muted);line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.playbook-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(155px,1fr));gap:.45rem;margin-bottom:1rem}
.pb-card{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:.65rem .75rem;border-top:3px solid var(--pb-color,var(--accent))}
.pb-card .pb-phase{font-size:.55rem;font-weight:800;color:var(--pb-color);text-transform:uppercase;letter-spacing:.06em}
.pb-card h4{font-size:.72rem;font-weight:700;color:var(--text);margin:.2rem 0 .35rem}
.pb-card ul{list-style:none;margin:0;padding:0}
.pb-card li{font-size:.58rem;color:var(--text-muted);padding:.1rem 0;display:flex;gap:.25rem;line-height:1.3}
.pb-card li::before{content:'';color:var(--pb-color);font-weight:700}
.pb-dur{font-size:.55rem;color:var(--text-dim);margin-top:.35rem;padding-top:.3rem;border-top:1px solid var(--border-light)}
.maturity-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.4rem}
.mat-col{background:var(--bg);border:1px solid var(--border-light);border-radius:8px;padding:.55rem .65rem}
.mat-col h5{font-size:.62rem;font-weight:700;color:var(--accent);margin:0 0 .35rem;text-transform:uppercase;letter-spacing:.04em}
.mat-col span{display:block;font-size:.58rem;color:var(--text-muted);padding:.12rem 0;border-bottom:1px solid var(--border-light)}
.mat-col span:last-child{border-bottom:none}
.advice-feed-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}
.advice-feed-header h3{font-size:.75rem;font-weight:700;color:var(--text);margin:0}
.advice-empty{padding:1.5rem;text-align:center;background:var(--bg-card);border:1px dashed var(--border);border-radius:10px}
.advice-empty p{font-size:.75rem;color:var(--text-muted);margin:.35rem 0 0}
.task-list{display:flex;flex-direction:column;gap:.4rem}
.task-item{display:flex;align-items:center;gap:.5rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:.55rem .8rem;transition:all .15s ease}
.task-item:hover{border-color:rgba(0,212,255,.15)}
.task-item.done{opacity:.5}.task-item.done .task-title{text-decoration:line-through}
.task-body{flex:1;display:flex;flex-direction:column;gap:.1rem}.task-title{font-size:.82rem;color:var(--text);font-weight:500}.task-desc{font-size:.72rem;color:var(--text-muted)}.task-meta{font-size:.68rem;color:var(--accent)}
.task-prio{font-size:.55rem;font-weight:700;text-transform:uppercase;padding:.08rem .38rem;border-radius:4px}
.task-prio.critical{background:var(--red-bg);color:var(--red)}.task-prio.high{background:var(--orange-bg);color:var(--orange)}.task-prio.medium{background:var(--cyan-bg);color:var(--cyan)}.task-prio.low{background:var(--green-bg);color:var(--green)}
.task-due{font-size:.68rem;color:var(--text-muted)}.task-due.overdue{color:var(--red);font-weight:600}
.rm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:.4rem}
.rm-card{display:flex;gap:.6rem;padding:.65rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;text-decoration:none;color:inherit;transition:all .2s cubic-bezier(.4,0,.2,1)}
.rm-card:hover{border-color:rgba(0,212,255,.3);transform:translateY(-1px);box-shadow:0 4px 16px rgba(0,0,0,.06)}
.rm-vis{width:14px;display:flex;flex-direction:column;gap:1px;flex-shrink:0}
.rm-u{width:100%;height:4px;border-radius:1px;background:var(--border)}.rm-u.filled{background:var(--accent)}
.nd-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:.4rem}
.nd-card{display:flex;align-items:center;gap:.5rem;padding:.6rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;text-decoration:none;color:inherit;transition:all .2s ease}
.nd-card:hover{border-color:rgba(0,212,255,.3);box-shadow:0 2px 12px rgba(0,0,0,.05)}
.nd-icon{width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:.6rem;font-weight:700;flex-shrink:0}
.quick-actions{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:.4rem}
.qa-btn{display:flex;flex-direction:column;align-items:center;gap:.25rem;padding:.6rem .5rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);text-decoration:none;font-size:.63rem;font-weight:500;transition:all .2s ease;cursor:pointer}
.qa-btn:hover{border-color:var(--qa,var(--accent));color:var(--qa,var(--accent));background:rgba(0,212,255,.04);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.08)}
.qa-btn .qa-icon svg{width:1.2rem;height:1.2rem;transition:transform .2s ease}
.qa-btn:hover .qa-icon svg{transform:scale(1.15)}
.empty-state-s{margin:.75rem 0;text-align:center;color:var(--text-dim);font-size:.75rem}
.progress-mini{height:3px;background:var(--border-light);border-radius:2px;overflow:hidden;margin-bottom:.05rem}
.progress-mini .pf{height:100%;border-radius:2px;transition:width .8s cubic-bezier(.4,0,.2,1)}
.timeline{display:flex;flex-direction:column;gap:.4rem}
.tl-item{display:flex;gap:.5rem;padding:.4rem 0;border-bottom:1px solid var(--border-light);font-size:.7rem;transition:all .15s}
.tl-item:hover{padding-left:.25rem}
.tl-item:last-child{border-bottom:none}.tl-item .tl-dot{width:8px;height:8px;border-radius:50%;margin-top:.3rem;flex-shrink:0}.tl-item .tl-body{flex:1}.tl-item .tl-title{color:var(--text);font-weight:500}.tl-item .tl-date{color:var(--text-dim);font-size:.64rem;margin-top:.05rem}
.alert{background:var(--green-bg);border:1px solid var(--green);color:var(--green);padding:.4rem .7rem;border-radius:8px;font-size:.76rem;margin-bottom:.75rem}
.alert-error{background:var(--red-bg);border:1px solid var(--red);color:var(--red);padding:.4rem .7rem;border-radius:8px;font-size:.76rem;margin-bottom:.75rem}
.time-tools{display:flex;gap:.4rem;margin-bottom:.75rem;flex-wrap:wrap}.time-tools .tt-btn{display:flex;align-items:center;gap:.3rem;padding:.35rem .65rem;border-radius:8px;font-size:.68rem;font-weight:500;cursor:pointer;border:1px solid var(--border);background:var(--bg-card);color:var(--text-muted);text-decoration:none;transition:all .2s ease;font-family:inherit}
.time-tools .tt-btn:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 2px 10px rgba(0,212,255,.06)}.time-tools .tt-btn svg{width:13px;height:13px}
.nav-right{display:flex;align-items:center;gap:.25rem;flex-shrink:0}
.nav-icon-btn{width:34px;height:34px;border-radius:8px;border:none;background:transparent;color:var(--text-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s;position:relative;text-decoration:none}
.nav-icon-btn:hover{background:var(--border-light);color:var(--text)}
.nav-icon-btn svg{width:18px;height:18px}
.nav-icon-btn .nav-dot{position:absolute;top:5px;right:5px;width:7px;height:7px;border-radius:50%;background:var(--red);border:2px solid var(--bg-nav)}
.nav-icon-btn::after{content:attr(data-count);position:absolute;top:2px;right:2px;min-width:15px;height:15px;border-radius:8px;background:var(--red);color:#fff;font-size:.58rem;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 4px;line-height:1}
.nav-icon-btn:not([data-count])::after{display:none}
.nav-logout{color:var(--red)!important}.nav-logout:hover{background:var(--red-bg)!important}
.nav-search{display:flex;align-items:center;gap:.3rem;flex:1;max-width:320px;margin:0 1rem}
.nav-search input{padding:.3rem .6rem;font-size:.7rem;background:var(--bg);border:1px solid var(--border);border-radius:7px;color:var(--text);font-family:inherit;width:100%;transition:all .2s}
.nav-search input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px rgba(0,212,255,.08)}
.nav-search input::placeholder{color:var(--text-dim)}
@media(max-width:600px){.nav-search{display:none}}
@media(max-width:480px){.nav-brand span{display:none}.nav-icon-btn{width:30px;height:30px}}
/* Interactive dashboard */
.welcome-bar{background:linear-gradient(135deg,rgba(0,212,255,.08),rgba(168,85,247,.06));border:1px solid rgba(0,212,255,.2);border-radius:12px;padding:1rem 1.15rem;margin-bottom:1rem;display:flex;justify-content:space-between;align-items:center;gap:1rem;flex-wrap:wrap}
.welcome-bar h2{font-size:1rem;font-weight:800;margin:0;color:#fff}
.welcome-bar p{font-size:.72rem;color:rgba(255,255,255,.88);margin:.2rem 0 0}
.welcome-clock{font-size:.68rem;color:rgba(255,255,255,.75);text-align:right}
.svc-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.45rem;margin-bottom:1rem}
.svc-pill{display:flex;align-items:center;gap:.45rem;padding:.5rem .65rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;font-size:.68rem;transition:all .2s;cursor:default}
.svc-pill.online{border-color:rgba(63,185,80,.35)}
.svc-pill.offline{border-color:rgba(248,81,73,.35);opacity:.85}
.svc-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.svc-dot.on{background:var(--green);box-shadow:0 0 8px var(--green)}
.svc-dot.off{background:var(--red)}
.advice-strip{display:flex;gap:.45rem;overflow-x:auto;padding-bottom:.25rem;margin-bottom:1rem;scrollbar-width:thin}
.advice-chip{flex:0 0 auto;max-width:280px;padding:.55rem .75rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;border-left:3px solid var(--accent);text-decoration:none;color:inherit;font-size:.68rem;transition:all .15s}
.advice-chip:hover{border-color:rgba(0,212,255,.35);transform:translateY(-1px)}
.advice-chip.critical{border-left-color:var(--red)}.advice-chip.high{border-left-color:var(--orange)}
.advice-chip strong{display:block;font-size:.72rem;margin-bottom:.15rem;color:var(--text)}
.tab-panel{animation:fadeInUp .3s ease}
@keyframes countUp{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
.kpi-num.anim{animation:countUp .5s ease}
</style></head><body>
<nav class="navbar"><div class="nav-inner">
<a href="/" class="nav-brand"><img src="/img/logo.svg" class="nav-logo" alt="Mek-Tech"></a>
<div class="nav-search"><input type="text" placeholder="Zoek cliënten, engagements, taken..." onkeydown="if(event.key==='Enter')window.location='/search?q='+encodeURIComponent(this.value)"></div>
<div class="nav-right">
<% if (overdueTasks > 0) { %><a href="/?tab=tasks" class="nav-icon-btn" title="<%= overdueTasks %> taken over tijd"><i data-lucide="alert-triangle"></i><span class="nav-dot"></span></a><% } %>
<a href="/notifications" class="nav-icon-btn" title="Meldingen"><i data-lucide="bell"></i></a>
<a href="/settings" class="nav-icon-btn" title="Instellingen"><i data-lucide="settings"></i></a>
<button class="theme-toggle" id="themeToggle" title="Toggle theme" onclick="toggleTheme()"></button>
<a href="/auth/logout" class="nav-icon-btn nav-logout" title="Logout"><i data-lucide="log-out"></i></a>
</div>
</div></nav>
<div class="app-layout">
<aside class="sidebar"><div class="sb-brand"><h2><span class="brand-dot"></span> Mek-Tech Console</h2><p>Enterprise Platform</p></div><nav class="sb-nav">
<div class="sb-sep">Core</div>
<button class="sb-item active" data-tab="overview"><span class="sb-icon"><i data-lucide="layout-dashboard" style="color:var(--accent)"></i></span>Overzicht</button>
<button class="sb-item" data-tab="clients"><span class="sb-icon"><i data-lucide="users" style="color:var(--green)"></i></span>Cliënten <span class="sb-badge"><%= clients.length %></span></button>
<button class="sb-item" data-tab="engagements"><span class="sb-icon"><i data-lucide="briefcase" style="color:var(--purple)"></i></span>Engagements <span class="sb-badge"><%= stats.totalEngagements %></span></button>
<button class="sb-item" data-tab="finance"><span class="sb-icon"><i data-lucide="wallet" style="color:var(--orange)"></i></span>Financiën</button>
<div class="sb-sep">Operatie</div>
<button class="sb-item" data-tab="infra"><span class="sb-icon"><i data-lucide="server" style="color:var(--cyan)"></i></span>Infrastructuur</button>
<button class="sb-item" data-tab="tasks"><span class="sb-icon"><i data-lucide="check-circle" style="color:var(--pink)"></i></span>Taken <% if (overdueTasks > 0) { %><span class="sb-badge warn"><%= overdueTasks %></span><% } %></button>
<button class="sb-item" data-tab="advice"><span class="sb-icon"><i data-lucide="lightbulb" style="color:var(--orange)"></i></span>Advies <% if (activeAdvice.length > 0) { %><span class="sb-badge ok"><%= activeAdvice.length %></span><% } %></button>
<div class="sb-sep">Intelligence</div>
<button class="sb-item" data-tab="leadgen"><span class="sb-icon"><i data-lucide="zap" style="color:var(--orange)"></i></span>LeadGen AI</button>
<button class="sb-item" data-tab="market"><span class="sb-icon"><i data-lucide="target" style="color:var(--accent)"></i></span>Markt Intel</button>
<button class="sb-item" data-tab="calendar"><span class="sb-icon"><i data-lucide="calendar" style="color:var(--purple)"></i></span>Agenda</button>
<button class="sb-item" data-tab="data"><span class="sb-icon"><i data-lucide="shield-check" style="color:var(--green)"></i></span>Data Kwaliteit</button>
<div class="sb-sep">Tools</div>
<a href="/email" class="sb-item"><span class="sb-icon"><i data-lucide="mail" style="color:var(--accent)"></i></span>E-mail</a>
<button class="sb-item" data-tab="activity"><span class="sb-icon"><i data-lucide="clock" style="color:var(--text-muted)"></i></span>Activiteit</button>
</nav></aside>
<div class="main-area">
<!-- ===== OVERVIEW ===== -->
<div class="tab-panel active" id="panel-overview">
<div class="welcome-bar fade-in-up">
<div>
<h2>Welkom terug<% if (typeof user !== 'undefined' && user && user.username) { %>, <%= user.username %><% } %></h2>
<p>Jouw consultancy command center — live inzicht in cliënten, trajecten en platformstatus.</p>
</div>
<div class="welcome-clock" id="liveClock"></div>
</div>
<% if (activeAdvice.length > 0) { %>
<div class="advice-strip fade-in-up">
<% activeAdvice.slice(0,4).forEach(function(a) { %>
<a href="<%= a.action_url %>" class="advice-chip <%= a.priority %>">
<strong><%= a.client_name %></strong>
<%= a.advice_text.length > 80 ? a.advice_text.substring(0,80) + '…' : a.advice_text %>
</a>
<% }) %>
<a href="#" onclick="switchTab('advice');return false" class="advice-chip" style="border-left-color:var(--purple);display:flex;align-items:center">Alle adviezen →</a>
</div>
<% } %>
<div class="svc-row fade-in-up">
<% var svcList = servicesStatus || {};
Object.keys(svcList).forEach(function(key) {
var s = svcList[key];
var on = s.online !== false;
%>
<div class="svc-pill <%= on ? 'online' : 'offline' %>" title="<%= s.label %> — poort <%= s.port || '—' %>">
<span class="svc-dot <%= on ? 'on' : 'off' %>"></span>
<span><strong><%= s.label %></strong><br><span style="color:var(--text-dim);font-size:.6rem"><%= on ? 'Online' : 'Offline' %></span></span>
</div>
<% }) %>
</div>
<div class="page-hdr fade-in-up"><h1><span class="gradient-text">Enterprise Dashboard</span></h1><p>Realtime consultancy intelligence platform</p></div>
<div class="kpi-row">
<div class="kpi-card accent kpi-blink" onclick="switchTab('clients')" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="building-2"></i></div><div class="kpi-num anim" data-count="<%= stats.total %>"><%= stats.total %></div><div class="kpi-label">Cliënten</div><div class="kpi-sub"><%= stats.active %> actief · <%= stats.leads %> leads</div></div>
<div class="kpi-card green" onclick="switchTab('engagements')" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="briefcase"></i></div><div class="kpi-num anim" data-count="<%= stats.activeEngagements %>"><%= stats.activeEngagements %></div><div class="kpi-label">Actieve Engagements</div><div class="kpi-sub">van <%= stats.totalEngagements %> totaal</div></div>
<div class="kpi-card purple" onclick="switchTab('infra')" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="layers"></i></div><div class="kpi-num anim" data-count="<%= totalRackDevices + totalNetworkDevices %>"><%= totalRackDevices + totalNetworkDevices %></div><div class="kpi-label">Infra Devices</div><div class="kpi-sub"><%= totalRacks %> racks · <%= totalDiagrams %> diagrammen</div></div>
<div class="kpi-card <%= overdueTasks > 0 ? 'red' : 'green' %>" onclick="switchTab('tasks')" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="check-circle"></i></div><div class="kpi-num anim" data-count="<%= overdueTasks %>"><%= overdueTasks %></div><div class="kpi-label">Taken over tijd</div><div class="kpi-sub"><%= completedTasks %>/<%= totalTasks %> voltooid</div></div>
<div class="kpi-card cyan" onclick="window.location='/quality'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="shield-check"></i></div><div class="kpi-num anim" data-count="<%= assessmentsCount %>"><%= assessmentsCount %></div><div class="kpi-label">Assessments</div><div class="kpi-sub">Data quality engine</div></div>
</div>
<div class="grid-2">
<div class="card card-hover"><div class="card-title">Clients Status</div>
<% var sts={active:'Actief',leads:'Lead',paused:'Paused',archived:'Archived'};var clrs={active:'var(--green)',leads:'var(--accent)',paused:'var(--orange)',archived:'var(--text-dim)'};var maxC=Math.max(stats.active,stats.leads,stats.paused,stats.archived,1);
for(var k in sts){var cnt=stats[k]||0;%><div style="display:flex;justify-content:space-between;font-size:.74rem;margin:.25rem 0"><span style="color:var(--text-muted);font-weight:500"><%= sts[k] %></span><span style="color:var(--text);font-weight:600"><%= cnt %></span></div><div class="progress-mini" style="margin-bottom:.1rem"><div class="pf" style="width:<%= (cnt/maxC*100) %>%;background:<%= clrs[k] %>"></div></div><% } %>
</div>
<div class="card card-hover"><div class="card-title">Engagements per Type</div>
<% var tCol={assessment:'var(--cyan)',architecture:'var(--purple)',implementation:'var(--green)',monitoring:'var(--orange)',consulting:'var(--pink)',training:'var(--red)'};var maxE=Math.max(...engagementByType.map(function(e){return e.cnt}),1);
engagementByType.forEach(function(e){ %><div style="display:flex;justify-content:space-between;font-size:.74rem;margin:.2rem 0"><span style="color:var(--text-muted);font-weight:500"><%= e.type %></span><span style="color:var(--text);font-weight:600"><%= e.cnt %></span></div><div class="progress-mini" style="margin-bottom:.05rem"><div class="pf" style="width:<%= (e.cnt/maxE*100) %>%;background:<%= tCol[e.type]||'var(--accent)' %>"></div></div><% }) %>
</div></div>
<div class="grid-2" style="margin-top:.85rem">
<div class="card card-hover"><div class="card-title">Recent Assessments</div>
<% if(recentAssessments.length===0){ %><div class="empty-state-s">Nog geen assessments</div><% }else{ %><div class="timeline"><% recentAssessments.forEach(function(a){ %><div class="tl-item"><div class="tl-dot" style="background:var(--purple)"></div><div class="tl-body"><div class="tl-title">Assessment voor <strong><%= a.client_name %></strong></div><div class="tl-date"><%= new Date(a.created_at).toLocaleDateString('nl-NL',{hour:'2-digit',minute:'2-digit'}) %></div></div></div><% }) %></div><% } %>
</div>
<div class="card card-hover"><div class="card-title">Quick Actions</div>
<div class="quick-actions">
<a href="/clients/new" class="qa-btn" style="--qa:var(--green)"><span class="qa-icon"><i data-lucide="user-plus" style="color:var(--green)"></i></span>Cliënt</a>
<a href="/engagements/new" class="qa-btn" style="--qa:var(--purple)"><span class="qa-icon"><i data-lucide="briefcase" style="color:var(--purple)"></i></span>Engagement</a>
<a href="/time" class="qa-btn" style="--qa:var(--orange)"><span class="qa-icon"><i data-lucide="clock" style="color:var(--orange)"></i></span>Hours</a>
<a href="/invoices/new" class="qa-btn" style="--qa:var(--accent)"><span class="qa-icon"><i data-lucide="receipt" style="color:var(--accent)"></i></span>Factuur</a>
<a href="/quality" class="qa-btn" style="--qa:var(--cyan)"><span class="qa-icon"><i data-lucide="shield-check" style="color:var(--cyan)"></i></span>Data</a>
<a href="/assess" class="qa-btn" style="--qa:var(--pink)"><span class="qa-icon"><i data-lucide="clipboard-list" style="color:var(--pink)"></i></span>Assess</a>
<a href="<%= showcaseUrl %>" target="_blank" rel="noopener" class="qa-btn" style="--qa:var(--accent)"><span class="qa-icon"><i data-lucide="external-link" style="color:var(--accent)"></i></span>Live Labs</a>
</div></div></div></div>
<!-- ===== CLIENTS ===== -->
<div class="tab-panel" id="panel-clients"><div class="page-hdr fade-in-up"><h1>Clients</h1><p><%= stats.total %> total · <%= stats.active %> active</p></div>
<% if (clients.length === 0) { %><div class="card card-hover" style="text-align:center;padding:2rem"><p class="empty-state-s" style="margin-bottom:.5rem">Nog geen cliënten</p><a href="/clients/new" class="btn btn-accent btn-small" style="margin-top:.5rem">+ Eerste Cliënt Toevoegen</a></div><% } else { %><div class="client-grid">
<% clients.forEach(function(c){ %><a href="/clients/<%= c.id %>" class="client-card fade-in-up"><div class="client-card-header"><h4><%= c.name %></h4><span class="status-badge <%= c.status %>"><%= c.status %></span></div><% if(c.industry){ %><p class="client-meta">Industry: <%= c.industry %></p><% } %><p class="client-meta"><%= c.engagement_count %> engagement(s) · <%= c.active_engagements %> active</p><% if(c.last_assessment){ %><p class="client-meta small">Assessment: <%= new Date(c.last_assessment).toLocaleDateString('nl-NL') %></p><% }else{ %><p class="client-meta small warn">Geen assessment</p><% } %></a><% }) %>
</div><% } %></div>
<!-- ===== ENGAGEMENTS ===== -->
<div class="tab-panel" id="panel-engagements"><div class="page-hdr fade-in-up"><h1>Engagements</h1><p><%= stats.totalEngagements %> total · <%= stats.activeEngagements %> active</p></div>
<div class="grid-2" style="margin-bottom:.85rem">
<div class="card card-hover"><div class="card-title">By Type</div><% engagementByType.forEach(function(e){ %><div style="display:flex;justify-content:space-between;padding:.25rem 0;font-size:.74rem;border-bottom:1px solid var(--border-light)"><span style="color:var(--text-muted)"><%= e.type %></span><span style="color:var(--text);font-weight:600"><%= e.cnt %></span></div><% }) %></div>
<div class="card card-hover"><div class="card-title">By Status</div><% engagementByStatus.forEach(function(e){ %><div style="display:flex;justify-content:space-between;padding:.25rem 0;font-size:.74rem;border-bottom:1px solid var(--border-light)"><span style="color:var(--text-muted)"><%= e.status.replace('_',' ') %></span><span style="color:var(--text);font-weight:600"><%= e.cnt %></span></div><% }) %></div></div>
<div class="card"><div class="card-title">Alle Engagements</div>
<% if(allEngagements.length===0){ %><div class="empty-state-s">Nog geen engagements</div><% }else{ %>
<table class="data-table"><thead><tr><th>Titel</th><th>Cliënt</th><th>Type</th><th>Status</th><th></th></tr></thead><tbody><% allEngagements.forEach(function(e){ %><tr><td><a href="/engagements/<%= e.id %>" class="link"><%= e.title %></a></td><td><%= e.client_name %></td><td><span class="engagement-type <%= e.type %>"><%= e.type %></span></td><td><span class="status-badge <%= e.status %>"><%= e.status %></span></td><td><a href="/engagements/<%= e.id %>" class="btn-small">Details</a></td></tr><% }) %></tbody></table><% } %>
</div></div>
<!-- ===== FINANCE ===== -->
<div class="tab-panel" id="panel-finance"><div class="page-hdr fade-in-up"><h1>Finance</h1><p>Urenregistratie en facturatie</p></div>
<div class="kpi-row"><div class="kpi-card fade-in-up"><div class="kpi-num"><%= totalHours.toFixed(1) %></div><div class="kpi-label">Total hours</div><div class="kpi-sub"><%= billableHours.toFixed(1) %> billable</div></div><div class="kpi-card green fade-in-up"><div class="kpi-num">€<%= totalInvoiced.toFixed(0) %></div><div class="kpi-label">Invoiced</div><div class="kpi-sub">€<%= totalPaid.toFixed(0) %> paid</div></div><div class="kpi-card orange fade-in-up"><div class="kpi-num">€<%= totalOutstanding.toFixed(0) %></div><div class="kpi-label">Outstanding</div></div></div>
<div class="time-tools"><a href="/time" class="tt-btn"><i data-lucide="plus"></i> Uren registreren</a><a href="/invoices/new" class="tt-btn"><i data-lucide="plus"></i> Factuur maken</a><a href="/finance" class="tt-btn"><i data-lucide="bar-chart-2"></i> Volledig overzicht</a></div>
<div class="card card-hover" style="margin-bottom:.85rem"><div class="card-title">Recente urenregistraties</div>
<% if(timeEntries.length===0){ %><div class="empty-state-s">Nog geen uren geregistreerd</div><% }else{ %><table class="data-table"><thead><tr><th>Datum</th><th class="num">Uren</th><th>Cliënt</th><th>Engagement</th><th>Omschrijving</th></tr></thead><tbody><% timeEntries.forEach(function(t){ %><tr><td style="white-space:nowrap"><%= t.date %></td><td class="num"><strong><%= t.hours %></strong></td><td><%= t.client_name||'-' %></td><td style="max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"><%= t.engagement_title||'-' %></td><td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted)"><%= t.description||'' %></td></tr><% }) %></tbody></table><% } %></div>
<div class="card card-hover"><div class="card-title">Recente facturen</div>
<% if(invoices.length===0){ %><div class="empty-state-s">Nog geen facturen</div><% }else{ %><table class="data-table"><thead><tr><th>Factuurnr</th><th>Cliënt</th><th class="num">Bedrag</th><th>Status</th><th></th></tr></thead><tbody><% invoices.forEach(function(i){ %><tr><td><a href="/invoices/<%= i.id %>" class="link"><%= i.number %></a></td><td><%= i.client_name %></td><td class="num">€<%= (i.total||0).toFixed(2) %></td><td><span class="status-badge <%= i.status %>"><%= i.status %></span></td><td><a href="/invoices/<%= i.id %>" class="btn-small">Open</a></td></tr><% }) %></tbody></table><% } %></div></div>
<!-- ===== INFRA ===== -->
<div class="tab-panel" id="panel-infra"><div class="page-hdr fade-in-up"><h1>Infrastructure</h1><p>Racks, netwerkapparatuur, verbindingen & architectuurdiagrammen</p></div>
<div class="kpi-row"><div class="kpi-card cyan" onclick="window.location='/infrastructure?tab=racks'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="server"></i></div><div class="kpi-num"><%= totalRacks %></div><div class="kpi-label">Server Racks</div><div class="kpi-sub"><%= totalRackDevices %> devices</div></div><div class="kpi-card purple" onclick="window.location='/infrastructure?tab=network'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="wifi"></i></div><div class="kpi-num"><%= totalNetworkDevices %></div><div class="kpi-label">Netwerk Devices</div><div class="kpi-sub">Switches, routers & firewalls</div></div><div class="kpi-card green" onclick="window.location='/infrastructure?tab=architecture'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="layers"></i></div><div class="kpi-num"><%= totalDiagrams %></div><div class="kpi-label">Diagrammen</div><div class="kpi-sub">Data, netwerk & systeem</div></div></div>
<div class="time-tools"><a href="/infrastructure" class="tt-btn"><i data-lucide="layout-dashboard"></i> Volledig Overzicht</a><a href="/racks" class="tt-btn" style="--qa:var(--cyan)"><i data-lucide="server"></i> Racks Beheren</a><a href="/networking" class="tt-btn" style="--qa:var(--purple)"><i data-lucide="wifi"></i> Netwerk</a><a href="/architecture" class="tt-btn" style="--qa:var(--green)"><i data-lucide="layers"></i> Architectuur</a></div>
<div class="grid-2">
<div class="card card-hover"><div class="card-title">Server Racks <% if(racks.length>0){ %><a href="/infrastructure?tab=racks" class="ct-btn">Alle</a><% } %></div>
<% if(racks.length===0){ %><div class="empty-state-s">Nog geen racks</div><% }else{ %><div class="rm-grid"><% racks.forEach(function(r){ %><a href="/racks/<%= r.id %>" class="rm-card"><div class="rm-vis"><% for(var i=0;i<Math.min(r.total_units||42,12);i++){%><div class="rm-u <%= i < (r.device_count||0) ? 'filled' : '' %>"></div><% } %></div><div><h4 style="font-size:.78rem;color:var(--text)"><%= r.name %></h4><p style="font-size:.66rem;color:var(--text-muted)"><%= r.client_name %> · <%= r.device_count||0 %>/<%= r.total_units||42 %>U</p></div></a><% }) %></div><% } %></div>
<div class="card card-hover"><div class="card-title">Netwerk Devices <% if(netDevices.length>0){ %><a href="/infrastructure?tab=network" class="ct-btn">Alle</a><% } %></div>
<% if(netDevices.length===0){ %><div class="empty-state-s">Nog geen netwerk apparaten</div><% }else{ %><div class="nd-grid"><% netDevices.forEach(function(d){ %><a href="/networking/devices/<%= d.id %>" class="nd-card"><div class="nd-icon" style="background:<%= d.device_type==='switch'?'rgba(188,140,255,.15)':d.device_type==='router'?'rgba(0,212,255,.15)':d.device_type==='firewall'?'rgba(248,81,73,.15)':'rgba(63,185,80,.15)' %>;color:<%= d.device_type==='switch'?'#bc8cff':d.device_type==='router'?'#58a6ff':d.device_type==='firewall'?'#f85149':'#3fb950' %>"><%= d.device_type.charAt(0).toUpperCase() %></div><div style="font-size:.7rem"><strong><%= d.name %></strong><br><span style="color:var(--text-muted);font-size:.63rem"><%= d.model||'' %> · <%= d.client_name %></span></div></a><% }) %></div><% } %></div>
</div>
<div class="card card-hover" style="margin-top:.85rem"><div class="card-title">Architectuur Diagrammen <% if(diagrams.length>0){ %><a href="/infrastructure?tab=architecture" class="ct-btn">Alle</a><% } %></div>
<% if(diagrams.length===0){ %><div class="empty-state-s">Nog geen diagrammen</div><% }else{ %><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:.5rem"><% diagrams.forEach(function(d){ %><a href="/architecture/<%= d.id %>" style="display:flex;flex-direction:column;gap:.15rem;padding:.55rem;background:var(--bg);border:1px solid var(--border-light);border-radius:8px;text-decoration:none;color:inherit;transition:all .15s"><span style="font-size:.6rem;padding:.05rem .35rem;border-radius:3px;background:<%= d.diagram_type==='data_architecture'?'rgba(0,212,255,.1)':'rgba(188,140,255,.1)' %>;color:<%= d.diagram_type==='data_architecture'?'#58a6ff':'#bc8cff' %>;width:fit-content"><%= d.diagram_type.replace(/_/g,' ') %></span><span style="font-size:.75rem;color:var(--text);font-weight:500"><%= d.name %></span><span style="font-size:.64rem;color:var(--text-dim)"><%= d.client_name %></span></a><% }) %></div><% } %></div></div>
<!-- ===== TASKS ===== -->
<div class="tab-panel" id="panel-tasks"><div class="page-hdr fade-in-up"><h1>Tasks</h1><p><%= completedTasks %>/<%= totalTasks %> completed <% if(overdueTasks>0){ %><span style="color:var(--red);font-weight:600"><%= overdueTasks %> overdue</span><% } %></p></div>
<div class="kpi-row"><div class="kpi-card green"><div class="kpi-num"><%= completedTasks %></div><div class="kpi-label">Voltooid</div></div><div class="kpi-card accent"><div class="kpi-num"><%= totalTasks-completedTasks %></div><div class="kpi-label">Open</div></div><div class="kpi-card red"><div class="kpi-num"><%= overdueTasks %></div><div class="kpi-label">Over tijd</div></div></div>
<% if(allTasks.length===0){ %><div class="empty-state-s">Nog geen taken</div><% }else{ %><div class="task-list"><% allTasks.forEach(function(t){ var io=t.status!=='done'&&t.status!=='cancelled'&&t.due_date&&new Date(t.due_date)<new Date(); %><div class="task-item <%= t.status==='done'?'done':'' %> fade-in-up"><span class="task-prio <%= t.priority %>"><%= t.priority %></span><div class="task-body"><span class="task-title"><a href="/engagements/<%= t.eid %>" class="link" style="color:inherit;text-decoration:none"><%= t.title %></a></span><span class="task-desc"><%= t.client_name %> · <%= t.engagement_title %></span></div><span class="status-badge <%= t.status %>"><%= t.status.replace('_',' ') %></span><% if(t.due_date){ %><span class="task-due <%= io?'overdue':'' %>"><%= new Date(t.due_date).toLocaleDateString('nl-NL') %></span><% } %></div><% }) %></div><% } %></div>
<!-- ===== ADVICE ===== -->
<div class="tab-panel" id="panel-advice">
<div class="page-hdr fade-in-up"><h1>Advice & Consulting</h1><p>Jouw complete consultancy werkplaats — van discovery tot delivery</p></div>
<div class="consult-hero fade-in-up">
<div>
<h2>Mek-Tech Consultancy Hub</h2>
<p>Assessments, architectuur, data quality, AI readiness en commerciële follow-up — alles op één plek voor jouw FDE- en enterprise consultancy.</p>
</div>
<div class="ch-btns">
<a href="/consulting" class="ch-btn"><i data-lucide="brain" style="width:14px;height:14px"></i> Start Assessment</a>
<a href="/clients/new" class="ch-btn sec"><i data-lucide="user-plus" style="width:14px;height:14px"></i> Nieuwe Cliënt</a>
</div>
</div>
<div class="kpi-row fade-in-up">
<div class="kpi-card cyan" onclick="window.location='/consulting'" style="cursor:pointer">
<div class="kpi-icon"><i data-lucide="clipboard-check"></i></div>
<div class="kpi-num"><%= consultingStats.assessments %></div>
<div class="kpi-label">Assessments</div>
<div class="kpi-sub"><%= consultingStats.clientsNeedingAssessment %> wachten op intake</div>
</div>
<div class="kpi-card purple" onclick="window.location='/engagements'" style="cursor:pointer">
<div class="kpi-icon"><i data-lucide="briefcase"></i></div>
<div class="kpi-num"><%= consultingStats.activeConsulting %></div>
<div class="kpi-label">Lopende Trajecten</div>
<div class="kpi-sub">Consulting & architectuur</div>
</div>
<div class="kpi-card green" onclick="window.location='/architecture'" style="cursor:pointer">
<div class="kpi-icon"><i data-lucide="layers"></i></div>
<div class="kpi-num"><%= totalDiagrams %></div>
<div class="kpi-label">Designs</div>
<div class="kpi-sub">Architectuurdiagrammen</div>
</div>
<div class="kpi-card orange" onclick="document.getElementById('adviceFeed').scrollIntoView({behavior:'smooth'})" style="cursor:pointer">
<div class="kpi-icon"><i data-lucide="lightbulb"></i></div>
<div class="kpi-num"><%= consultingStats.openAdvice %></div>
<div class="kpi-label">Open Adviezen</div>
<div class="kpi-sub">Actie-items per cliënt</div>
</div>
</div>
<% toolkit.forEach(function(section) { %>
<div class="toolkit-section fade-in-up">
<div class="toolkit-section-hdr">
<span class="ts-dot" style="background:<%= section.color %>;box-shadow:0 0 8px <%= section.color %>55"></span>
<h3><%= section.title %></h3>
<span>— <%= section.desc %></span>
</div>
<div class="toolkit-grid">
<% section.tools.forEach(function(tool) { %>
<a href="<%= tool.href %>" class="tool-card">
<div class="tc-icon" style="background:<%= section.color %>18;color:<%= section.color %>">
<i data-lucide="<%= tool.icon %>" style="width:14px;height:14px"></i>
</div>
<span class="tc-label"><%= tool.label %></span>
<span class="tc-desc"><%= tool.desc %></span>
</a>
<% }) %>
</div>
</div>
<% }) %>
<div class="grid-2" style="margin-bottom:1rem">
<div class="card card-hover fade-in-up">
<div class="card-title">Delivery Playbook</div>
<p style="font-size:.65rem;color:var(--text-muted);margin:-.25rem 0 .55rem">Standaard consultancy traject in 5 fasen</p>
<div class="playbook-row">
<% playbooks.forEach(function(pb) { %>
<div class="pb-card" style="--pb-color:<%= pb.color %>">
<div class="pb-phase">Fase <%= pb.phase %></div>
<h4><%= pb.title %></h4>
<ul><% pb.steps.forEach(function(s) { %><li><%= s %></li><% }) %></ul>
<div class="pb-dur"><i data-lucide="clock" style="width:10px;height:10px;display:inline;vertical-align:-1px"></i> <%= pb.duration %></div>
</div>
<% }) %>
</div>
</div>
<div class="card card-hover fade-in-up">
<div class="card-title">Maturity Model</div>
<p style="font-size:.65rem;color:var(--text-muted);margin:-.25rem 0 .55rem">Consultancy dimensies per engagement</p>
<div class="maturity-grid">
<% maturityLayers.forEach(function(layer) { %>
<div class="mat-col">
<h5><%= layer.name %></h5>
<% layer.items.forEach(function(item) { %><span><%= item %></span><% }) %>
</div>
<% }) %>
</div>
</div>
</div>
<div class="grid-2">
<div class="card card-hover fade-in-up" id="adviceFeed">
<div class="advice-feed-header">
<h3><i data-lucide="sparkles" style="width:14px;height:14px;color:var(--accent);vertical-align:-2px"></i> Slim Advies</h3>
<span style="font-size:.62rem;color:var(--text-muted)">Automatisch gegenereerd uit CRM-data</span>
</div>
<% if(activeAdvice.length===0){ %>
<div class="advice-empty">
<i data-lucide="check-circle-2" style="width:32px;height:32px;color:var(--green);opacity:.7"></i>
<p>Geen open actie-items — voeg cliënten toe om gepersonaliseerd advies te krijgen.</p>
<a href="/clients/new" class="ch-btn" style="display:inline-flex;margin-top:.75rem;font-size:.65rem">Eerste cliënt toevoegen</a>
</div>
<% }else{ %>
<div class="advice-list">
<% activeAdvice.forEach(function(a) { %>
<div class="advice-card <%= a.priority %> fade-in-up">
<div class="advice-prio <%= a.priority %>"><%= a.priority %></div>
<div class="advice-body">
<p><%= a.advice_text %></p>
<div class="advice-meta"><%= a.client_name %> · <%= a.advice_type.replace(/_/g,' ') %></div>
</div>
<a href="<%= a.action_url %>" class="advice-action">Actie →</a>
</div>
<% }) %>
</div>
<% } %>
</div>
<div class="card card-hover fade-in-up">
<div class="card-title">Recente Assessments <% if(recentAssessments.length>0){ %><a href="/consulting" class="ct-btn">Alle</a><% } %></div>
<% if(recentAssessments.length===0){ %>
<div class="empty-state-s">Nog geen assessments — start via Enterprise Framework</div>
<% }else{ %>
<div class="timeline">
<% recentAssessments.forEach(function(a) { %>
<div class="tl-item">
<div class="tl-dot" style="background:var(--purple)"></div>
<div class="tl-body">
<div class="tl-title">Assessment voor <strong><%= a.client_name %></strong></div>
<div class="tl-date"><%= new Date(a.created_at).toLocaleString('nl-NL') %></div>
<a href="/consulting/report/<%= a.id %>" class="link" style="font-size:.65rem;margin-top:.2rem;display:inline-block">Rapport bekijken →</a>
</div>
</div>
<% }) %>
</div>
<% } %>
<div class="props-divider" style="height:1px;background:var(--border);margin:.75rem 0"></div>
<div class="quick-actions" style="grid-template-columns:1fr 1fr">
<a href="/consulting" class="qa-btn" style="padding:.65rem"><span class="qa-icon"><i data-lucide="brain" style="color:var(--purple)"></i></span>Enterprise Framework</a>
<a href="/assess" class="qa-btn" style="padding:.65rem"><span class="qa-icon"><i data-lucide="clipboard-list" style="color:var(--cyan)"></i></span>Quick Assessment</a>
<a href="/architecture" class="qa-btn" style="padding:.65rem"><span class="qa-icon"><i data-lucide="layers" style="color:var(--green)"></i></span>Architectuur</a>
<a href="/quality" class="qa-btn" style="padding:.65rem"><span class="qa-icon"><i data-lucide="shield-check" style="color:var(--accent)"></i></span>Data Quality</a>
</div>
</div>
</div>
</div>
<!-- ===== LEADGEN AI ===== -->
<div class="tab-panel" id="panel-leadgen"><div class="page-hdr fade-in-up"><h1><span class="gradient-text">LeadGen AI</span></h1><p>AI-driven Lead Generation & Tech Scoping — analyseer bedrijfswebsites op technische signalen</p></div>
<div class="grid-2">
<div class="card card-hover"><div class="card-title">URL Analyseren</div>
<div style="display:flex;gap:.4rem;flex-wrap:wrap;margin-bottom:.5rem">
<input type="url" id="leadgenUrl" placeholder="https://bedrijf.nl/over-ons" style="flex:1;min-width:200px;padding:.45rem .6rem;background:var(--bg);border:1px solid var(--border);border-radius:7px;color:var(--text);font-size:.78rem;font-family:inherit">
<button onclick="analyzeLeadgen()" class="btn btn-accent" style="display:inline-flex;align-items:center;gap:.3rem;padding:.4rem .8rem;font-size:.72rem;border-radius:7px;font-weight:500"><i data-lucide="search" style="width:14px;height:14px"></i> Analyseer</button>
</div>
<button onclick="batchLeadgen()" class="btn btn-secondary" style="width:100%;display:flex;align-items:center;justify-content:center;gap:.3rem;padding:.35rem;font-size:.7rem;border-radius:7px"><i data-lucide="layers" style="width:13px;height:13px"></i> Batch URLs (max 10)</button>
</div>
<div class="card card-hover"><div class="card-title">API Configuratie <button onclick="toggleLeadgenConfig()" id="cfgBtn" style="background:var(--border-light);border:none;border-radius:6px;padding:.2rem .6rem;cursor:pointer;color:var(--text-muted);font-size:.68rem;font-family:inherit;font-weight:500;transition:all .15s">⚙ Instellingen</button></div>
<div id="leadgenConfig" style="display:none">
<div style="margin-bottom:.5rem"><label style="font-size:.6rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:.3px">API Key</label><input type="password" id="lgApiKey" placeholder="sk-or-v1-..." style="width:100%;padding:.35rem .5rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.72rem;font-family:inherit"></div>
<div style="margin-bottom:.5rem"><label style="font-size:.6rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:.3px">Base URL</label><input type="text" id="lgBaseUrl" placeholder="https://openrouter.ai/api/v1" style="width:100%;padding:.35rem .5rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.72rem;font-family:inherit"></div>
<div style="margin-bottom:.5rem"><label style="font-size:.6rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:.3px">Model <button onclick="loadModels()" class="ct-btn" style="font-size:.55rem;margin-left:.3rem">🔄 Laad gratis modellen</button></label><select id="lgModel" style="width:100%;padding:.35rem .5rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.72rem;font-family:inherit"></select></div>
<div style="display:flex;gap:.4rem"><button onclick="saveLeadgenConfig()" class="btn btn-accent btn-small" style="padding:.3rem .6rem;font-size:.65rem">Opslaan</button><span id="lgStatus" style="font-size:.62rem;color:var(--green);padding:.3rem 0;display:none">✓ Opgeslagen</span></div>
</div></div></div>
<div id="leadgenResults" style="display:none"><div class="page-hdr" style="margin-bottom:.5rem"><h3 style="font-size:.85rem;color:var(--text)">Resultaten <span id="leadgenTiming" style="font-size:.65rem;color:var(--text-dim);font-weight:400"></span></h3></div><div id="leadgenCards"></div></div>
<div id="leadgenLoading" style="display:none;text-align:center;padding:2rem"><div class="shimmer" style="width:60%;margin:0 auto .5rem"></div><div class="shimmer" style="width:40%;margin:0 auto .5rem"></div><div class="shimmer" style="width:50%;margin:0 auto"></div><p style="font-size:.7rem;color:var(--text-dim);margin-top:.75rem">AI analyse bezig...</p></div>
<div id="leadgenEmpty"><div class="empty-state-s" style="padding:2rem"><i data-lucide="zap" style="width:36px;height:36px;color:var(--text-dim);margin-bottom:.5rem"></i><p>Plak een bedrijfs-URL en klik Analyseren. De AI scant de pagina op:</p><div style="display:flex;gap:.5rem;flex-wrap:wrap;justify-content:center;margin-top:.5rem"><span style="font-size:.65rem;padding:.2rem .5rem;background:var(--cyan-bg);color:var(--cyan);border-radius:4px">Tech stack detectie</span><span style="font-size:.65rem;padding:.2rem .5rem;background:var(--purple-bg);color:var(--purple);border-radius:4px">Pain points</span><span style="font-size:.65rem;padding:.2rem .5rem;background:var(--green-bg);color:var(--green);border-radius:4px">Data maturity</span><span style="font-size:.65rem;padding:.2rem .5rem;background:var(--orange-bg);color:var(--orange);border-radius:4px">Outreach hook</span></div></div></div>
<div id="leadgenError" style="display:none"><div class="alert alert-error"></div></div>
</div>
<!-- ===== MARKET ===== -->
<div class="tab-panel" id="panel-market"><div class="page-hdr fade-in-up"><h1>Market Intelligence</h1><p>Nederlandse datacenters & data-gedreven bedrijven</p></div>
<div class="kpi-row"><div class="kpi-card cyan" onclick="window.location='/market'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="database"></i></div><div class="kpi-num"><%= datacenterCount || 0 %></div><div class="kpi-label">Datacenters</div><div class="kpi-sub">Nederlandse markt</div></div><div class="kpi-card purple" onclick="window.location='/market'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="building-2"></i></div><div class="kpi-num"><%= marketCompaniesCount || 0 %></div><div class="kpi-label">Prospects</div><div class="kpi-sub">Data-gedreven bedrijven</div></div></div>
<div class="grid-2"><div class="card card-hover"><div class="card-title">Quick Tools</div><div class="quick-actions" style="grid-template-columns:1fr 1fr"><a href="/market" class="qa-btn" style="--qa:var(--cyan);padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="map" style="color:var(--cyan)"></i></span>Kaart & Datacenters</a><a href="/market" class="qa-btn" style="--qa:var(--purple);padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="target" style="color:var(--purple)"></i></span>Prospects & Leads</a></div></div>
<div class="card card-hover"><div class="card-title">Snel Actie</div><div class="quick-actions"><a href="/market" class="qa-btn"><span class="qa-icon"><i data-lucide="search"></i></span>Open Market Explorer</a><a href="/clients/new" class="qa-btn"><span class="qa-icon"><i data-lucide="user-plus"></i></span>Nieuwe Prospect</a></div></div></div></div>
<!-- ===== CALENDAR ===== -->
<div class="tab-panel" id="panel-calendar"><div class="page-hdr fade-in-up"><h1>Calendar</h1><p>Planning, deadlines & afspraken</p></div>
<div class="kpi-row"><div class="kpi-card cyan" onclick="window.location='/calendar'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="calendar"></i></div><div class="kpi-num"><%= overdueTasks %></div><div class="kpi-label">Open Tasks</div><div class="kpi-sub"><%= completedTasks %> completed</div></div></div>
<div class="card card-hover"><div class="card-title">Acties</div><div class="quick-actions" style="grid-template-columns:1fr 1fr"><a href="/calendar" class="qa-btn" style="padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="calendar-days" style="color:var(--accent)"></i></span>Bekijk Agenda</a><a href="/calendar" class="qa-btn" style="padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="plus-circle" style="color:var(--green)"></i></span>Nieuwe Afspraak</a></div></div></div>
<!-- ===== DATA ===== -->
<div class="tab-panel" id="panel-data"><div class="page-hdr fade-in-up"><h1>Data Quality</h1><p>Data profiling, kwaliteitschecks & modellen</p></div>
<div class="kpi-row"><div class="kpi-card green" onclick="window.location='/quality'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="shield-check"></i></div><div class="kpi-num"><%= assessmentsCount %></div><div class="kpi-label">Assessments</div><div class="kpi-sub">Data kwaliteit checks</div></div><div class="kpi-card purple" onclick="window.location='/quality'" style="cursor:pointer"><div class="kpi-icon"><i data-lucide="code-2"></i></div><div class="kpi-num"><%= qualityFileCount || 0 %></div><div class="kpi-label">Data Bestanden</div><div class="kpi-sub">SQL & dbt modellen</div></div></div>
<div class="card card-hover"><div class="card-title">Data Tools</div><div class="quick-actions" style="grid-template-columns:1fr 1fr"><a href="/quality" class="qa-btn" style="padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="shield-check" style="color:var(--green)"></i></span>Quality Engine</a><a href="/python-quality/" class="qa-btn" style="padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="code-2" style="color:var(--purple)"></i></span>Schema Editor</a><a href="/assess" class="qa-btn" style="padding:.8rem"><span class="qa-icon" style="font-size:1.3rem"><i data-lucide="clipboard-list" style="color:var(--accent)"></i></span>Assessment Starten</a></div></div></div>
<!-- ===== ACTIVITY ===== -->
<div class="tab-panel" id="panel-activity"><div class="page-hdr fade-in-up"><h1>Recente Activiteit</h1></div>
<div class="card card-hover"><div class="card-title">Recent Assessments</div>
<% if(recentAssessments.length===0){ %><div class="empty-state-s">Nog geen recente activiteit</div><% }else{ %><div class="timeline"><% recentAssessments.forEach(function(a){ %><div class="tl-item"><div class="tl-dot" style="background:var(--purple)"></div><div class="tl-body"><div class="tl-title">Rapport voor <strong><%= a.client_name %></strong></div><div class="tl-date"><%= new Date(a.created_at).toLocaleString('nl-NL') %></div></div></div><% }) %></div><% } %></div>
<div style="margin-top:.85rem" class="quick-actions"><a href="/audit" class="qa-btn"><span class="qa-icon"><i data-lucide="file-text"></i></span>Audit Log</a><a href="/notifications" class="qa-btn"><span class="qa-icon"><i data-lucide="bell"></i></span>Notifications</a><a href="/users" class="qa-btn"><span class="qa-icon"><i data-lucide="users"></i></span>Users</a></div></div>
</div></div>
<!-- BATCH MODAL -->
<div id="batchModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.6);z-index:999;display:none;align-items:center;justify-content:center" onclick="if(event.target===this)closeBatchModal()">
<div style="background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:1.25rem;width:95%;max-width:550px;animation:fadeInUp .25s ease">
<h3 style="font-size:.85rem;color:var(--text);margin-bottom:.5rem">Batch URLs (max 10)</h3>
<textarea id="batchTextarea" rows="6" placeholder="https://bedrijf1.nl&#10;https://bedrijf2.nl/about&#10;https://bedrijf3.nl/careers" style="width:100%;padding:.5rem;background:var(--bg);border:1px solid var(--border);border-radius:7px;color:var(--text);font-size:.75rem;font-family:inherit;resize:vertical"></textarea>
<div style="display:flex;gap:.4rem;margin-top:.75rem"><button onclick="submitBatch()" class="btn btn-accent btn-small">Analyseren</button><button onclick="closeBatchModal()" class="btn btn-secondary btn-small">Sluiten</button></div>
</div></div>
<!-- DETAIL MODAL -->
<div id="detailModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.65);z-index:999;display:none;align-items:center;justify-content:center" onclick="if(event.target===this)closeDetailModal()">
<div style="background:var(--bg-card);border:1px solid var(--border);border-radius:12px;width:95%;max-width:650px;max-height:85vh;overflow-y:auto;animation:fadeInUp .25s ease">
<div style="position:sticky;top:0;background:var(--bg-nav);padding:.6rem 1rem;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;z-index:1">
<h3 style="font-size:.82rem;color:var(--text)">Company Details</h3>
<button onclick="closeDetailModal()" style="background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:1.1rem">✕</button></div>
<div id="detailContent"></div>
</div></div>
<style> #batchModal.open,#detailModal.open{display:flex!important}</style>
<script>
document.querySelectorAll('.sb-item[data-tab]').forEach(function(b){
b.addEventListener('click',function(e){
if(this.tagName==='A')return;
e.preventDefault();
var t=this.dataset.tab;
document.querySelectorAll('.sb-item[data-tab]').forEach(function(x){x.classList.remove('active')});
document.querySelectorAll('.tab-panel').forEach(function(x){x.classList.remove('active')});
this.classList.add('active');document.getElementById('panel-'+t).classList.add('active');
var u=new URL(window.location);u.searchParams.set('tab',t);window.history.replaceState({},'',u);
});
});
function switchTab(t){
document.querySelectorAll('.sb-item').forEach(function(x){x.classList.remove('active')});
document.querySelectorAll('.tab-panel').forEach(function(x){x.classList.remove('active')});
var b=document.querySelector('.sb-item[data-tab="'+t+'"]');if(b)b.classList.add('active');
document.getElementById('panel-'+t).classList.add('active');
}
var h=(new URL(window.location)).searchParams.get('tab');
if(h){switchTab(h)}else{switchTab('overview')}
function updateClock(){
var el=document.getElementById('liveClock');
if(el)el.textContent=new Date().toLocaleString('nl-NL',{weekday:'long',day:'numeric',month:'long',hour:'2-digit',minute:'2-digit'});
}
updateClock();setInterval(updateClock,60000);
document.querySelectorAll('.kpi-num[data-count]').forEach(function(el){
var target=parseInt(el.dataset.count,10)||0;
if(target<=20){var n=0;var step=Math.max(1,Math.ceil(target/12));var iv=setInterval(function(){n+=step;if(n>=target){n=target;clearInterval(iv)}el.textContent=n},40);}
});
lucide.createIcons();
fetch('/notifications/api/unread').then(r=>r.json()).then(d=>{if(d.unread>0){var nb=document.querySelector('.nav-icon-btn[title="Meldingen"]');if(nb)nb.setAttribute('data-count',d.unread)}}).catch(function(){});
// LeadGen AI
async function loadLeadgenConfig() {
try { var r=await fetch('/api/v1/leadgen-config'); var c=await r.json();
document.getElementById('lgApiKey').value=c.api_key_set?'••••••••••••••':'';
document.getElementById('lgBaseUrl').value=c.base_url||'https://openrouter.ai/api/v1';
document.getElementById('lgModel').innerHTML='<option value="'+c.model+'">'+c.model+'</option>';
if(c.fallback_models) c.fallback_models.forEach(function(m){document.getElementById('lgModel').innerHTML+='<option value="'+m+'">'+m+'</option>'});
} catch(e){}
}
async function loadModels() {
var sel=document.getElementById('lgModel'); sel.innerHTML='<option>Laden...</option>';
try { var r=await fetch('/api/v1/leadgen-models'); var d=await r.json();
sel.innerHTML='';
(d.models||[]).forEach(function(m){sel.innerHTML+='<option value="'+m.id+'">'+m.id.split('/').pop()+' — '+m.name+' ('+(m.context_length/1000).toFixed(0)+'K)</option>'});
} catch(e){ sel.innerHTML='<option>Fout bij laden</option>'; }
}
async function saveLeadgenConfig() {
var key=document.getElementById('lgApiKey').value.trim();
var payload={base_url:document.getElementById('lgBaseUrl').value.trim(),model:document.getElementById('lgModel').value};
if(key && key!=='••••••••••••••') payload.api_key=key;
try { await fetch('/api/v1/leadgen-config',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
var s=document.getElementById('lgStatus'); s.style.display='inline'; setTimeout(function(){s.style.display='none'},2000);
} catch(e){ alert('Fout: '+e.message); }
}
function toggleLeadgenConfig() { var c=document.getElementById('leadgenConfig'); c.style.display=c.style.display==='none'?'block':'none'; if(c.style.display==='block')loadLeadgenConfig(); }
async function analyzeLeadgen() {
var url = document.getElementById('leadgenUrl').value.trim();
if (!url) return alert('Voer een URL in');
if (!/^https?:\/\//.test(url)) url = 'https://' + url;
var empty = document.getElementById('leadgenEmpty');
var load = document.getElementById('leadgenLoading');
var results = document.getElementById('leadgenResults');
var err = document.getElementById('leadgenError');
empty.style.display = 'none'; err.style.display = 'none'; results.style.display = 'none';
load.style.display = 'block';
try {
var r = await fetch('/api/v1/leadgen-analyze', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url:url})});
var d = await r.json();
load.style.display = 'none';
if (!d.success) { err.querySelector('.alert-error').textContent = d.error || 'Onbekende fout'; err.style.display = 'block'; return; }
renderLeadgen([d]);
} catch(e) { load.style.display = 'none'; err.querySelector('.alert-error').textContent = e.message; err.style.display = 'block'; }
}
function batchLeadgen() { document.getElementById('batchModal').classList.add('open'); }
function closeBatchModal() { document.getElementById('batchModal').classList.remove('open'); }
async function submitBatch() {
var t = document.getElementById('batchTextarea').value.trim();
if (!t) return;
var list = t.split('\n').map(function(u){return u.trim()}).filter(Boolean).slice(0,10);
if (!list.length) return alert('Geen geldige URLs');
closeBatchModal();
batchAnalyze(list);
}
async function batchAnalyze(list) {
var empty = document.getElementById('leadgenEmpty');
var load = document.getElementById('leadgenLoading');
var results = document.getElementById('leadgenResults');
var err = document.getElementById('leadgenError');
empty.style.display = 'none'; err.style.display = 'none'; results.style.display = 'none';
load.style.display = 'block';
try {
var r = await fetch('/api/v1/leadgen-batch', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({urls:list})});
var d = await r.json();
load.style.display = 'none';
if (!d.success) { err.querySelector('.alert-error').textContent = d.error || 'Onbekende fout'; err.style.display = 'block'; return; }
renderLeadgen(d.results);
} catch(e) { load.style.display = 'none'; err.querySelector('.alert-error').textContent = e.message; err.style.display = 'block'; }
}
var leadgenHistory = [];
function renderLeadgen(list) {
var results = document.getElementById('leadgenResults');
var cards = document.getElementById('leadgenCards');
var valid = list.filter(function(r){return r.success && r.profile});
var failed = list.filter(function(r){return !r.success});
var t = valid.length > 0 ? valid[0].elapsed_ms : 0;
leadgenHistory = valid;
document.getElementById('leadgenTiming').textContent = valid.length + ' van ' + list.length + ' · ' + (t ? Math.round(t/1000)+'s' : '');
cards.innerHTML = valid.map(function(r,idx){var p=r.profile;
var matScore = p.estimated_data_maturity === 'High' ? 100 : p.estimated_data_maturity === 'Medium' ? 60 : 30;
var matColor = p.estimated_data_maturity === 'High' ? 'var(--green)' : p.estimated_data_maturity === 'Medium' ? 'var(--orange)' : 'var(--red)';
var hasHiring = (p.hiring_roles && p.hiring_roles.length > 0) || (p.desired_skills && p.desired_skills.length > 0);
return '<div class="card card-hover fade-in-up" style="margin-bottom:.6rem;overflow:hidden;border-left:4px solid var(--orange)">'+
'<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:.5rem;flex-wrap:wrap">'+
'<div style="flex:1;min-width:200px"><h3 style="font-size:.95rem;color:var(--text);font-weight:700;margin-bottom:.1rem">'+esc(p.company_name)+'</h3>'+
'<div style="display:flex;gap:.4rem;flex-wrap:wrap;align-items:center">'+
(p.industry?'<span style="font-size:.62rem;color:var(--text-dim);background:var(--border-light);padding:.12rem .45rem;border-radius:4px;font-weight:500">'+esc(p.industry)+'</span>':'')+
(p.company_size_hint?'<span style="font-size:.62rem;color:var(--text-dim)">'+esc(p.company_size_hint)+'</span>':'')+
(p.source_url?'<a href="'+esc(p.source_url)+'" target="_blank" style="font-size:.58rem;color:var(--text-dim);text-decoration:none">🔗 bron</a>':'')+
'</div></div>'+
'<div style="display:flex;align-items:center;gap:.35rem"><div style="width:50px;text-align:center"><div style="font-size:.7rem;font-weight:800;color:'+matColor+'">'+p.estimated_data_maturity+'</div><div style="height:4px;background:var(--border-light);border-radius:2px;overflow:hidden;margin-top:2px"><div style="height:100%;width:'+matScore+'%;background:'+matColor+';border-radius:2px"></div></div><div style="font-size:.5rem;color:var(--text-dim)">maturity</div></div></div>'+
'</div>'+
// --- TECH TABLE ---
'<table class="data-table" style="margin:.5rem 0;min-width:auto"><thead><tr><th style="width:50%">Tech Stack</th><th style="width:50%">Pain Points</th></tr></thead><tbody><tr><td style="padding:.3rem .5rem">'+
'<div style="display:flex;gap:.25rem;flex-wrap:wrap">'+
(p.detected_technologies||[]).map(function(t,ti){
var colors = ['var(--cyan-bg)','var(--purple-bg)','var(--green-bg)','var(--orange-bg)','var(--pink-bg)'];
var cols = ['var(--cyan)','var(--purple)','var(--green)','var(--orange)','var(--pink)'];
return '<span style="font-size:.6rem;padding:.15rem .45rem;background:'+colors[ti%5]+';color:'+cols[ti%5]+';border-radius:4px;font-weight:500;cursor:pointer;margin-bottom:2px" onclick="searchTech(\''+esc(t)+'\')" title="Zoek '+esc(t)+'">'+esc(t)+'</span>';
}).join('')+'</div>'+
'</td><td style="padding:.3rem .5rem">'+
'<div style="display:flex;gap:.25rem;flex-wrap:wrap">'+
(p.current_pain_points||[]).map(function(pp){return '<span style="font-size:.6rem;padding:.15rem .45rem;background:var(--red-bg);color:var(--red);border-radius:4px;font-weight:500;margin-bottom:2px">'+esc(pp)+'</span>'}).join('')+'</div>'+
'</td></tr></tbody></table>'+
// --- HIRING TABLE ---
(hasHiring ? '<table class="data-table" style="margin-bottom:.4rem;min-width:auto"><thead><tr><th style="width:45%">Vacatures / Rollen</th><th style="width:55%">Gevraagde Skills</th></tr></thead><tbody><tr><td style="padding:.3rem .5rem">'+
'<div style="display:flex;gap:.25rem;flex-wrap:wrap">'+
(p.hiring_roles||[]).map(function(hr){return '<span style="font-size:.6rem;padding:.15rem .45rem;background:var(--green-bg);color:var(--green);border-radius:4px;font-weight:500;margin-bottom:2px">'+esc(hr)+'</span>'}).join('')+'</div>'+
'</td><td style="padding:.3rem .5rem">'+
'<div style="display:flex;gap:.25rem;flex-wrap:wrap">'+
(p.desired_skills||[]).map(function(ds){return '<span style="font-size:.6rem;padding:.15rem .45rem;background:var(--orange-bg);color:var(--orange);border-radius:4px;font-weight:500;margin-bottom:2px">'+esc(ds)+'</span>'}).join('')+'</div>'+
'</td></tr></tbody></table>' : '')+
(p.outreach_hook?'<div style="background:var(--bg);border:1px solid var(--accent);border-radius:8px;padding:.55rem .75rem;margin-top:.35rem;position:relative">'+
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.25rem"><span style="font-size:.6rem;color:var(--accent);text-transform:uppercase;letter-spacing:.3px;font-weight:700">Outreach Hook</span>'+
'<button onclick="copyHook('+idx+')" style="background:var(--border-light);border:none;border-radius:5px;padding:.12rem .45rem;cursor:pointer;color:var(--text-muted);font-size:.6rem;font-family:inherit">📋 Kopieer</button></div>'+
'<p style="font-size:.75rem;color:var(--text);margin-top:.15rem;line-height:1.5">'+esc(p.outreach_hook)+'</p></div>':'')+
'<div style="display:flex;gap:.4rem;margin-top:.65rem;flex-wrap:wrap">'+
'<button onclick="saveAsProspect('+idx+')" class="btn btn-accent btn-small" style="display:inline-flex;align-items:center;gap:.25rem;padding:.3rem .7rem;font-size:.65rem;border-radius:6px"><i data-lucide="user-plus" style="width:12px;height:12px"></i> Save als Prospect</button>'+
'<button onclick="sendOutreach('+idx+')" class="btn btn-secondary btn-small" style="display:inline-flex;align-items:center;gap:.25rem;padding:.3rem .7rem;font-size:.65rem;border-radius:6px"><i data-lucide="send" style="width:12px;height:12px"></i> Outreach email</button>'+
'<button onclick="showDetail('+idx+')" class="btn btn-secondary btn-small" style="display:inline-flex;align-items:center;gap:.25rem;padding:.3rem .7rem;font-size:.65rem;border-radius:6px"><i data-lucide="eye" style="width:12px;height:12px"></i> Details</button>'+
'</div></div>';
}).join('');
if (failed.length) cards.innerHTML += '<div class="alert alert-error" style="margin-top:.5rem">'+failed.length+' URL(s) konden niet geanalyseerd worden</div>';
results.style.display = 'block';
lucide.createIcons();
}
function copyHook(idx) {
var p = leadgenHistory[idx].profile;
navigator.clipboard.writeText(p.outreach_hook).then(function(){alert('Outreach hook gekopieerd!')});
}
function searchTech(tech) {
window.open('/search?q='+encodeURIComponent(tech), '_blank');
}
function saveAsProspect(idx) {
var p = leadgenHistory[idx].profile;
var params = new URLSearchParams();
params.set('name', p.company_name);
if (p.industry) params.set('industry', p.industry);
var tech = (p.detected_technologies||[]).join(', ');
if (tech) params.set('notes', 'Tech stack: '+tech+'\nPain points: '+(p.current_pain_points||[]).join(', ')+'\nOutreach: '+p.outreach_hook);
window.open('/clients/new?'+params.toString(), '_blank');
}
function sendOutreach(idx) {
var p = leadgenHistory[idx].profile;
window.open('/email/compose?subject=Mek-Tech Consulting — '+encodeURIComponent(p.company_name)+'&to=', '_blank');
}
function showDetail(idx) {
var p = leadgenHistory[idx].profile;
var html = '<div style="padding:1rem">'+
'<h2 style="font-size:1.1rem;margin-bottom:.5rem">'+esc(p.company_name)+'</h2>'+
'<table class="info-table" style="margin-bottom:1rem">'+
'<tr><td>Industry</td><td>'+esc(p.industry||'-')+'</td></tr>'+
'<tr><td>Size</td><td>'+esc(p.company_size_hint||'-')+'</td></tr>'+
'<tr><td>Maturity</td><td>'+esc(p.estimated_data_maturity)+'</td></tr>'+
'<tr><td>URL</td><td><a href="'+esc(p.source_url||'')+'" target="_blank" class="link">'+esc(p.source_url||'-')+'</a></td></tr>'+
'<tr><td>Tech Blog</td><td>'+(p.tech_blog_posts?'<a href="'+esc(p.tech_blog_posts)+'" target="_blank" class="link">'+esc(p.tech_blog_posts)+'</a>':'-')+'</td></tr>'+
'</table>'+
'<h3 style="font-size:.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px;margin-bottom:.4rem">Alle Technologieën</h3>'+
'<div style="display:flex;gap:.3rem;flex-wrap:wrap;margin-bottom:1rem">'+ (p.detected_technologies||[]).map(function(t){return '<span style="font-size:.65rem;padding:.2rem .55rem;background:var(--cyan-bg);color:var(--cyan);border-radius:5px;font-weight:500">'+esc(t)+'</span>'}).join('')+'</div>'+
'<h3 style="font-size:.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px;margin-bottom:.4rem">Pain Points</h3>'+
'<div style="display:flex;gap:.3rem;flex-wrap:wrap;margin-bottom:1rem">'+ (p.current_pain_points||[]).map(function(pp){return '<span style="font-size:.65rem;padding:.2rem .55rem;background:var(--red-bg);color:var(--red);border-radius:5px;font-weight:500">'+esc(pp)+'</span>'}).join('')+'</div>'+
'<h3 style="font-size:.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px;margin-bottom:.4rem">Outreach Hook</h3>'+
'<div style="background:var(--bg);border:1px solid var(--accent);border-radius:8px;padding:.6rem .8rem;margin-bottom:1rem"><p style="font-size:.78rem;color:var(--text);line-height:1.5">'+esc(p.outreach_hook)+'</p></div>'+
(p.hiring_roles&&p.hiring_roles.length?'<h3 style="font-size:.75rem;color:var(--text-muted);text-transform:uppercase;letter-spacing:.3px;margin-bottom:.4rem">Vacatures & Gewenste Skills</h3><table class="data-table" style="margin-bottom:1rem"><thead><tr><th>Rol</th><th style="text-align:right">Gevraagde Skills</th></tr></thead><tbody>'+
(p.hiring_roles||[]).map(function(hr,i){var ds = (p.desired_skills||[]).slice(i*3,(i+1)*3).join(', '); return '<tr><td>'+esc(hr)+'</td><td style="text-align:right"><span style="font-size:.65rem;color:var(--orange)">'+esc(ds||'—')+'</span></td></tr>'}).join('')+'</tbody></table>':'')+
(p.job_listings_url?'<a href="'+esc(p.job_listings_url)+'" target="_blank" class="btn btn-accent btn-small" style="margin-top:.25rem">Bekijk vacatures</a>':'')+
'</div>';
var modal = document.getElementById('detailModal');
document.getElementById('detailContent').innerHTML = html;
modal.classList.add('open');
}
function closeDetailModal() { document.getElementById('detailModal').classList.remove('open'); }
function esc(s){return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;')}
</script>
<script src="/js/theme.js"></script></body></html>