bowler-chart/public/index.html

213 lines
8.2 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bowler Chart</title>
<link rel="stylesheet" href="/vendor/beer.min.css" />
<link rel="stylesheet" href="/css/app.css" />
<script src="/vendor/material-dynamic-colors.min.js"></script>
<script src="/vendor/beer.min.js"></script>
<script src="/js/api.js"></script>
<script src="/js/theme.js"></script>
<script src="/js/app.js" defer></script>
<script src="/vendor/alpine.min.js" defer></script>
</head>
<body class="light" x-data="bowlerApp()" x-init="init()" x-cloak>
<!-- Top app bar -->
<nav class="top fill">
<i>insights</i>
<h6 class="app-bar-title max">Bowler Chart</h6>
<div class="field label suffix small border" style="margin: 0 0.5rem; width: 7rem;">
<select x-model.number="year" @change="loadChart()">
<template x-for="y in years" :key="y"><option :value="y" x-text="y"></option></template>
</select>
<label>Year</label>
</div>
<div class="field label suffix small border" style="margin: 0 0.5rem; width: 11rem;"
x-show="schemes.length">
<select @change="changeScheme($event.target.value)">
<template x-for="s in schemes" :key="s.id">
<option :value="s.id" :selected="s.id === me?.preferred_scheme_id" x-text="s.name"></option>
</template>
</select>
<label>Theme</label>
</div>
<button class="circle transparent" x-show="isAdmin" @click="location.href='/admin.html'" title="Admin">
<i>admin_panel_settings</i>
</button>
<button class="chip" @click="logout()">
<i>logout</i>
<span x-text="me?.username"></span>
</button>
</nav>
<main class="responsive">
<!-- Toolbar -->
<div class="row middle-align" style="margin: 1rem 0;">
<h5 class="max">KPI Status — <span x-text="year"></span></h5>
<button @click="openNewProject()">
<i>add</i><span>New project</span>
</button>
</div>
<progress class="circle large" x-show="loading"></progress>
<div class="bowler-wrap" x-show="!loading">
<article class="border round" x-show="!projects.length">
<p class="center-align muted">No projects yet. Create one to start tracking KPIs.</p>
</article>
<table class="bowler" x-show="projects.length">
<thead>
<tr>
<th class="kpi-col">KPI</th>
<th>Goal</th>
<template x-for="m in months" :key="m"><th x-text="m"></th></template>
<th>Trend</th>
<th></th>
</tr>
</thead>
<tbody>
<template x-for="p in projects" :key="p.id">
<template>
<!-- Project header row -->
<tr class="project-row">
<td :colspan="months.length + 4">
<div class="row middle-align">
<span class="max">
<i class="small">folder</i>
<span x-text="p.name"></span>
<span class="muted small-text">· <span x-text="p.owner_name"></span></span>
</span>
<template x-if="p.can_edit">
<span class="nowrap">
<button class="circle small transparent" @click="openNewKpi(p)" title="Add KPI"><i>add</i></button>
<button class="circle small transparent" @click="openEditProject(p)" title="Edit project"><i>edit</i></button>
<button class="circle small transparent" @click="deleteProject(p)" title="Delete project"><i>delete</i></button>
</span>
</template>
</div>
</td>
</tr>
<!-- KPI rows -->
<template x-for="k in p.kpis" :key="k.id">
<tr>
<td class="kpi-col">
<strong x-text="k.name"></strong>
<template x-if="k.unit"><span class="muted small-text"> (<span x-text="k.unit"></span>)</span></template>
</td>
<td class="muted small-text" x-text="k.goal_text || '—'"></td>
<template x-for="m in k.months" :key="m.month">
<td class="cell" :class="[m.status, p.can_edit ? 'editable' : '', m.note ? 'has-note' : '']"
@click="openCell(p, k, m)" :title="m.note || ''">
<span class="cell-value" x-text="m.value ?? ''"></span>
</td>
</template>
<td class="trend-col">
<template x-if="sparkline(k)">
<svg width="88" height="24" viewBox="0 0 88 24">
<polyline :points="sparkline(k)" fill="none"
stroke="var(--primary, #6750a4)" stroke-width="2"
stroke-linejoin="round" stroke-linecap="round" />
</svg>
</template>
</td>
<td>
<template x-if="p.can_edit">
<span class="nowrap">
<button class="circle small transparent" @click="openEditKpi(p, k)" title="Edit KPI"><i>edit</i></button>
<button class="circle small transparent" @click="deleteKpi(k)" title="Delete KPI"><i>delete</i></button>
</span>
</template>
</td>
</tr>
</template>
</template>
</template>
</tbody>
</table>
</div>
</main>
<!-- Cell edit dialog -->
<dialog class="modal" x-ref="cellDialog">
<h6><span x-text="cell.kpiName"></span> — <span x-text="months[cell.month - 1]"></span> <span x-text="year"></span></h6>
<div class="field label border round">
<input type="text" x-model="cell.value" />
<label>Value</label>
</div>
<div class="field label border round">
<select x-model="cell.status">
<template x-for="o in statusOptions" :key="o.value">
<option :value="o.value" x-text="o.label"></option>
</template>
</select>
<label>Status</label>
</div>
<div class="field label border round textarea">
<textarea x-model="cell.note"></textarea>
<label>Note</label>
</div>
<nav class="right-align">
<button class="border" @click="$refs.cellDialog.close()">Cancel</button>
<button @click="saveCell()"><i>save</i><span>Save</span></button>
</nav>
</dialog>
<!-- Project dialog -->
<dialog class="modal" x-ref="projectDialog">
<h6 x-text="proj.id ? 'Edit project' : 'New project'"></h6>
<div class="field label border round">
<input type="text" x-model="proj.name" />
<label>Name</label>
</div>
<div class="field label border round textarea">
<textarea x-model="proj.description"></textarea>
<label>Description</label>
</div>
<nav class="right-align">
<button class="border" @click="$refs.projectDialog.close()">Cancel</button>
<button @click="saveProject()"><i>save</i><span>Save</span></button>
</nav>
</dialog>
<!-- KPI dialog -->
<dialog class="modal" x-ref="kpiDialog">
<h6 x-text="kpi.id ? 'Edit KPI' : 'New KPI'"></h6>
<div class="field label border round">
<input type="text" x-model="kpi.name" />
<label>Name</label>
</div>
<div class="grid">
<div class="field label border round s6">
<input type="text" x-model="kpi.unit" />
<label>Unit</label>
</div>
<div class="field label border round s6">
<input type="text" x-model="kpi.goal_text" />
<label>Goal</label>
</div>
</div>
<div class="field label border round textarea">
<textarea x-model="kpi.description"></textarea>
<label>Description</label>
</div>
<nav class="right-align">
<button class="border" @click="$refs.kpiDialog.close()">Cancel</button>
<button @click="saveKpi()"><i>save</i><span>Save</span></button>
</nav>
</dialog>
<!-- Snackbar for errors -->
<div class="snackbar error" id="snackbar"><span x-text="error"></span></div>
<style>[x-cloak]{display:none!important;}</style>
</body>
</html>