init
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
{{define "admin.html"}}
|
||||
{{template "shell-start" .}}
|
||||
<header class="page-head">
|
||||
<div><p class="eyebrow">ADMIN</p><h1>Team approvals</h1><p>Review time-off and remote-day requests.</p></div>
|
||||
<div class="filter-tabs"><a href="/admin/requests" class="active">All</a><a href="/admin/requests?status=pending">Pending</a><a href="/admin/requests?status=approved">Approved</a></div>
|
||||
</header>
|
||||
{{template "notice" .}}
|
||||
<section class="card approval-card">
|
||||
{{if .Requests}}
|
||||
<div class="request-list">
|
||||
{{range .Requests}}
|
||||
<article class="approval-row">
|
||||
<span class="avatar small">{{initial .UserName}}</span>
|
||||
<div class="approval-main">
|
||||
<div class="approval-title"><strong>{{.UserName}}</strong><span class="badge kind">{{kindLabel .Kind}}</span><span class="badge {{.Status}}">{{statusLabel .Status}}</span></div>
|
||||
<p>{{dateFA .StartDate}}{{if ne .StartDate .EndDate}} — {{dateFA .EndDate}}{{end}} · {{.DayCount}} day(s)</p>
|
||||
{{if .Reason}}<blockquote>“{{.Reason}}”</blockquote>{{end}}
|
||||
{{if eq .Status "pending"}}
|
||||
<form method="post" action="/admin/requests/{{.ID}}/review" class="review-form">
|
||||
<input type="hidden" name="csrf" value="{{$.CSRF}}">
|
||||
<input name="note" maxlength="500" placeholder="Optional note to teammate">
|
||||
<button class="button approve" name="decision" value="approved">Approve</button>
|
||||
<button class="button reject" name="decision" value="rejected">Reject</button>
|
||||
</form>
|
||||
{{else if .AdminNote}}<p class="admin-note">Review note: {{.AdminNote}}</p>{{end}}
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}<div class="empty roomy"><span>✓</span><h3>All clear</h3><p>No requests match this view.</p></div>{{end}}
|
||||
</section>
|
||||
{{template "shell-end" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,84 @@
|
||||
{{define "head"}}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>{{.Title}} · Hamkar</title>
|
||||
<script src="/static/theme.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css">
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
<script src="/static/jalali-picker.js" defer></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
{{end}}
|
||||
|
||||
{{define "theme-toggle"}}
|
||||
<button type="button" class="theme-toggle" data-theme-toggle aria-label="Switch to dark mode" title="Change color mode">
|
||||
<span class="theme-sun" aria-hidden="true">☀</span>
|
||||
<span class="theme-moon" aria-hidden="true">☾</span>
|
||||
</button>
|
||||
{{end}}
|
||||
|
||||
{{define "sidebar"}}
|
||||
<aside class="sidebar">
|
||||
<a class="brand" href="/">
|
||||
<span class="brand-mark">ه</span>
|
||||
<span>Hamkar<small>Team presence</small></span>
|
||||
</a>
|
||||
<nav>
|
||||
<a href="/" class="{{if eq .SelectedSection "dashboard"}}active{{end}}">
|
||||
<span class="nav-icon">⌂</span> Overview
|
||||
</a>
|
||||
<a href="/day" class="{{if eq .SelectedSection "day"}}active{{end}}">
|
||||
<span class="nav-icon">◉</span> Team day
|
||||
</a>
|
||||
<a href="/requests" class="{{if eq .SelectedSection "requests"}}active{{end}}">
|
||||
<span class="nav-icon">◫</span> My requests
|
||||
</a>
|
||||
{{if eq .User.Role "admin"}}
|
||||
<p class="nav-label">ADMIN</p>
|
||||
<a href="/admin/requests" class="{{if eq .SelectedSection "admin"}}active{{end}}">
|
||||
<span class="nav-icon">✓</span> Approvals
|
||||
{{if .PendingCount}}<span class="nav-count">{{.PendingCount}}</span>{{end}}
|
||||
</a>
|
||||
<a href="/admin/users" class="{{if eq .SelectedSection "users"}}active{{end}}">
|
||||
<span class="nav-icon">◎</span> Teammates
|
||||
</a>
|
||||
<a href="/reports" class="{{if eq .SelectedSection "reports"}}active{{end}}">
|
||||
<span class="nav-icon">↧</span> Reports
|
||||
</a>
|
||||
{{end}}
|
||||
</nav>
|
||||
<div class="sidebar-theme">{{template "theme-toggle" .}}<span>Appearance</span></div>
|
||||
<div class="sidebar-user">
|
||||
<span class="avatar">{{initial .User.DisplayName}}</span>
|
||||
<span><strong>{{.User.DisplayName}}</strong><small>{{.User.Role}}</small></span>
|
||||
<form method="post" action="/logout">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
||||
<button class="icon-button" title="Sign out" aria-label="Sign out">↗</button>
|
||||
</form>
|
||||
</div>
|
||||
</aside>
|
||||
{{end}}
|
||||
|
||||
{{define "shell-start"}}
|
||||
{{template "head" .}}
|
||||
<div class="app-shell">
|
||||
{{template "sidebar" .}}
|
||||
<main class="main">
|
||||
{{end}}
|
||||
|
||||
{{define "shell-end"}}
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
{{define "notice"}}
|
||||
{{if .Flash}}<div class="notice success">{{.Flash}}</div>{{end}}
|
||||
{{if .Error}}<div class="notice error">{{.Error}}</div>{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,35 @@
|
||||
{{define "calendar.html"}}
|
||||
<section id="calendar-panel" class="card calendar-card">
|
||||
<header class="calendar-head">
|
||||
<div>
|
||||
<p class="eyebrow">PERSIAN CALENDAR</p>
|
||||
<h2>{{.Calendar.MonthName}} <span>{{.Calendar.Year}}</span></h2>
|
||||
<p class="fa-month" lang="fa" dir="rtl">{{.Calendar.MonthNameFA}} {{.Calendar.Year}}</p>
|
||||
</div>
|
||||
<div class="calendar-nav">
|
||||
<a aria-label="Previous month" href="/?month={{.Calendar.Prev}}" hx-get="/calendar?month={{.Calendar.Prev}}" hx-target="#calendar-panel" hx-swap="outerHTML">‹</a>
|
||||
<a aria-label="Next month" href="/?month={{.Calendar.Next}}" hx-get="/calendar?month={{.Calendar.Next}}" hx-target="#calendar-panel" hx-swap="outerHTML">›</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="calendar-grid weekdays">
|
||||
<span>Sat</span><span>Sun</span><span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span>
|
||||
</div>
|
||||
<div class="calendar-grid days">
|
||||
{{range .Calendar.Cells}}
|
||||
{{if .InMonth}}
|
||||
<a href="/day?date={{.Gregorian}}" class="day {{if .IsToday}}today{{end}} {{if .IsFriday}}friday{{end}} {{if .Holiday}}holiday{{end}}" title="View team details for this day">
|
||||
<span class="day-number">{{.Day}}</span>
|
||||
{{if .Status}}<i class="status-dot {{.Status}}" title="{{.Status}}"></i>{{end}}
|
||||
{{if .Holiday}}<small title="{{.Holiday}}">{{.Holiday}}</small>{{end}}
|
||||
</a>
|
||||
{{else}}<div class="day blank"></div>{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
<footer class="calendar-legend">
|
||||
<span><i class="status-dot office"></i> Office</span>
|
||||
<span><i class="status-dot remote"></i> Remote</span>
|
||||
<span><i class="status-dot leave"></i> Time off</span>
|
||||
<span><i class="holiday-mark"></i> Holiday</span>
|
||||
</footer>
|
||||
</section>
|
||||
{{end}}
|
||||
@@ -0,0 +1,63 @@
|
||||
{{define "dashboard.html"}}
|
||||
{{template "shell-start" .}}
|
||||
<header class="page-head">
|
||||
<div><p class="eyebrow">TEAM WORKSPACE</p><h1>Good day, {{.User.DisplayName}}</h1><p>Here’s your month at a glance.</p></div>
|
||||
<div class="today-pill"><span>Today</span><strong>{{.TodayJalali}}</strong></div>
|
||||
</header>
|
||||
{{template "notice" .}}
|
||||
<div class="dashboard-grid">
|
||||
<section class="card presence-card">
|
||||
<div class="presence-top">
|
||||
<div><p class="eyebrow">TODAY’S PRESENCE</p><h2>{{if .Attendance}}{{if .Attendance.CheckOut.Valid}}Day complete{{else}}You’re checked in{{end}}{{else}}Ready when you are{{end}}</h2></div>
|
||||
<span class="live-badge"><i></i> {{if and .Attendance (not .Attendance.CheckOut.Valid)}}ACTIVE{{else}}TODAY{{end}}</span>
|
||||
</div>
|
||||
{{if .Attendance}}
|
||||
<div class="time-line">
|
||||
<div><small>CHECKED IN</small><strong>{{timeHM .Attendance.CheckIn}}</strong></div>
|
||||
<span></span>
|
||||
<div><small>CHECKED OUT</small><strong>{{timeHM .Attendance.CheckOut}}</strong></div>
|
||||
<div><small>LOCATION</small><strong>{{if eq .Attendance.Mode "remote"}}Remote{{else}}Office{{end}}</strong></div>
|
||||
</div>
|
||||
{{if not .Attendance.CheckOut.Valid}}
|
||||
<form method="post" action="/attendance/check-out">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
||||
<button class="button dark full">Check out</button>
|
||||
</form>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<p class="muted">Start your day by choosing where you’re working.</p>
|
||||
<div class="checkin-actions">
|
||||
<form method="post" action="/attendance/check-in">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}"><input type="hidden" name="mode" value="office">
|
||||
<button class="button primary">Check in at office</button>
|
||||
</form>
|
||||
<form method="post" action="/attendance/check-in">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}"><input type="hidden" name="mode" value="remote">
|
||||
<button class="button secondary">Working remotely</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
<section class="stats-grid">
|
||||
<div class="stat card"><span class="stat-icon mint">✓</span><small>PRESENT DAYS</small><strong>{{.Stats.Present}}</strong><p>This month</p></div>
|
||||
<div class="stat card"><span class="stat-icon blue">⌂</span><small>REMOTE DAYS</small><strong>{{.Stats.Remote}}</strong><p>This month</p></div>
|
||||
<div class="stat card"><span class="stat-icon sand">☼</span><small>TIME OFF</small><strong>{{.Stats.Leave}}</strong><p>Approved days</p></div>
|
||||
</section>
|
||||
{{template "calendar.html" .}}
|
||||
<section class="card recent-card">
|
||||
<header class="section-head"><div><p class="eyebrow">RECENT ACTIVITY</p><h2>Your requests</h2></div><a href="/requests">View all →</a></header>
|
||||
{{if .Requests}}
|
||||
<div class="request-list compact">
|
||||
{{range .Requests}}
|
||||
<div class="request-row">
|
||||
<span class="request-icon {{.Kind}}">{{if eq .Kind "remote"}}⌂{{else}}☼{{end}}</span>
|
||||
<div><strong>{{kindLabel .Kind}}</strong><small>{{dateFA .StartDate}}{{if ne .StartDate .EndDate}} — {{dateFA .EndDate}}{{end}}</small></div>
|
||||
<span class="badge {{.Status}}">{{statusLabel .Status}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}<div class="empty"><span>◌</span><p>No requests yet.</p><a href="/requests">Make a request</a></div>{{end}}
|
||||
</section>
|
||||
</div>
|
||||
{{template "shell-end" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,56 @@
|
||||
{{define "day.html"}}
|
||||
{{template "shell-start" .}}
|
||||
<header class="page-head day-page-head">
|
||||
<div><p class="eyebrow">TEAM DAY</p><h1>Who’s working?</h1><p>See the team’s presence, remote work, and absences for one day.</p></div>
|
||||
<form method="get" action="/day" class="day-date-form">
|
||||
<label>Persian date
|
||||
<span class="jalali-field">
|
||||
<input name="date" value="{{.Day.Jalali}}" data-jalali-picker required autocomplete="off" inputmode="numeric" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
|
||||
<button type="button" class="jalali-trigger" aria-label="Choose team date" title="Open Persian calendar">▦</button>
|
||||
</span>
|
||||
</label>
|
||||
<button class="button primary" type="submit">View day</button>
|
||||
</form>
|
||||
</header>
|
||||
{{template "notice" .}}
|
||||
<div class="day-layout">
|
||||
<section class="card day-summary-card">
|
||||
<div class="day-date-nav">
|
||||
<a href="/day?date={{.Day.Prev}}" aria-label="Previous day">‹</a>
|
||||
<div>
|
||||
<p class="eyebrow">PERSIAN CALENDAR</p>
|
||||
<h2>{{.Day.Jalali}}</h2>
|
||||
<span>{{.Day.Weekday}} · {{.Day.Gregorian}}{{if .Day.DayNote}} · {{.Day.DayNote}}{{end}}</span>
|
||||
</div>
|
||||
<a href="/day?date={{.Day.Next}}" aria-label="Next day">›</a>
|
||||
</div>
|
||||
<div class="day-stats">
|
||||
<div><i class="presence-mark present"></i><span><strong>{{.Day.Present}}</strong><small>Present</small></span></div>
|
||||
<div><i class="presence-mark remote"></i><span><strong>{{.Day.Remote}}</strong><small>Remote</small></span></div>
|
||||
<div><i class="presence-mark absent"></i><span><strong>{{.Day.Absent}}</strong><small>Absent</small></span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card roster-card">
|
||||
<header class="section-head">
|
||||
<div><p class="eyebrow">TEAM ROSTER</p><h2>{{len .Day.Members}} teammates</h2></div>
|
||||
<div class="roster-legend"><span><i class="presence-mark present"></i> Present</span><span><i class="presence-mark remote"></i> Remote</span><span><i class="presence-mark absent"></i> Absent</span></div>
|
||||
</header>
|
||||
{{if .Day.Members}}
|
||||
<div class="roster-list">
|
||||
{{range .Day.Members}}
|
||||
<article class="roster-row">
|
||||
{{if .User.AvatarURL}}<img class="avatar" src="{{.User.AvatarURL}}" alt="">{{else}}<span class="avatar">{{initial .User.DisplayName}}</span>{{end}}
|
||||
<div class="roster-person"><strong>{{.User.DisplayName}}</strong><small>@{{.User.Username}}</small></div>
|
||||
<div class="roster-detail"><span>{{.Detail}}</span>{{if .CheckIn}}<small>{{if .CheckOut}}Completed{{else}}Active{{end}}</small>{{end}}</div>
|
||||
<span class="presence-badge {{.Status}}"><i class="presence-mark {{.Status}}"></i>{{.Label}}</span>
|
||||
</article>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty roomy"><span>◌</span><h3>No teammates yet</h3><p>Add teammates to see the daily roster.</p></div>
|
||||
{{end}}
|
||||
</section>
|
||||
</div>
|
||||
{{template "shell-end" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,44 @@
|
||||
{{define "login.html"}}
|
||||
{{template "head" .}}
|
||||
<main class="login-page">
|
||||
<div class="auth-theme">{{template "theme-toggle" .}}</div>
|
||||
<section class="login-story">
|
||||
<a class="brand light" href="/">
|
||||
<span class="brand-mark">ه</span>
|
||||
<span>Hamkar<small>Team presence</small></span>
|
||||
</a>
|
||||
<div class="story-copy">
|
||||
<p class="eyebrow">A lighter way to work together</p>
|
||||
<h1>Know who’s here.<br>Plan what’s next.</h1>
|
||||
<p>Presence, time off, and remote days—organized around the Persian calendar your team actually uses.</p>
|
||||
</div>
|
||||
<div class="story-calendar" aria-hidden="true">
|
||||
<div><small>امروز</small><strong>۶ مرداد</strong><span>۱۴۰۵</span></div>
|
||||
<div class="people-dots"><i></i><i></i><i></i><b>+8</b></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="login-form-wrap">
|
||||
<form class="login-card" method="post" action="/login">
|
||||
<div class="mobile-brand"><span class="brand-mark">ه</span> Hamkar</div>
|
||||
<p class="eyebrow">WELCOME BACK</p>
|
||||
<h2>Sign in to your workspace</h2>
|
||||
<p class="muted">Use your email, username, or company SSO.</p>
|
||||
{{if .Error}}<div class="notice error">{{.Error}}</div>{{end}}
|
||||
<label>Email or username<input name="identifier" autocomplete="username" required autofocus placeholder="you@example.com or navid"></label>
|
||||
<label>Password<input type="password" name="password" autocomplete="current-password" required placeholder="••••••••"></label>
|
||||
<button class="button primary full" type="submit">Sign in</button>
|
||||
{{if or .OAuthGitHub .OAuthGoogle}}
|
||||
<div class="separator"><span>or continue with</span></div>
|
||||
<div class="oauth-row">
|
||||
{{if .OAuthGoogle}}<a class="button secondary" href="/auth/google">Google</a>{{end}}
|
||||
{{if .OAuthGitHub}}<a class="button secondary" href="/auth/github">GitHub</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<p class="auth-switch">New to Hamkar? <a href="/register">Create an account</a></p>
|
||||
<p class="login-hint">Demo admin: <code>admin</code> / <code>admin123</code></p>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
@@ -0,0 +1,51 @@
|
||||
{{define "register.html"}}
|
||||
{{template "head" .}}
|
||||
<main class="login-page">
|
||||
<div class="auth-theme">{{template "theme-toggle" .}}</div>
|
||||
<section class="login-story register-story">
|
||||
<a class="brand light" href="/">
|
||||
<span class="brand-mark">ه</span>
|
||||
<span>Hamkar<small>Team presence</small></span>
|
||||
</a>
|
||||
<div class="story-copy">
|
||||
<p class="eyebrow">Your team, in one place</p>
|
||||
<h1>Start showing up.<br>Stay in sync.</h1>
|
||||
<p>Create an account with email or username and password—or continue with your preferred SSO provider.</p>
|
||||
</div>
|
||||
<div class="story-calendar" aria-hidden="true">
|
||||
<div><small>شروع همکاری</small><strong>حساب جدید</strong><span>خوش آمدید</span></div>
|
||||
<div class="people-dots"><i></i><i></i><i></i><b>+</b></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="login-form-wrap register-wrap">
|
||||
<form class="login-card register-card" method="post" action="/register">
|
||||
<div class="mobile-brand"><span class="brand-mark">ه</span> Hamkar</div>
|
||||
<p class="eyebrow">JOIN YOUR TEAM</p>
|
||||
<h2>Create your account</h2>
|
||||
<p class="muted">Use an email or username, plus a secure password.</p>
|
||||
{{if .Error}}<div class="notice error">{{.Error}}</div>{{end}}
|
||||
<label>Full name<input name="display_name" autocomplete="name" required autofocus maxlength="100" placeholder="Sara Ahmadi"></label>
|
||||
<div class="form-row">
|
||||
<label>Email <small>Optional</small><input type="email" name="email" autocomplete="email" maxlength="200" placeholder="sara@example.com"></label>
|
||||
<label>Username <small>Optional</small><input name="username" autocomplete="username" maxlength="40" placeholder="sara"></label>
|
||||
</div>
|
||||
<p class="field-help">Enter at least an email address or username.</p>
|
||||
<div class="form-row">
|
||||
<label>Password<input type="password" name="password" autocomplete="new-password" minlength="8" required placeholder="At least 8 characters"></label>
|
||||
<label>Confirm password<input type="password" name="password_confirm" autocomplete="new-password" minlength="8" required placeholder="Repeat password"></label>
|
||||
</div>
|
||||
<button class="button primary full" type="submit">Create account</button>
|
||||
{{if or .OAuthGitHub .OAuthGoogle}}
|
||||
<div class="separator"><span>or sign up with SSO</span></div>
|
||||
<div class="oauth-row">
|
||||
{{if .OAuthGoogle}}<a class="button secondary" href="/auth/google">Google</a>{{end}}
|
||||
{{if .OAuthGitHub}}<a class="button secondary" href="/auth/github">GitHub</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<p class="auth-switch">Already have an account? <a href="/login">Sign in</a></p>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
@@ -0,0 +1,14 @@
|
||||
{{define "reports.html"}}
|
||||
{{template "shell-start" .}}
|
||||
<header class="page-head"><div><p class="eyebrow">ADMIN</p><h1>Reports</h1><p>Export team presence data for payroll or analysis.</p></div></header>
|
||||
<section class="card report-card">
|
||||
<div class="report-illustration">↧</div>
|
||||
<div><p class="eyebrow">ATTENDANCE EXPORT</p><h2>Download presence records</h2><p class="muted">The UTF-8 CSV includes Gregorian and Persian dates, check-in/out time, location, and approved requests. It opens directly in Excel.</p></div>
|
||||
<form method="get" action="/reports/attendance.csv" class="report-form">
|
||||
<label>From<input type="date" name="start" value="{{.ReportStart}}" required></label>
|
||||
<label>To<input type="date" name="end" value="{{.ReportEnd}}" required></label>
|
||||
<button class="button primary">Download CSV</button>
|
||||
</form>
|
||||
</section>
|
||||
{{template "shell-end" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,42 @@
|
||||
{{define "requests.html"}}
|
||||
{{template "shell-start" .}}
|
||||
<header class="page-head">
|
||||
<div><p class="eyebrow">PLANNING</p><h1>My requests</h1><p>Ask for time off or a remote work day.</p></div>
|
||||
</header>
|
||||
{{template "notice" .}}
|
||||
<div class="two-column">
|
||||
<section class="card form-card">
|
||||
<p class="eyebrow">NEW REQUEST</p><h2>Plan a day away</h2>
|
||||
<form method="post" action="/requests" class="stack-form">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
||||
<fieldset class="choice-cards">
|
||||
<label><input type="radio" name="kind" value="leave" checked><span><b>☼</b><strong>Time off</strong><small>Holiday or personal leave</small></span></label>
|
||||
<label><input type="radio" name="kind" value="remote"><span><b>⌂</b><strong>Remote day</strong><small>Work away from the office</small></span></label>
|
||||
</fieldset>
|
||||
<div class="form-row">
|
||||
<label>From <small>Persian date</small><span class="jalali-field"><input name="start_date" data-jalali-picker data-jalali-role="start" required autocomplete="off" inputmode="numeric" placeholder="1405-05-06" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}"><button type="button" class="jalali-trigger" aria-label="Choose start date" title="Open Persian calendar">▦</button></span></label>
|
||||
<label>To <small>Persian date</small><span class="jalali-field"><input name="end_date" data-jalali-picker data-jalali-role="end" required autocomplete="off" inputmode="numeric" placeholder="1405-05-06" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}"><button type="button" class="jalali-trigger" aria-label="Choose end date" title="Open Persian calendar">▦</button></span></label>
|
||||
</div>
|
||||
<label>Note <small>Optional</small><textarea name="reason" rows="4" maxlength="500" placeholder="Anything your manager should know?"></textarea></label>
|
||||
<button class="button primary full">Send for approval</button>
|
||||
</form>
|
||||
</section>
|
||||
<section class="card">
|
||||
<header class="section-head"><div><p class="eyebrow">HISTORY</p><h2>All requests</h2></div></header>
|
||||
{{if .Requests}}
|
||||
<div class="request-list">
|
||||
{{range .Requests}}
|
||||
<div class="request-row request-detail">
|
||||
<span class="request-icon {{.Kind}}">{{if eq .Kind "remote"}}⌂{{else}}☼{{end}}</span>
|
||||
<div><strong>{{kindLabel .Kind}}</strong><small>{{dateFA .StartDate}}{{if ne .StartDate .EndDate}} — {{dateFA .EndDate}}{{end}} · {{.DayCount}} day(s)</small>{{if .Reason}}<p>{{.Reason}}</p>{{end}}{{if .AdminNote}}<p class="admin-note">Manager: {{.AdminNote}}</p>{{end}}</div>
|
||||
<div class="request-end"><span class="badge {{.Status}}">{{statusLabel .Status}}</span>
|
||||
{{if eq .Status "pending"}}<form method="post" action="/requests/{{.ID}}/cancel"><input type="hidden" name="csrf" value="{{$.CSRF}}"><button class="text-button">Cancel</button></form>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}<div class="empty"><span>◌</span><p>You haven’t made any requests.</p></div>{{end}}
|
||||
</section>
|
||||
</div>
|
||||
{{template "shell-end" .}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,38 @@
|
||||
{{define "users.html"}}
|
||||
{{template "shell-start" .}}
|
||||
<header class="page-head">
|
||||
<div><p class="eyebrow">ADMIN</p><h1>Teammates</h1><p>Create local accounts and see who has access.</p></div>
|
||||
</header>
|
||||
{{template "notice" .}}
|
||||
<div class="two-column users-layout">
|
||||
<section class="card form-card">
|
||||
<p class="eyebrow">NEW ACCOUNT</p><h2>Add a teammate</h2>
|
||||
<form method="post" action="/admin/users" class="stack-form">
|
||||
<input type="hidden" name="csrf" value="{{.CSRF}}">
|
||||
<label>Full name<input name="display_name" required maxlength="100" placeholder="Sara Ahmadi"></label>
|
||||
<div class="form-row">
|
||||
<label>Username<input name="username" required maxlength="40" autocomplete="off" placeholder="sara"></label>
|
||||
<label>Email <small>Optional</small><input type="email" name="email" maxlength="200" placeholder="sara@example.com"></label>
|
||||
</div>
|
||||
<label>Temporary password<input type="password" name="password" minlength="8" required autocomplete="new-password" placeholder="At least 8 characters"></label>
|
||||
<label>Access level
|
||||
<select name="role"><option value="member">Member</option><option value="admin">Admin</option></select>
|
||||
</label>
|
||||
<button class="button primary full">Create account</button>
|
||||
</form>
|
||||
</section>
|
||||
<section class="card">
|
||||
<header class="section-head"><div><p class="eyebrow">DIRECTORY</p><h2>{{len .Users}} teammates</h2></div></header>
|
||||
<div class="user-list">
|
||||
{{range .Users}}
|
||||
<div class="user-row">
|
||||
<span class="avatar">{{initial .DisplayName}}</span>
|
||||
<div><strong>{{.DisplayName}}</strong><small>@{{.Username}}{{if .Email}} · {{.Email}}{{end}}</small></div>
|
||||
<span class="badge {{if eq .Role "admin"}}approved{{else}}kind{{end}}">{{.Role}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{{template "shell-end" .}}
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user