Files
TimeTracker/templates/timer/edit_timer.html
Dries Peeters 90d8407bda feat(billing): add paid status tracking for time entries with invoice reference
Add ability to mark time entries as paid and link them to internal invoice
numbers. Automatically mark time entries as paid when invoices are sent.

Database Changes:
- Add migration 083 to add `paid` boolean and `invoice_number` string columns
  to time_entries table
- Add index on `paid` field for faster queries

Model Updates:
- Add `paid` (default: False) and `invoice_number` (nullable) fields to TimeEntry
- Add `set_paid()` helper method to TimeEntry model
- Update `to_dict()` to include paid status and invoice number

API & Service Layer:
- Update TimeEntrySchema (all variants) to include paid/invoice_number fields
- Update API endpoints (/api/entry, /api/v1/time-entries) to accept these fields
- Update TimeTrackingService and TimeEntryRepository to handle paid status
- Add InvoiceService.mark_time_entries_as_paid() to automatically mark entries
- Update InvoiceService.mark_as_sent() to auto-mark time entries as paid

UI Updates:
- Add "Paid" checkbox and "Invoice Number" input field to time entry edit forms
- Update both admin and regular user edit forms
- Fields appear in timer edit page after tags section

Invoice Integration:
- Automatically mark time entries as paid when invoice status changes to "sent"
- Mark entries when time is added to already-sent invoices
- Store invoice number reference on time entries for tracking
- Enhanced create_invoice_from_time_entries() to properly link time entries

This enables proper tracking of which hours have been invoiced and paid
through the internal invoicing system, separate from the external ERP system.
2025-11-30 11:31:42 +01:00

498 lines
29 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ _('Edit Time Entry') }} - {{ app_name }}{% endblock %}
{% block extra_js %}
{% if current_user.is_admin %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const projectSelect = document.getElementById('project_id');
const taskSelect = document.getElementById('task_id');
const form = document.querySelector('form');
if (projectSelect && taskSelect) {
projectSelect.addEventListener('change', function() {
const projectId = this.value;
// Clear current tasks
taskSelect.innerHTML = '<option value="">No Task</option>';
if (projectId) {
// Fetch tasks for the selected project
fetch(`/api/projects/${projectId}/tasks`, { credentials: 'same-origin' })
.then(response => response.json())
.then(data => {
if (data.success && data.tasks) {
data.tasks.forEach(task => {
const option = document.createElement('option');
option.value = task.id;
option.textContent = task.name;
taskSelect.appendChild(option);
});
}
})
.catch(error => {
console.error('Error fetching tasks:', error);
});
}
});
}
// Add form submission confirmation for admin users
if (form) {
form.addEventListener('submit', function(e) {
// If we already confirmed, let it proceed
if (form.dataset.confirmed === '1') {
delete form.dataset.confirmed;
return true;
}
const originalProject = '{{ timer.project.name }}';
const selectedProject = projectSelect.options[projectSelect.selectedIndex].text;
const originalStart = '{{ timer.start_time.strftime("%Y-%m-%d %H:%M") }}';
const originalEnd = '{{ timer.end_time.strftime("%Y-%m-%d %H:%M") if timer.end_time else "Running" }}';
const originalNotes = {{ (timer.notes or '')|tojson }};
const originalTags = {{ (timer.tags or '')|tojson }};
const originalBillable = {{ 'true' if timer.billable else 'false' }};
const startDate = document.getElementById('start_date').value;
const startTime = document.getElementById('start_time').value;
const endDate = document.getElementById('end_date').value;
const endTime = document.getElementById('end_time').value;
const notesVal = document.getElementById('notes').value || '';
const tagsVal = document.getElementById('tags').value || '';
const billableVal = document.getElementById('billable').checked;
const newStart = startDate && startTime ? `${startDate} ${startTime}` : originalStart;
const newEnd = endDate && endTime ? `${endDate} ${endTime}` : originalEnd;
const changes = [];
if (originalProject !== selectedProject) changes.push({ label: 'Project', from: originalProject, to: selectedProject });
if (originalStart !== newStart) changes.push({ label: 'Start', from: originalStart, to: newStart });
if (originalEnd !== newEnd) changes.push({ label: 'End', from: originalEnd, to: newEnd });
if (originalNotes !== notesVal) changes.push({ label: 'Notes', from: originalNotes || '—', to: notesVal || '—' });
if (originalTags !== tagsVal) changes.push({ label: 'Tags', from: originalTags || '—', to: tagsVal || '—' });
if ((originalBillable ? true : false) !== billableVal) changes.push({ label: 'Billable', from: originalBillable ? 'Yes' : 'No', to: billableVal ? 'Yes' : 'No' });
if (changes.length > 0) {
e.preventDefault();
let summaryHtml = changes.map(ch => `
<div class="mb-2 pb-2 border-b border-border-light dark:border-border-dark">
<div class="text-sm text-text-muted-light dark:text-text-muted-dark mb-1">${ch.label}</div>
<div class="flex items-center gap-2">
<span class="text-red-600 dark:text-red-400">${ch.from}</span>
<i class="fas fa-arrow-right text-text-muted-light dark:text-text-muted-dark"></i>
<span class="text-green-600 dark:text-green-400">${ch.to}</span>
</div>
</div>
`).join('');
window.showConfirm(
summaryHtml + '<div class="mt-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded"><i class="fas fa-info-circle text-amber-600 dark:text-amber-400 me-2"></i>' + '{{ _("These updates will modify this time entry permanently.") }}' + '</div>',
{
title: '{{ _("Confirm Changes") }}',
confirmText: '{{ _("Confirm & Save") }}'
}
).then(confirmed => {
if (confirmed) {
form.dataset.confirmed = '1';
if (typeof form.requestSubmit === 'function') {
form.requestSubmit();
} else {
form.submit();
}
}
});
}
});
}
// Ensure admin save button programmatically submits the form
const adminSaveBtn = document.getElementById('adminEditSaveBtn');
if (adminSaveBtn && form) {
adminSaveBtn.addEventListener('click', function(ev) {
ev.preventDefault();
if (typeof form.checkValidity === 'function' && !form.checkValidity()) {
if (typeof form.reportValidity === 'function') form.reportValidity();
return;
}
if (typeof form.requestSubmit === 'function') {
form.requestSubmit();
} else {
form.submit();
}
});
}
// Live update duration when date/time fields change (admin form)
const startDate = document.getElementById('start_date');
const startTime = document.getElementById('start_time');
const endDate = document.getElementById('end_date');
const endTime = document.getElementById('end_time');
const durationLabel = document.getElementById('adminEditDuration');
function updateDuration() {
if (!startDate || !startTime || !endDate || !endTime || !durationLabel) return;
const sd = startDate.value;
const st = startTime.value;
const ed = endDate.value;
const et = endTime.value;
if (!sd || !st || !ed || !et) {
durationLabel.textContent = '--:--:--';
return;
}
const s = new Date(`${sd}T${st}`);
const e = new Date(`${ed}T${et}`);
const diff = Math.max(0, Math.floor((e - s) / 1000));
const h = Math.floor(diff / 3600);
const m = Math.floor((diff % 3600) / 60);
const srem = diff % 60;
durationLabel.textContent = `${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}:${srem.toString().padStart(2,'0')}`;
}
if (startDate && startTime && endDate && endTime) {
startDate.addEventListener('change', updateDuration);
startTime.addEventListener('change', updateDuration);
endDate.addEventListener('change', updateDuration);
endTime.addEventListener('change', updateDuration);
}
});
</script>
{% endif %}
{% endblock %}
{% block content %}
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
<div>
<h1 class="text-2xl font-bold flex items-center gap-2">
<i class="fas fa-edit text-primary"></i>
{{ _('Edit Time Entry') }}
</h1>
<p class="text-text-muted-light dark:text-text-muted-dark">
{{ timer.project.name }}{% if timer.task %} - {{ timer.task.name }}{% endif %}
</p>
</div>
{% if current_user.is_admin %}
<span class="inline-flex items-center px-3 py-1.5 rounded-lg bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300 text-sm font-medium mt-4 md:mt-0">
<i class="fas fa-shield-alt mr-2"></i>{{ _('Admin Mode') }}
</span>
{% endif %}
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2">
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow">
{% if current_user.is_admin %}
<!-- Admin notification -->
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-6">
<div class="flex items-start gap-3">
<i class="fas fa-info-circle text-blue-600 dark:text-blue-400 mt-1"></i>
<div>
<p class="text-sm text-blue-800 dark:text-blue-200">
<strong>{{ _('Admin Mode:') }}</strong> {{ _('You can edit all fields of this time entry, including project, task, start/end times, and source.') }}
</p>
</div>
</div>
</div>
<form method="POST" action="{{ url_for('timer.edit_timer', timer_id=timer.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<!-- Project and Task Selection -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label for="project_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-project-diagram mr-1"></i>{{ _('Project') }}
</label>
<select class="form-input" id="project_id" name="project_id" required>
{% for project in projects %}
<option value="{{ project.id }}" {% if project.id == timer.project_id %}selected{% endif %}>
{{ project.name }}
</option>
{% endfor %}
</select>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Select the project this time entry belongs to') }}</p>
</div>
<div>
<label for="task_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-tasks mr-1"></i>{{ _('Task (Optional)') }}
</label>
<select class="form-input" id="task_id" name="task_id">
<option value="">No Task</option>
{% for task in tasks %}
<option value="{{ task.id }}" {% if task.id == timer.task_id %}selected{% endif %}>
{{ task.name }}
</option>
{% endfor %}
</select>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Select a specific task within the project') }}</p>
</div>
</div>
<!-- Start Date/Time -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label for="start_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-clock mr-1"></i>{{ _('Start Date') }}
</label>
<input type="date" class="form-input" id="start_date" name="start_date"
value="{{ timer.start_time.strftime('%Y-%m-%d') }}" required>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('When the work started') }}</p>
</div>
<div>
<label for="start_time" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-clock mr-1"></i>{{ _('Start Time') }}
</label>
<input type="time" class="form-input" id="start_time" name="start_time"
value="{{ timer.start_time.strftime('%H:%M') }}" required>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Time the work started') }}</p>
</div>
</div>
<!-- End Date/Time -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label for="end_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-stop-circle mr-1"></i>{{ _('End Date') }}
</label>
<input type="date" class="form-input" id="end_date" name="end_date"
value="{{ timer.end_time.strftime('%Y-%m-%d') if timer.end_time else '' }}">
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('When the work ended (leave empty if still running)') }}</p>
</div>
<div>
<label for="end_time" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-stop-circle mr-1"></i>{{ _('End Time') }}
</label>
<input type="time" class="form-input" id="end_time" name="end_time"
value="{{ timer.end_time.strftime('%H:%M') if timer.end_time else '' }}">
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Time the work ended') }}</p>
</div>
</div>
<!-- Source, Billable, Duration -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div>
<label for="source" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-tag mr-1"></i>{{ _('Source') }}
</label>
<select class="form-input" id="source" name="source">
<option value="manual" {% if timer.source == 'manual' %}selected{% endif %}>{{ _('Manual') }}</option>
<option value="auto" {% if timer.source == 'auto' %}selected{% endif %}>{{ _('Automatic') }}</option>
</select>
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('How this entry was created') }}</p>
</div>
<div class="flex items-center">
<div class="flex items-center gap-3 mt-6">
<input type="checkbox" id="billable" name="billable" class="h-5 w-5 rounded border-gray-300 text-primary focus:ring-0" {% if timer.billable %}checked{% endif %}>
<label for="billable" class="text-sm font-medium text-gray-700 dark:text-gray-300">
<i class="fas fa-dollar-sign mr-1"></i>{{ _('Billable') }}
</label>
</div>
</div>
<div class="flex items-center">
<div class="text-sm mt-6">
<strong class="text-gray-700 dark:text-gray-300">{{ _('Duration:') }}</strong>
<span id="adminEditDuration" class="ml-2 text-primary font-mono">{{ timer.duration_formatted }}</span>
</div>
</div>
</div>
<!-- Notes -->
<div class="mb-6">
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-sticky-note mr-1"></i>{{ _('Notes') }}
</label>
<textarea class="form-input" id="notes" name="notes" rows="4" placeholder="{{ _('Describe what you worked on') }}">{{ timer.notes or '' }}</textarea>
</div>
<!-- Tags -->
<div class="mb-6">
<label for="tags" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-tags mr-1"></i>{{ _('Tags') }}
</label>
<input type="text" class="form-input" id="tags" name="tags" placeholder="{{ _('tag1, tag2') }}" value="{{ timer.tags or '' }}">
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Separate tags with commas') }}</p>
</div>
<!-- Paid Status and Invoice Number -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div class="flex items-center">
<div class="flex items-center gap-3">
<input type="checkbox" id="paid" name="paid" class="h-5 w-5 rounded border-gray-300 text-primary focus:ring-0" {% if timer.paid %}checked{% endif %}>
<label for="paid" class="text-sm font-medium text-gray-700 dark:text-gray-300">
<i class="fas fa-check-circle mr-1"></i>{{ _('Paid') }}
</label>
</div>
</div>
<div>
<label for="invoice_number" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-file-invoice mr-1"></i>{{ _('Invoice Number') }}
</label>
<input type="text" class="form-input" id="invoice_number" name="invoice_number" placeholder="{{ _('Internal invoice reference') }}" value="{{ timer.invoice_number or '' }}" maxlength="100">
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Reference to internal invoice number') }}</p>
</div>
</div>
<!-- Action Buttons -->
<div class="flex flex-col sm:flex-row justify-between gap-3 mt-8 pt-6 border-t border-border-light dark:border-border-dark">
<div class="flex gap-2">
<a href="{{ url_for('main.dashboard') }}" class="px-4 py-2 rounded-lg border border-border-light dark:border-border-dark text-text-light dark:text-text-dark hover:bg-background-light dark:hover:bg-background-dark">
<i class="fas fa-arrow-left mr-1"></i>{{ _('Back') }}
</a>
<a href="{{ url_for('timer.duplicate_timer', timer_id=timer.id) }}" class="px-4 py-2 rounded-lg border border-primary text-primary hover:bg-primary hover:text-white transition-colors">
<i class="fas fa-copy mr-1"></i>{{ _('Duplicate') }}
</a>
</div>
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary-dark transition-colors" id="adminEditSaveBtn">
<i class="fas fa-save mr-2"></i>{{ _('Save Changes') }}
</button>
</div>
</form>
{% else %}
<!-- Regular user form -->
<form method="POST" action="{{ url_for('timer.edit_timer', timer_id=timer.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<!-- Read-only information -->
<div class="bg-background-light dark:bg-background-dark p-4 rounded-lg mb-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<span class="text-text-muted-light dark:text-text-muted-dark">{{ _('Project:') }}</span>
<span class="ml-2 font-medium">{{ timer.project.name }}</span>
</div>
{% if timer.task %}
<div>
<span class="text-text-muted-light dark:text-text-muted-dark">{{ _('Task:') }}</span>
<span class="ml-2 font-medium">{{ timer.task.name }}</span>
</div>
{% endif %}
<div>
<span class="text-text-muted-light dark:text-text-muted-dark">{{ _('Start:') }}</span>
<span class="ml-2 font-medium">{{ timer.start_time.strftime('%Y-%m-%d %H:%M') }}</span>
</div>
<div>
<span class="text-text-muted-light dark:text-text-muted-dark">{{ _('End:') }}</span>
{% if timer.end_time %}
<span class="ml-2 font-medium">{{ timer.end_time.strftime('%Y-%m-%d %H:%M') }}</span>
{% else %}
<span class="ml-2 text-amber-600 dark:text-amber-400 font-medium">{{ _('Running') }}</span>
{% endif %}
</div>
</div>
<div class="mt-3 flex gap-2">
<span class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-primary/10 text-primary">
{{ _('Duration:') }} {{ timer.duration_formatted }}
</span>
{% if timer.source == 'manual' %}
<span class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200">
{{ _('Manual') }}
</span>
{% else %}
<span class="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300">
{{ _('Automatic') }}
</span>
{% endif %}
</div>
</div>
<!-- Notes -->
<div class="mb-6">
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-sticky-note mr-1"></i>{{ _('Notes') }}
</label>
<textarea class="form-input" id="notes" name="notes" rows="4" placeholder="{{ _('Describe what you worked on') }}">{{ timer.notes or '' }}</textarea>
</div>
<!-- Tags and Billable -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="md:col-span-2">
<label for="tags" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-tags mr-1"></i>{{ _('Tags') }}
</label>
<input type="text" class="form-input" id="tags" name="tags" placeholder="{{ _('tag1, tag2') }}" value="{{ timer.tags or '' }}">
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Separate tags with commas') }}</p>
</div>
<div class="flex items-center">
<div class="flex items-center gap-3 mt-6">
<input type="checkbox" id="billable" name="billable" class="h-5 w-5 rounded border-gray-300 text-primary focus:ring-0" {% if timer.billable %}checked{% endif %}>
<label for="billable" class="text-sm font-medium text-gray-700 dark:text-gray-300">
<i class="fas fa-dollar-sign mr-1"></i>{{ _('Billable') }}
</label>
</div>
</div>
</div>
<!-- Paid Status and Invoice Number -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div class="flex items-center">
<div class="flex items-center gap-3">
<input type="checkbox" id="paid" name="paid" class="h-5 w-5 rounded border-gray-300 text-primary focus:ring-0" {% if timer.paid %}checked{% endif %}>
<label for="paid" class="text-sm font-medium text-gray-700 dark:text-gray-300">
<i class="fas fa-check-circle mr-1"></i>{{ _('Paid') }}
</label>
</div>
</div>
<div>
<label for="invoice_number" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
<i class="fas fa-file-invoice mr-1"></i>{{ _('Invoice Number') }}
</label>
<input type="text" class="form-input" id="invoice_number" name="invoice_number" placeholder="{{ _('Internal invoice reference') }}" value="{{ timer.invoice_number or '' }}" maxlength="100">
<p class="text-xs text-text-muted-light dark:text-text-muted-dark mt-1">{{ _('Reference to internal invoice number') }}</p>
</div>
</div>
<!-- Action Buttons -->
<div class="flex flex-col sm:flex-row justify-between gap-3 mt-8 pt-6 border-t border-border-light dark:border-border-dark">
<div class="flex gap-2">
<a href="{{ url_for('main.dashboard') }}" class="px-4 py-2 rounded-lg border border-border-light dark:border-border-dark text-text-light dark:text-text-dark hover:bg-background-light dark:hover:bg-background-dark">
<i class="fas fa-arrow-left mr-1"></i>{{ _('Back') }}
</a>
<a href="{{ url_for('timer.duplicate_timer', timer_id=timer.id) }}" class="px-4 py-2 rounded-lg border border-primary text-primary hover:bg-primary hover:text-white transition-colors">
<i class="fas fa-copy mr-1"></i>{{ _('Duplicate') }}
</a>
</div>
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary-dark transition-colors">
<i class="fas fa-save mr-2"></i>{{ _('Save Changes') }}
</button>
</div>
</form>
{% endif %}
</div>
</div>
<!-- Sidebar with additional info -->
<div class="lg:col-span-1 space-y-6">
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow">
<h2 class="text-lg font-semibold mb-4">{{ _('Entry Details') }}</h2>
<div class="space-y-3 text-sm">
<div>
<span class="text-text-muted-light dark:text-text-muted-dark block mb-1">{{ _('Created') }}</span>
<span class="font-medium">{{ timer.created_at.strftime('%Y-%m-%d %H:%M') if timer.created_at else 'N/A' }}</span>
</div>
{% if timer.user and (current_user.is_admin or timer.user_id == current_user.id) %}
<div>
<span class="text-text-muted-light dark:text-text-muted-dark block mb-1">{{ _('User') }}</span>
<span class="font-medium">{{ timer.user.full_name or timer.user.username }}</span>
</div>
{% endif %}
<div>
<span class="text-text-muted-light dark:text-text-muted-dark block mb-1">{{ _('Entry ID') }}</span>
<span class="font-mono text-xs">#{{ timer.id }}</span>
</div>
</div>
</div>
{% if current_user.is_admin %}
<div class="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4">
<h3 class="text-sm font-semibold text-amber-800 dark:text-amber-300 mb-2">
<i class="fas fa-exclamation-triangle mr-1"></i>{{ _('Admin Notice') }}
</h3>
<p class="text-xs text-amber-700 dark:text-amber-400">
{{ _('As an admin, you have full editing privileges for this time entry. Changes will be logged for audit purposes.') }}
</p>
</div>
{% endif %}
</div>
</div>
{% endblock %}