Files
mek-tech-consulting/dashboard.ejs
T

817 lines
80 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>