213 lines
8.2 KiB
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>
|