Files
foodlinkk-command-center/cockpit/templates/revenue_cockpit.html
T

341 lines
17 KiB
HTML
Raw Normal View History

{% extends "base.html" %}
{% block title %}Revenue Cockpit · Cucina{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/revenue-cockpit.css?v=12" />
{% endblock %}
{% block content %}
<div class="rc-page" :class="{ 'rc-mode-sheet': viewMode === 'sheet' }" x-data="revenueCockpit()" x-init="init()">
<header class="rc-header">
<div>
<h1>Revenue Cockpit <span class="live-badge">Excel Live</span></h1>
<p class="rc-subtitle" x-show="meta.file_mtime">
<span x-text="meta.source_file || 'Succes Sheet .xlsx'"></span>
· <span x-text="fmtDate(meta.file_mtime)"></span>
</p>
</div>
<div class="rc-header-actions">
<div class="rc-view-toggle">
<button type="button" class="rc-toggle-btn" :class="{ active: viewMode === 'cockpit' }" @click="viewMode = 'cockpit'">Cockpit</button>
<button type="button" class="rc-toggle-btn" :class="{ active: viewMode === 'sheet' }" @click="switchToSheet()">Bewerken</button>
</div>
<button type="button" class="btn btn-sm btn-pulse" @click="loadLive()" :disabled="loading">↻</button>
<button type="button" class="btn btn-sm" @click="takeSnapshot()" title="Snapshot">📸</button>
<button type="button" class="btn btn-sm btn-pulse-green" @click="importExcel()" title="Sync DB">📥</button>
</div>
</header>
<!-- Cockpit: Excel-layout met charts -->
<div class="rc-cockpit" x-show="viewMode === 'cockpit'" :class="{ 'rc-loading': loading }">
<div class="rc-summary-strip">
<div class="rc-sum-item">
<span class="rc-sum-label">Marge / mnd</span>
<strong x-text="fmtEuro(aggregates.total_margin_month)"></strong>
</div>
<div class="rc-sum-item">
<span class="rc-sum-label">Marge / jr</span>
<strong x-text="fmtEuro(aggregates.total_margin_year)"></strong>
</div>
<div class="rc-sum-item">
<span class="rc-sum-label">Deals</span>
<strong x-text="filteredProjects().length + '/' + projects.length"></strong>
</div>
<div class="rc-visual-toggle" role="group" aria-label="Weergave">
<button type="button" class="rc-vis-btn" :class="{ active: visualMode === 'table' }" @click="setVisualMode('table')">Tabel</button>
<button type="button" class="rc-vis-btn" :class="{ active: visualMode === 'bars' }" @click="setVisualMode('bars')">Staven</button>
<button type="button" class="rc-vis-btn" :class="{ active: visualMode === 'cards' }" @click="setVisualMode('cards')">Kaarten</button>
</div>
<input type="search" class="rc-filter-search rc-filter-inline" placeholder="Zoek deal…" x-model="filterQ" />
<div class="rc-style-chips">
<button type="button" class="rc-chip" :class="{ active: !filterStyle }" @click="filterStyle = ''">Alle</button>
<template x-for="s in styleFilters" :key="'c-' + s.id">
<button type="button" class="rc-chip" :class="['rc-chip-' + s.id, { active: filterStyle === s.id }]"
@click="filterStyle = filterStyle === s.id ? '' : s.id"
x-text="s.label"></button>
</template>
</div>
</div>
<div class="rc-goals-banner" x-show="goals.vision_text || goals.horizon_text || goals.tagline">
<span x-text="goals.vision_text"></span>
<span class="rc-goals-sep" x-show="goals.horizon_text">·</span>
<span x-text="goals.horizon_text"></span>
<span class="rc-goals-sep" x-show="goals.mid_text">·</span>
<span x-text="goals.mid_text"></span>
<em x-show="goals.tagline" x-text="goals.tagline"></em>
</div>
<!-- TABEL -->
<div class="rc-visual-wrap" x-show="visualMode === 'table'" x-cloak>
<table class="rc-visual-table" cellspacing="0" cellpadding="0">
<colgroup>
<col class="rc-col-deal" />
<col class="rc-col-chart-num" />
<col class="rc-col-chart-num" />
<col class="rc-col-donut" />
<col class="rc-col-steps" />
</colgroup>
<tr class="rc-visual-head">
<td class="rc-vcell">Deal</td>
<td class="rc-vcell rc-num">Marge mnd</td>
<td class="rc-vcell rc-num">Marge jr</td>
<td class="rc-vcell rc-num">Aandeel</td>
<td class="rc-vcell">Next steps</td>
</tr>
<template x-for="row in visualRows()" :key="'t-' + row.source_row + '-' + row.name">
<tr class="rc-visual-row"
:class="['rc-style-' + (row.row_style || 'white'), { 'rc-row-open': selectedKey === projectKey(row) }]"
@click="toggleExpand(row)">
<td class="rc-vcell rc-vdeal">
<span class="rc-deal-name" x-text="row.name"></span>
<span class="rc-brand-tag" x-show="row.logoCell === 'cucina'">Cucina</span>
<span class="rc-brand-tag rc-brand-tag-fl" x-show="row.logoCell === 'foodlinkk'">Foodlinkk</span>
</td>
<td class="rc-vcell rc-vnum">
<span class="rc-vamount" x-text="row.margin_month != null ? fmtEuroCompact(row.margin_month) : '—'"></span>
<div class="rc-mini-bar" x-show="row.margin_month">
<div class="rc-mini-bar-fill" :style="'width:' + marginBarPct(row, 'margin_month') + '%;background:' + styleColor(row.row_style)"></div>
</div>
</td>
<td class="rc-vcell rc-vnum">
<span class="rc-vamount" x-text="row.margin_year != null ? fmtEuroCompact(row.margin_year) : '—'"></span>
<div class="rc-mini-bar" x-show="row.margin_year">
<div class="rc-mini-bar-fill rc-mini-bar-year" :style="'width:' + marginBarPct(row, 'margin_year') + '%'"></div>
</div>
</td>
<td class="rc-vcell rc-vdonut">
<div class="rc-donut" x-show="row.margin_month" :style="donutStyle(row)">
<span class="rc-donut-label" x-text="sharePct(row) + '%'"></span>
</div>
<span class="rc-donut-empty" x-show="!row.margin_month">—</span>
</td>
<td class="rc-vcell rc-vsteps">
<span x-text="truncate(row.next_steps, 72) || '—'"></span>
</td>
</tr>
</template>
</table>
</div>
<!-- STAVEN -->
<div class="rc-bars-view" x-show="visualMode === 'bars'" x-cloak>
<template x-for="row in rankedRows()" :key="'b-' + row.source_row + '-' + row.name">
<button type="button" class="rc-bar-row"
:class="['rc-style-' + (row.row_style || 'white'), { active: selectedKey === projectKey(row) }]"
@click="toggleExpand(row)">
<div class="rc-bar-row-top">
<span class="rc-bar-name" x-text="row.name"></span>
<span class="rc-bar-amt" x-text="row.margin_month != null ? fmtEuroCompact(row.margin_month) : '—'"></span>
<span class="rc-bar-share" x-text="sharePct(row) + '%'"></span>
</div>
<div class="rc-bar-track">
<div class="rc-bar-fill" :style="'width:' + marginBarPct(row, 'margin_month') + '%;background:' + styleColor(row.row_style)"></div>
</div>
</button>
</template>
</div>
<!-- KAARTEN -->
<div class="rc-cards-view" x-show="visualMode === 'cards'" x-cloak>
<template x-for="row in cardRows()" :key="'k-' + row.source_row + '-' + row.name">
<article class="rc-card"
:class="['rc-style-' + (row.row_style || 'white'), { active: selectedKey === projectKey(row) }]"
@click="toggleExpand(row)">
<div class="rc-card-head">
<h3 x-text="row.name"></h3>
<span class="rc-chip rc-chip-static" :class="'rc-chip-' + (row.row_style || 'white')" x-text="styleLabel(row.row_style)"></span>
</div>
<div class="rc-card-metrics">
<div>
<span>mnd</span>
<strong x-text="row.margin_month != null ? fmtEuroCompact(row.margin_month) : '—'"></strong>
</div>
<div x-show="row.margin_year != null">
<span>jr</span>
<strong x-text="fmtEuroCompact(row.margin_year)"></strong>
</div>
<div>
<span>aandeel</span>
<strong x-text="sharePct(row) + '%'"></strong>
</div>
</div>
<div class="rc-card-bar" aria-hidden="true">
<div :style="'width:' + marginBarPct(row, 'margin_month') + '%;background:' + styleColor(row.row_style)"></div>
</div>
<p class="rc-card-steps" x-text="truncate(row.next_steps, 100) || 'Geen next step'"></p>
</article>
</template>
</div>
<!-- DETAIL PANEEL -->
<aside class="rc-detail" x-show="selectedProject" x-cloak>
<div class="rc-detail-head">
<div>
<h3 x-text="selectedProject?.name"></h3>
<span class="rc-chip rc-chip-static" :class="'rc-chip-' + (selectedProject?.row_style || 'white')"
x-text="styleLabel(selectedProject?.row_style)"></span>
</div>
<button type="button" class="rc-detail-close" @click="selectedKey = null; expandedKey = null">✕</button>
</div>
<div class="rc-detail-grid">
<div class="rc-detail-stat">
<span>Marge / mnd</span>
<strong x-text="fmtEuro(selectedProject?.margin_month)"></strong>
</div>
<div class="rc-detail-stat" x-show="selectedProject?.margin_year != null">
<span>Marge / jr</span>
<strong x-text="fmtEuro(selectedProject?.margin_year)"></strong>
</div>
<div class="rc-detail-stat">
<span>Portfolio</span>
<strong x-text="sharePct(selectedProject || {}) + '%'"></strong>
</div>
<div class="rc-detail-ring" :style="donutStyle(selectedProject || {})">
<span x-text="sharePct(selectedProject || {}) + '%'"></span>
</div>
</div>
<div class="rc-detail-compare">
<div class="rc-detail-compare-col">
<div class="rc-detail-vbar" :style="'height:' + marginBarPct(selectedProject || {}, 'margin_month') + '%;background:' + styleColor(selectedProject?.row_style)"></div>
<span>Mnd</span>
</div>
<div class="rc-detail-compare-col" x-show="selectedProject?.margin_year != null">
<div class="rc-detail-vbar rc-detail-vbar-year" :style="'height:' + marginBarPct(selectedProject || {}, 'margin_year') + '%'"></div>
<span>Jr</span>
</div>
</div>
<div class="rc-detail-status">
<span class="rc-detail-label">Zet status</span>
<div class="rc-style-chips rc-style-chips-assign">
<template x-for="s in styleFilters" :key="'as-' + s.id">
<button type="button" class="rc-chip" :class="['rc-chip-' + s.id, { active: selectedProject?.row_style === s.id }]"
@click="setCockpitStyle(s.id)" x-text="s.label"></button>
</template>
</div>
<p class="rc-detail-hint" x-show="selectedProject && !selectedProject.db_id">Sync DB om status permanent te bewaren</p>
</div>
<div class="rc-detail-steps">
<h4>Next steps</h4>
<p x-text="selectedProject?.next_steps || 'Geen next steps in Excel'"></p>
</div>
<div class="rc-detail-task" x-show="selectedProject?.db_id">
<div class="rc-inline">
<select x-model="taskForm.agent_name">
<template x-for="a in agents" :key="a"><option :value="a" x-text="a"></option></template>
</select>
<input type="text" x-model="taskForm.title" placeholder="Taak…" />
<button type="button" class="btn btn-sm btn-pulse" @click="assignTask()">→</button>
</div>
</div>
</aside>
</div>
<!-- Bewerken: originele Excel -->
<div class="rc-excel-page" x-show="viewMode === 'sheet'" x-cloak>
<div class="rc-toolbar">
<span class="rc-toolbar-title">Bewerken · DB</span>
<div class="rc-toolbar-actions">
<span class="rc-save-hint" x-show="saving" x-cloak>Opslaan…</span>
<span class="rc-save-hint rc-saved" x-show="savedAt && !saving" x-cloak x-text="'Opgeslagen ' + savedAt"></span>
</div>
</div>
<div class="rc-sheet-wrap" :class="{ 'rc-loading': loading }">
<table class="rc-sheet" cellspacing="0" cellpadding="0">
<colgroup>
<col class="rc-col-logo" />
<col class="rc-col-deal" />
<col class="rc-col-num" />
<col class="rc-col-num" />
<col class="rc-col-steps" />
</colgroup>
<tr class="rc-row rc-row-goals">
<td class="rc-logo-cell"></td>
<td class="rc-cell rc-cell-edit" contenteditable="true"
x-init="$el.textContent = goals.vision_text || ''"
@blur="saveGoalsField('vision_text', $event.target)" @focus="editStart($event)"></td>
<td class="rc-cell rc-cell-edit" contenteditable="true"
x-init="$el.textContent = goals.horizon_text || ''"
@blur="saveGoalsField('horizon_text', $event.target)" @focus="editStart($event)"></td>
<td class="rc-cell rc-cell-edit" contenteditable="true"
x-init="$el.textContent = goals.mid_text || ''"
@blur="saveGoalsField('mid_text', $event.target)" @focus="editStart($event)"></td>
<td class="rc-cell rc-cell-edit rc-cell-tagline" contenteditable="true"
x-init="$el.textContent = goals.tagline || ''"
@blur="saveGoalsField('tagline', $event.target)" @focus="editStart($event)"></td>
</tr>
<tr class="rc-row rc-row-head">
<td class="rc-logo-cell"></td>
<td class="rc-cell rc-th">Deal</td>
<td class="rc-cell rc-th rc-num">Marge Month</td>
<td class="rc-cell rc-th rc-num">Marge Year</td>
<td class="rc-cell rc-th">Next steps</td>
</tr>
<template x-for="row in sheetRows" :key="row.id || row.source_row">
<tr class="rc-row rc-data-row"
:class="['rc-style-' + row.row_style, { 'rc-selected': selectedId === row.id }]"
@click="selectRow(row)">
<template x-if="row.logoCell === 'cucina'">
<td class="rc-logo-cell" :rowspan="row.logoSpan">
<div class="rc-logo rc-logo-cucina"><span class="rc-logo-ring">CUCINA<br>HALAL<br>FOOD</span></div>
</td>
</template>
<template x-if="row.logoCell === 'foodlinkk'">
<td class="rc-logo-cell" :rowspan="row.logoSpan">
<div class="rc-logo rc-logo-foodlinkk">
<span class="rc-fl-brand">Foodlinkk</span>
<span class="rc-fl-sub">FOOD MARKETING AGENCY</span>
</div>
</td>
</template>
<td class="rc-cell rc-cell-deal rc-cell-edit" contenteditable="true"
x-init="$el.textContent = row.name || ''"
@blur="saveProjectField(row, 'name', $event.target)" @focus="editStart($event)" @click.stop></td>
<td class="rc-cell rc-cell-num rc-cell-edit" contenteditable="true"
x-init="$el.textContent = fmtNum(row.margin_month)"
@blur="saveProjectField(row, 'margin_month', $event.target)" @focus="editStart($event)" @click.stop></td>
<td class="rc-cell rc-cell-num rc-cell-edit" contenteditable="true"
x-init="$el.textContent = fmtNum(row.margin_year)"
@blur="saveProjectField(row, 'margin_year', $event.target)" @focus="editStart($event)" @click.stop></td>
<td class="rc-cell rc-cell-steps rc-cell-edit" contenteditable="true"
x-init="$el.textContent = row.next_steps || ''"
@blur="saveProjectField(row, 'next_steps', $event.target)" @focus="editStart($event)" @click.stop></td>
</tr>
</template>
</table>
</div>
<div class="rc-row-panel" x-show="selectedRow" x-cloak @click.outside="selectedId = null">
<div class="rc-row-panel-head">
<strong x-text="selectedRow?.name"></strong>
<select class="rc-style-select" :value="selectedRow?.row_style" @change="setRowStyle($event.target.value)">
<option value="green">Groen (live)</option>
<option value="red">Rood (inactief)</option>
<option value="orange">Oranje</option>
<option value="yellow">Geel (sectie)</option>
<option value="blue">Blauw</option>
<option value="white">Wit</option>
</select>
</div>
<div class="rc-row-panel-body">
<label>Agent taak
<div class="rc-inline">
<select x-model="taskForm.agent_name">
<template x-for="a in agents" :key="a"><option :value="a" x-text="a"></option></template>
</select>
<input type="text" x-model="taskForm.title" placeholder="Taak titel…" />
<button type="button" class="rc-btn rc-btn-primary" @click="assignTask()">→</button>
</div>
</label>
</div>
</div>
<p class="rc-footnote">Klik in een cel om te bewerken · bron: Excel op NAS</p>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="/static/js/revenue-cockpit.js?v=12"></script>
{% endblock %}