164 lines
5.6 KiB
HTML
164 lines
5.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>TimeKeeper</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="stylesheet" href="/static/styles.css?v=20251223-03">
|
|
<script>
|
|
// Expose edit capability to client scripts (true only for admins)
|
|
window.can_edit = {{ 'true' if request.session.get('is_admin') else 'false' }};
|
|
</script>
|
|
<style>
|
|
/* Admin dropdown: inline in nav, no outline/border; ASCII-safe caret */
|
|
.tk-nav { display: flex; gap: 12px; align-items: center; }
|
|
.tk-dropdown { position: relative; display: inline-flex; align-items: center; }
|
|
.tk-dropdown-toggle.tk-nav-link {
|
|
border: 0;
|
|
outline: none;
|
|
box-shadow: none;
|
|
background: transparent;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 6px 10px;
|
|
}
|
|
.tk-dropdown-toggle.tk-nav-link:focus { outline: none; box-shadow: none; }
|
|
.tk-dropdown-toggle.tk-nav-link::after {
|
|
content: "\25BE"; /* small down triangle */
|
|
margin-left: 4px;
|
|
font-size: 0.9em;
|
|
}
|
|
.tk-dropdown-menu {
|
|
position: absolute;
|
|
right: 0;
|
|
top: calc(100% + 6px);
|
|
min-width: 200px;
|
|
background: #fff;
|
|
border: 1px solid #e5e7eb;
|
|
box-shadow: 0 6px 18px rgba(0,0,0,0.08);
|
|
border-radius: 8px;
|
|
padding: 6px;
|
|
display: none;
|
|
z-index: 1001;
|
|
}
|
|
.tk-dropdown.open .tk-dropdown-menu { display: block; }
|
|
.tk-dropdown-menu a { display: block; padding: 8px 10px; border-radius: 6px; }
|
|
.tk-dropdown-menu a:hover { background: #f3f4f6; }
|
|
</style>
|
|
</head>
|
|
<body class="tk">
|
|
<header class="tk-header">
|
|
<div class="tk-header-inner">
|
|
<a href="/viewer" class="tk-brand">TimeKeeper</a>
|
|
{% if not hide_nav_links %}
|
|
<nav class="tk-nav">
|
|
<a href="/viewer" class="tk-nav-link">Timesheet Editor</a>
|
|
<a href="/review" class="tk-nav-link">Review Timesheets</a>
|
|
{% if request.session.get('is_admin') %}
|
|
<a href="/pto-tracker" class="tk-nav-link">PTO Tracker</a>
|
|
<a href="/upload" class="tk-nav-link">Import</a>
|
|
<div class="tk-dropdown" id="adminDropdown">
|
|
<button type="button" class="tk-nav-link tk-dropdown-toggle" aria-haspopup="true" aria-expanded="false">Admin</button>
|
|
<div class="tk-dropdown-menu" role="menu" aria-label="Admin menu">
|
|
<a href="/admin/users" class="tk-nav-link">User Management</a>
|
|
<a href="/admin/employees" class="tk-nav-link">Employee Management</a>
|
|
<a href="/attendance" class="tk-nav-link">Attendance</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<a href="/logout" class="tk-nav-link danger">Logout</a>
|
|
</nav>
|
|
{% endif %}
|
|
</div>
|
|
</header>
|
|
|
|
<main class="tk-container">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<script>
|
|
// Admin dropdown toggle
|
|
(function () {
|
|
var dd = document.getElementById('adminDropdown');
|
|
if (!dd) return;
|
|
var btn = dd.querySelector('.tk-dropdown-toggle');
|
|
|
|
function closeAll() {
|
|
dd.classList.remove('open');
|
|
if (btn) btn.setAttribute('aria-expanded', 'false');
|
|
}
|
|
|
|
btn.addEventListener('click', function (e) {
|
|
e.stopPropagation();
|
|
var willOpen = !dd.classList.contains('open');
|
|
if (willOpen) {
|
|
dd.classList.add('open');
|
|
btn.setAttribute('aria-expanded', 'true');
|
|
} else {
|
|
closeAll();
|
|
}
|
|
});
|
|
|
|
document.addEventListener('click', function (e) {
|
|
if (!dd.contains(e.target)) closeAll();
|
|
});
|
|
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape') closeAll();
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
<script>
|
|
// Preserve and restore scroll position across same-origin navigations and refreshes.
|
|
(function () {
|
|
var key = 'scrollpos:' + location.pathname + location.search;
|
|
|
|
function headerOffset() {
|
|
var hdr = document.querySelector('.tk-header');
|
|
return hdr ? hdr.getBoundingClientRect().height : 0;
|
|
}
|
|
|
|
function restore() {
|
|
// If URL has a hash, let the browser handle anchor scrolling.
|
|
if (location.hash) return;
|
|
try {
|
|
var v = sessionStorage.getItem(key);
|
|
if (v !== null) {
|
|
var y = parseInt(v, 10) || 0;
|
|
var off = headerOffset();
|
|
window.scrollTo({ top: Math.max(0, y - off), left: 0, behavior: 'auto' });
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
function persist() {
|
|
try {
|
|
var y = window.scrollY || window.pageYOffset || 0;
|
|
sessionStorage.setItem(key, String(y));
|
|
} catch (e) {}
|
|
}
|
|
|
|
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
restore();
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', restore, { once: true });
|
|
}
|
|
|
|
window.addEventListener('beforeunload', persist);
|
|
|
|
// Persist on same-origin link clicks and form submits
|
|
document.addEventListener('click', function (ev) {
|
|
var a = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null;
|
|
if (!a) return;
|
|
try {
|
|
var u = new URL(a.href, location.href);
|
|
if (u.origin === location.origin) persist();
|
|
} catch (e) {}
|
|
}, true);
|
|
|
|
document.addEventListener('submit', function () { persist(); }, true);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html> |