Files

170 lines
11 KiB
Plaintext

<!DOCTYPE html><html lang="nl"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Projecten — 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><script src="https://cdn.jsdelivr.net/npm/chart.js"></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}
.main-area{flex:1;margin-left:var(--sb-w);padding:1.5rem 2rem 3rem;max-width:1400px}
.page-hdr{margin-bottom:1.5rem}.page-hdr h1{font-size:1.5rem;color:var(--text);font-weight:800;letter-spacing:-.4px}
.kanban-board{display:grid;grid-template-columns:repeat(4,1fr);gap:1rem;overflow-x:auto;padding-bottom:1rem}
.kanban-column{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1rem;min-height:400px}
.kanban-header{font-size:.85rem;font-weight:600;color:var(--text);margin-bottom:.75rem;display:flex;justify-content:space-between;align-items:center}
.kanban-count{font-size:.7rem;color:var(--text-muted);background:var(--border-light);padding:.2rem .5rem;border-radius:999px}
.kanban-tasks{display:flex;flex-direction:column;gap:.5rem}
.task-card{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:.75rem;cursor:grab;transition:all .2s}
.task-card:hover{border-color:var(--accent);transform:translateY(-2px)}
.task-card.dragging{opacity:.5}
.task-title{font-size:.8rem;font-weight:500;color:var(--text);margin-bottom:.4rem}
.task-meta{font-size:.7rem;color:var(--text-muted);display:flex;gap:.5rem;align-items:center}
.task-badge{padding:.1rem .3rem;border-radius:4px;font-size:.65rem;font-weight:500}
.task-badge.critical{background:var(--red-bg);color:var(--red)}
.task-badge.high{background:var(--orange-bg);color:var(--orange)}
.task-badge.medium{background:var(--cyan-bg);color:var(--cyan)}
.task-badge.low{background:var(--green-bg);color:var(--green)}
.project-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem}
.project-card{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1.25rem;transition:all .2s;text-decoration:none;color:inherit;display:block}
.project-card:hover{border-color:var(--accent);transform:translateY(-2px);box-shadow:0 8px 25px rgba(0,0,0,.1)}
.project-card-header{display:flex;justify-content:space-between;align-items:start;margin-bottom:.75rem}
.project-title{font-size:.95rem;font-weight:600;color:var(--text)}
.project-client{font-size:.75rem;color:var(--text-muted);margin-bottom:.5rem}
.project-meta{display:flex;gap:1rem;font-size:.7rem;color:var(--text-dim)}
.project-progress{height:4px;background:var(--border-light);border-radius:2px;margin:.75rem 0;overflow:hidden}
.project-progress-fill{height:100%;background:linear-gradient(90deg,var(--accent),var(--purple));transition:width .3s}
.gantt-chart{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1.5rem;overflow-x:auto}
.gantt-header{display:flex;gap:2rem;padding-bottom:.75rem;border-bottom:1px solid var(--border);margin-bottom:1rem;font-size:.7rem;color:var(--text-muted)}
.gantt-row{display:flex;align-items:center;padding:.5rem 0;border-bottom:1px solid var(--border-light)}
.gantt-label{width:200px;font-size:.8rem;color:var(--text);flex-shrink:0}
.gantt-bar-container{flex:1;position:relative;height:24px;background:var(--bg);border-radius:4px}
.gantt-bar{position:absolute;height:100%;background:linear-gradient(90deg,var(--accent),var(--purple));border-radius:4px;transition:all .2s}
.gantt-bar:hover{filter:brightness(1.1)}
@media(max-width:1024px){.kanban-board{grid-template-columns:repeat(2,1fr)}}
@media(max-width:768px){.kanban-board{grid-template-columns:1fr}.project-grid{grid-template-columns:1fr}}
</style>
</head>
<body>
<div 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-links"><a href="/" class="nav-link">Dashboard</a><a href="/projects" class="nav-link active">Projecten</a><a href="/clients" class="nav-link">Cliënten</a><a href="/engagements" class="nav-link">Engagements</a><a href="/finance" class="nav-link">Financiën</a><a href="/market" class="nav-link">Markt</a><a href="/tasks" class="nav-link">Taken</a><a href="/quality" class="nav-link">Data</a><a href="/settings" class="nav-link">Instellingen</a><a href="/auth/logout" class="nav-link logout">Uitloggen</a></div></div></div>
<div class="app-layout">
<div class="sidebar">
<div class="sb-brand"><h2><span class="brand-dot"></span>Mek-Tech Enterprise</h2><p>Project Management</p></div>
<div class="sb-nav">
<div class="sb-sep">Views</div>
<button class="sb-item" data-tab="kanban"><span class="sb-icon"><i data-lucide="trello"></i></span>Kanban Board</button>
<button class="sb-item" data-tab="gantt"><span class="sb-icon"><i data-lucide="bar-chart-2"></i></span>Gantt Chart</button>
<button class="sb-item" data-tab="projects"><span class="sb-icon"><i data-lucide="folder"></i></span>Alle Projecten</button>
<div class="sb-sep">Actions</div>
<button class="sb-item" id="newProjectBtn"><span class="sb-icon"><i data-lucide="plus"></i></span>Nieuw Project</button>
<button class="sb-item" id="newTaskBtn"><span class="sb-icon"><i data-lucide="plus-circle"></i></span>Nieuwe Taak</button>
</div>
</div>
<div class="main-area">
<div class="page-hdr fade-in-up"><h1>Project Management</h1><p><%= projects.length %> projecten</p></div>
<div class="tab-panel active" id="panel-kanban">
<div class="kanban-board">
<% const columns = ['todo','in_progress','review','done']; const columnNames = ['To Do','In Progress','Review','Done']; %>
<% columns.forEach((col,i)=>{ %>
<div class="kanban-column">
<div class="kanban-header"><span><%= columnNames[i] %></span><span class="kanban-count"><%= projectTasks.filter(t=>t.status===col).length %></span></div>
<div class="kanban-tasks" data-status="<%= col %>">
<% projectTasks.filter(t=>t.status===col).forEach(task=>{ %>
<div class="task-card" draggable="true" data-task-id="<%= task.id %>">
<div class="task-title"><%= task.title %></div>
<div class="task-meta">
<span class="task-badge <%= task.priority %>"><%= task.priority %></span>
<span><i data-lucide="user" style="width:12px;height:12px"></i> <%= task.assignee_name||'Unassigned' %></span>
</div>
<div style="height:4px;background:var(--border-light);border-radius:2px;margin:.5rem 0;overflow:hidden">
<div style="height:100%;background:var(--accent);width:<%= task.progress %>%;border-radius:2px"></div>
</div>
</div>
<% }) %>
</div>
</div>
<% }) %>
</div>
</div>
<div class="tab-panel" id="panel-gantt">
<div class="gantt-chart">
<div class="gantt-header"><span>Task</span><span>Progress</span></div>
<% projectTasks.forEach(task=>{ %>
<div class="gantt-row">
<div class="gantt-label"><%= task.title %></div>
<div class="gantt-bar-container">
<div class="gantt-bar" style="width:<%= task.progress %>%;background:<%= task.status==='done'?'var(--green)':task.status==='in_progress'?'var(--accent)':'var(--orange)' %>"></div>
</div>
<div style="width:60px;font-size:.7rem;color:var(--text)"><%= task.progress %>%</div>
</div>
<% }) %>
</div>
</div>
<div class="tab-panel" id="panel-projects">
<div class="project-grid">
<% projects.forEach(project=>{ %>
<a href="/projects/<%= project.id %>" class="project-card fade-in-up">
<div class="project-card-header">
<div class="project-title"><%= project.name %></div>
<span class="status-badge <%= project.status %>"><%= project.status %></span>
</div>
<div class="project-client"><%= project.client_name||'No client' %></div>
<div class="project-meta">
<span><%= project.task_count %> taken</span>
<span><%= project.completed_tasks %> voltooid</span>
</div>
</a>
<% }) %>
</div>
</div>
</div></div>
<div class="modal" id="newProjectModal" style="display:none">
<div class="modal-content">
<div class="modal-header"><h3>Nieuw Project</h3><button class="btn-small" id="closeProjectModal">✕</button></div>
<form method="POST" action="/projects/new">
<div class="form-grid">
<div><label>Naam</label><input type="text" name="name" required></div>
<div><label>Cliënt</label><select name="client_id"><option value="">Selecteer cliënt</option><% clients.forEach(c=>{ %><option value="<%= c.id %>"><%= c.name %></option><% }) %></select></div>
<div><label>Start Datum</label><input type="date" name="start_date"></div>
<div><label>Eind Datum</label><input type="date" name="end_date"></div>
<div><label>Prioriteit</label><select name="priority"><option value="low">Low</option><option value="medium" selected>Medium</option><option value="high">High</option><option value="critical">Critical</option></select></div>
</div>
<div style="margin-top:1rem"><label>Beschrijving</label><textarea name="description" rows="3"></textarea></div>
<div class="form-actions"><button type="submit" class="btn btn-primary">Project Aanmaken</button></div>
</form>
</div>
</div>
<script>
document.querySelectorAll('.sb-item').forEach(function(b){
b.addEventListener('click',function(){
var t=this.dataset.tab;
document.querySelectorAll('.sb-item').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');
});
});
document.getElementById('newProjectBtn').addEventListener('click',function(){
document.getElementById('newProjectModal').style.display='flex';
});
document.getElementById('closeProjectModal').addEventListener('click',function(){
document.getElementById('newProjectModal').style.display='none';
});
document.getElementById('newTaskBtn').addEventListener('click',function(){
alert('Task modal coming soon');
});
document.querySelectorAll('.task-card').forEach(card=>{
card.addEventListener('dragstart',e=>{
e.dataTransfer.setData('text/plain',card.dataset.taskId);
});
});
document.querySelectorAll('.kanban-tasks').forEach(column=>{
column.addEventListener('dragover',e=>e.preventDefault());
column.addEventListener('drop',e=>{
e.preventDefault();
const taskId=e.dataTransfer.getData('text/plain');
const newStatus=column.dataset.status;
fetch('/projects/tasks/'+taskId+'/status',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({status:newStatus})}).then(()=>location.reload());
});
});
lucide.createIcons();
</script>
</body></html>