dudi/templates/app.html.twig
Simon Kühn eea6119e36 Add admin user list view for simon@kuehn.de
- ADMIN_EMAIL env var controls who has admin access
- GET /api/admin/users returns all users (id, email, username, registered);
  returns 403 for non-admins
- GET /api/me now includes is_admin flag
- Menu shows "Nutzer/Users/Użytkownicy" button for admins that opens a
  table with name, email, and registration date for all users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:06:14 +02:00

361 lines
15 KiB
Twig
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="de">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<meta name="theme-color" content="#f5f4f0"/>
<meta name="mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<title>Dudi</title>
<link rel="stylesheet" href="style.css?v={{ cssv }}"/>
</head>
<body>
<div class="main-wrap">
<div class="hdr">
<div>
<img src="logo.png" alt="Dudi" class="hdr-logo"/>
<div class="hdr-sub" id="tlbl"></div>
</div>
<span id="sw" class="sw">0.00s</span>
<div class="hdr-btns">
<button class="btn-menu" id="btnData">⋯</button>
<button class="btn-add" id="btnNew">+</button>
</div>
</div>
<div class="main" id="main"></div>
</div>
<div id="ov" style="display:none"></div>
<!-- ── Templates ──────────────────────────────────────────────────────────── -->
<template id="tpl-hint">
<div class="hint"><span data-t="hint"></span><button class="hclose">×</button></div>
</template>
<template id="tpl-empty">
<div class="empty"><div style="font-size:40px;opacity:.4;margin-bottom:12px">🎯</div><span data-t="emptyLine1"></span><br><span data-t="emptyLine2"></span></div>
</template>
<template id="tpl-dot">
<div></div>
</template>
<template id="tpl-nosets">
<div class="nosets" data-t="noEntry"></div>
</template>
<template id="tpl-set-row">
<div class="set-row">
<span></span>
<button class="sdel">×</button>
</div>
</template>
<template id="tpl-add-row">
<div class="add-row">
<input class="num-in" type="number" min="1"/>
<span class="ulbl"></span>
<button class="btn-sw-fill" style="display:none">⏱</button>
<button class="btn-as" data-t="log"></button>
</div>
</template>
<template id="tpl-panel">
<div class="dpanel">
<div class="dpanel-hdr">
<span class="dpanel-title"></span>
<span class="dpanel-sub"></span>
</div>
<div class="dpanel-body"></div>
</div>
</template>
<template id="tpl-qb-row">
<div class="qb-row">
<div class="qb-name"></div>
<div class="qb-stat"></div>
<input class="num-in" type="number" min="1"/>
<button class="btn-sw-fill" style="display:none">⏱</button>
<button class="btn-as" data-t="log"></button>
</div>
</template>
<template id="tpl-name-view">
<div class="name-wrap">
<div class="goal-name"></div>
<button class="btn-ren">✎</button>
</div>
</template>
<template id="tpl-name-edit">
<div class="name-wrap">
<input class="ren-input" type="text"/>
</div>
</template>
<template id="tpl-card-collapsed">
<div class="card">
<div class="card-hdr">
<div class="card-bd" style="flex:1;min-width:0">
<div class="goal-meta"><span data-t="noch"></span> <span class="m-dr"></span><span data-t="dAbbr"></span> · <span data-t="endet"></span> <span class="m-end"></span> · <span data-t="todayShort"></span>: <span class="m-heute"></span><br><span data-t="total"></span>: <span class="m-total"></span></div>
</div>
<span class="badge"></span>
<span class="chevron">▸</span>
</div>
<div style="padding:0 16px 12px">
<div class="prog-track"><div class="prog-fill"></div></div>
</div>
</div>
</template>
<template id="tpl-card-expanded">
<div class="card">
<div class="card-hdr">
<div class="card-bd" style="flex:1;min-width:0">
<div class="goal-meta"><span data-t="noch"></span> <span class="m-dr"></span><span data-t="dAbbr"></span> · <span data-t="endet"></span> <span class="m-end"></span></div>
</div>
<span class="badge"></span>
<span class="chevron">▴</span>
</div>
<div class="prog-wrap">
<div class="prog-track"><div class="prog-fill"></div></div>
<div class="prog-row">
<span class="pr-done"></span>
<span class="pr-pct"></span>
</div>
</div>
<div class="heute-stats">
<div class="heute-group">
<div class="heute-lbl" data-t="todayHeading"></div>
<div class="heute-inner">
<div class="stat">
<div class="slbl" data-t="done2"></div>
<div class="sval"><span class="sv-tdone"></span><div class="sunit"></div></div>
</div>
<div class="stat">
<div class="slbl" data-t="dailyGoal"></div>
<div class="sval"><span class="sv-daily"></span><div class="sunit"></div></div>
</div>
<div class="stat">
<div class="slbl" data-t="remaining"></div>
<div class="sval sv-noch"><span class="sv-st"></span><div class="sunit"></div></div>
</div>
</div>
</div>
</div>
<div class="dots-sec">
<div class="dots-lbl" data-t="history"></div>
<div class="dots-wrap"></div>
<div class="legend">
<span class="leg"><span class="ldot" style="background:rgba(37,99,235,.3)"></span><span data-t="legBuf"></span></span>
<span class="leg"><span class="ldot" style="background:rgba(22,163,74,.3)"></span><span data-t="legDone"></span></span>
<span class="leg"><span class="ldot" style="background:rgba(217,119,6,.3)"></span><span data-t="legPartial"></span></span>
<span class="leg"><span class="ldot" style="background:rgba(220,38,38,.3)"></span><span data-t="legMissed"></span></span>
</div>
</div>
<div class="card-foot"><button class="btn-del" data-t="delGoal"></button></div>
</div>
</template>
<template id="tpl-sheet">
<div class="sheet">
<div class="shandle"></div>
</div>
</template>
<template id="tpl-login">
<div>
<div class="stitle" data-t="loginTitle"></div>
<div class="ssub">Dudi</div>
<div class="login-err" style="display:none"></div>
<div class="ff"><label data-t="emailLabel"></label><input class="fi lf-email" type="email" autocomplete="email"/></div>
<div class="ff"><label data-t="passwordLabel"></label><input class="fi lf-pass" type="password" autocomplete="current-password"/></div>
<div class="factions"><button class="btn-p lf-sub" data-t="loginBtn"></button></div>
<div style="text-align:center;margin-top:8px"><button class="btn-lnk lf-fgt" data-t="forgotPw"></button></div>
</div>
</template>
<template id="tpl-forgot-pw">
<div>
<div class="stitle" data-t="forgotTitle"></div>
<div class="ssub" data-t="forgotSub"></div>
<div class="ff"><label data-t="emailLabel"></label><input class="fi fp-email" type="email" autocomplete="email"/></div>
<div class="login-err" style="display:none"></div>
<div class="factions">
<button class="btn-p fp-sub" data-t="sendLink"></button>
<button class="btn-c fp-back" data-t="back"></button>
</div>
</div>
</template>
<template id="tpl-email-sent">
<div>
<div class="stitle" data-t="emailSentTitle"></div>
<div class="ssub" data-t="emailSentSub"></div>
<div class="factions"><button class="btn-p es-ok" data-t="ok"></button></div>
</div>
</template>
<template id="tpl-reset-pw">
<div>
<div class="stitle" data-t="resetTitle"></div>
<div class="ff"><label data-t="newPwLabel"></label><input class="fi rp-pass" type="password" autocomplete="new-password" data-ph="min8"/></div>
<div class="login-err" style="display:none"></div>
<div class="factions"><button class="btn-p rp-sub" data-t="setPw"></button></div>
</div>
</template>
<template id="tpl-pw-changed">
<div>
<div class="stitle" data-t="pwChangedTitle"></div>
<div class="ssub" data-t="pwChangedSub"></div>
<div class="factions"><button class="btn-p pc-ok" data-t="loginBtn"></button></div>
</div>
</template>
<template id="tpl-change-name">
<div>
<div class="stitle" data-t="changeNameTitle"></div>
<div class="ff"><label data-t="yourName"></label><input class="fi cn-name" type="text" autocomplete="name"/></div>
<div class="login-err" style="display:none"></div>
<div class="factions">
<button class="btn-p cn-sub" data-t="save"></button>
<button class="btn-c cn-can" data-t="cancel"></button>
</div>
</div>
</template>
<template id="tpl-change-pw">
<div>
<div class="stitle" data-t="changePwTitle"></div>
<div class="ff"><label data-t="currentPwLabel"></label><input class="fi cp-old" type="password" autocomplete="current-password"/></div>
<div class="ff"><label data-t="newPwLabel"></label><input class="fi cp-new" type="password" autocomplete="new-password" data-ph="min8"/></div>
<div class="ff"><label data-t="newPwConfLabel"></label><input class="fi cp-new2" type="password" autocomplete="new-password" data-ph="min8"/></div>
<div class="login-err" style="display:none"></div>
<div class="factions">
<button class="btn-p cp-sub" data-t="changePwBtn"></button>
<button class="btn-c cp-can" data-t="cancel"></button>
</div>
</div>
</template>
<template id="tpl-register">
<div>
<div class="stitle" data-t="registerTitle"></div>
<div class="ssub" data-t="registerSub"></div>
<div class="ff"><label data-t="yourName"></label><input class="fi rg-name" type="text" autocomplete="name" data-ph="namePlaceholder"/></div>
<div class="ff"><label data-t="emailLabel"></label><input class="fi rg-email" type="email" autocomplete="email"/></div>
<div class="ff"><label data-t="passwordLabel"></label><input class="fi rg-pass" type="password" autocomplete="new-password" data-ph="min8"/></div>
<div class="ff"><label data-t="pwConfLabel"></label><input class="fi rg-pass2" type="password" autocomplete="new-password" data-ph="pwPlaceholder"/></div>
<div class="login-err" style="display:none"></div>
<div class="factions"><button class="btn-p rg-sub" data-t="registerBtn"></button></div>
</div>
</template>
<template id="tpl-new-goal">
<div>
<div class="stitle" data-t="newGoalTitle"></div>
<div class="ff"><label data-t="exerciseLabel"></label><input class="fi ng-name" type="text" data-ph="exercisePlaceholder"/></div>
<div class="fgrid">
<div class="ff"><label data-t="unitLabel"></label><input class="fi ng-unit" type="text" data-val="unitDefault"/></div>
<div class="ff"><label data-t="daysLabel"></label><input class="fi ng-days" type="number" min="7" max="365" value="30"/></div>
</div>
<div class="fgrid">
<div class="ff"><label data-t="dailyLabel"></label><input class="fi ng-daily" type="number" min="0.01" step="any" value="50"/></div>
<div class="ff"><label data-t="weeklyLabel"></label><input class="fi ng-weekly" type="number" min="0.01" step="any" value="350"/></div>
</div>
<div class="factions">
<button class="btn-p ng-sub" data-t="startGoal"></button>
<button class="btn-c ng-can" data-t="cancel"></button>
</div>
</div>
</template>
<template id="tpl-data-menu">
<div>
<div class="stitle" data-t="dataMenuTitle"></div>
<div class="ssub" data-t="dataMenuSub"></div>
<button class="dbtn dm-exp"><span class="dico">⬇</span><span class="dlbl"><span data-t="exportLabel"></span><span class="dsub" data-t="exportSub"></span></span></button>
<button class="dbtn dm-imp"><span class="dico">⬆</span><span class="dlbl"><span data-t="importLabel"></span><span class="dsub" data-t="importSub"></span></span></button>
<div class="ddiv"></div>
<button class="dbtn dm-inv"><span class="dico">🔗</span><span class="dlbl"><span data-t="inviteLabel"></span><span class="dsub" data-t="inviteSub"></span></span></button>
<button class="dbtn dm-invlist"><span class="dico">📋</span><span class="dlbl"><span data-t="inviteListLabel"></span><span class="dsub" data-t="inviteListSub"></span></span></button>
<button class="dbtn dm-admin" style="display:none"><span class="dico">👥</span><span class="dlbl" data-t="adminLabel"></span></button>
<div class="ddiv"></div>
<button class="dbtn dm-name"><span class="dico">✏️</span><span class="dlbl" data-t="changeName"></span></button>
<button class="dbtn dm-cpw"><span class="dico">🔑</span><span class="dlbl" data-t="changePw"></span></button>
<button class="dbtn dm-lgout"><span class="dico">→</span><span class="dlbl" data-t="logout"></span></button>
<div class="ddiv"></div>
<div class="lang-row">
<button class="btn-lang" data-lang="de">DE</button>
<button class="btn-lang" data-lang="en">EN</button>
<button class="btn-lang" data-lang="pl">PL</button>
</div>
<button class="btn-c dm-cls" style="width:100%;margin-top:4px;text-align:center" data-t="close"></button>
<div class="ddiv" style="margin-top:16px"></div>
<button class="dbtn ddanger dm-clr"><span class="dico">✕</span><span class="dlbl"><span data-t="clearAll"></span><span class="dsub" data-t="clearAllSub"></span></span></button>
</div>
</template>
<template id="tpl-invite-form">
<div>
<div class="stitle" data-t="inviteFormTitle"></div>
<div class="ssub" data-t="inviteFormSub"></div>
<div class="ff"><label data-t="inviteNameLabel"></label><input class="fi inv-name" type="text" data-ph="inviteNamePlaceholder"/></div>
<div class="factions">
<button class="btn-p inv-gen" data-t="generateLink"></button>
<button class="btn-c inv-cancel" data-t="cancel"></button>
</div>
</div>
</template>
<template id="tpl-invite-link">
<div>
<div class="stitle"></div>
<div class="ssub" data-t="inviteFormSub"></div>
<div class="ff"><input class="fi il-url" type="text" readonly/></div>
<div class="factions">
<button class="btn-p il-copy" data-t="copyLink"></button>
<button class="btn-c il-close" data-t="close"></button>
</div>
</div>
</template>
<template id="tpl-invite-list">
<div>
<div class="stitle" data-t="inviteListLabel"></div>
<div class="dpanel-body"></div>
<div class="factions"><button class="btn-c il-close" data-t="close"></button></div>
</div>
</template>
<template id="tpl-invite-row">
<div class="set-row">
<span style="flex:1"><strong class="ir-label"></strong><span class="ir-detail" style="opacity:.6;font-size:.85em"></span></span>
<button class="ir-copy btn-lnk" style="font-size:.8em;display:none">🔗 <span data-t="linkLabel"></span></button>
<span class="ir-status" style="font-size:.85em;font-weight:600"></span>
</div>
</template>
<template id="tpl-admin-users">
<div>
<div class="stitle" data-t="adminLabel"></div>
<div class="dpanel-body" style="overflow-x:auto;padding:0">
<table style="width:100%;border-collapse:collapse;font-size:13px">
<thead>
<tr style="border-bottom:1px solid var(--border)">
<th style="padding:8px 12px;text-align:left;font-weight:600" data-t="adminColName"></th>
<th style="padding:8px 12px;text-align:left;font-weight:600" data-t="adminColEmail"></th>
<th style="padding:8px 12px;text-align:left;font-weight:600;color:var(--text2)" data-t="adminColRegistered"></th>
</tr>
</thead>
<tbody class="au-body"></tbody>
</table>
</div>
<div class="factions"><button class="btn-c au-close" data-t="close"></button></div>
</div>
</template>
<script src="app.js?v={{ jsv }}"></script>
</body>
</html>