mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-20 21:30:12 -05:00
8b6e61873b
Display formats for dates and times now follow the system settings (Admin settings) by default. Users can override in their profile (User settings) or choose "Use system default" so their view matches the rest of the system. Backend: - User.date_format and User.time_format are nullable; null means use system. - Migration 120 makes these columns nullable (existing rows unchanged). - get_resolved_date_format_key() and get_resolved_time_format_key() in timezone utils return the effective key (user or system) for templates and API. - Context processor injects resolved_date_format_key and resolved_time_format_key so base.html and JS (window.userPrefs) always see the resolved format. - User settings form: "Use system default" option and save logic for null. - User.to_dict() includes resolved date_format, time_format, and timezone for API clients (e.g. mobile). Web: - base.html uses resolved keys for window.userPrefs (no hardcoded fallback). - Replaced display-only strftime() in templates with |user_date, |user_datetime, |user_time, and |format_date so all visible dates/times respect settings. Left <input type="date"> values and URL/API params as YYYY-MM-DD where required. Mobile: - ApiClient.getCurrentUser() and user prefs provider load resolved prefs from /api/v1/users/me. - date_format_utils maps API keys to intl patterns; formatDate, formatTime, formatDateTime, formatDateRange used for display. - Time entries screen (filter dialog), time entry form, time entry card, and home dashboard use user prefs for formatting; API requests still send ISO dates. Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
5.6 KiB
HTML
134 lines
5.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ _('Edit Comment') }} - {{ app_name }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid px-3 px-md-4">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-8">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h3 mb-0">
|
|
<i class="fas fa-edit text-primary me-2"></i>
|
|
{{ _('Edit Comment') }}
|
|
</h1>
|
|
<a href="javascript:history.back()" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left me-1"></i>{{ _('Back') }}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<!-- Comment context -->
|
|
<div class="alert alert-info mb-4">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
<div>
|
|
<strong>{{ _('Editing comment on:') }}</strong>
|
|
{% if comment.project %}
|
|
<a href="{{ url_for('projects.view_project', project_id=comment.project.id) }}" class="ms-2">
|
|
<i class="fas fa-project-diagram me-1"></i>{{ comment.project.name }}
|
|
</a>
|
|
{% elif comment.task %}
|
|
<a href="{{ url_for('tasks.view_task', task_id=comment.task.id) }}" class="ms-2">
|
|
<i class="fas fa-tasks me-1"></i>{{ comment.task.name }}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Original comment info -->
|
|
<div class="mb-4">
|
|
<div class="d-flex align-items-center mb-2">
|
|
<div class="avatar-circle me-2">
|
|
{{ (comment.author.full_name or comment.author.username)[0].upper() }}
|
|
</div>
|
|
<div>
|
|
<strong>{{ comment.author.full_name or comment.author.username }}</strong>
|
|
<div class="text-muted small">
|
|
<i class="fas fa-clock me-1"></i>
|
|
{{ _('Originally posted on') }} {{ comment.created_at|user_datetime }}
|
|
{% if comment.created_at != comment.updated_at %}
|
|
<br>
|
|
<i class="fas fa-edit me-1"></i>
|
|
{{ _('Last edited on') }} {{ comment.updated_at|user_datetime }}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit form -->
|
|
<form method="POST">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<div class="mb-3">
|
|
<label for="content" class="form-label">
|
|
<i class="fas fa-comment me-1"></i>{{ _('Comment Content') }}
|
|
</label>
|
|
<textarea name="content" id="content" class="form-control" rows="6" required>{{ comment.content }}</textarea>
|
|
<div class="form-text">
|
|
{{ _('You can use line breaks to format your comment.') }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save me-1"></i>{{ _('Save Changes') }}
|
|
</button>
|
|
<a href="javascript:history.back()" class="btn btn-secondary">
|
|
<i class="fas fa-times me-1"></i>{{ _('Cancel') }}
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Auto-resize textarea
|
|
const textarea = document.getElementById('content');
|
|
textarea.addEventListener('input', function() {
|
|
this.style.height = 'auto';
|
|
this.style.height = (this.scrollHeight) + 'px';
|
|
});
|
|
|
|
// Initial resize
|
|
textarea.style.height = 'auto';
|
|
textarea.style.height = (textarea.scrollHeight) + 'px';
|
|
|
|
// Focus on textarea
|
|
textarea.focus();
|
|
|
|
// Add loading state to form
|
|
document.querySelector('form').addEventListener('submit', function(e) {
|
|
const submitBtn = this.querySelector('button[type="submit"]');
|
|
submitBtn.innerHTML = '<div class="spinner-border spinner-border-sm me-2" role="status"></div>{{ _('Saving...') }}';
|
|
submitBtn.disabled = true;
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.avatar-circle {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: var(--primary-color);
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: bold;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
textarea {
|
|
resize: vertical;
|
|
min-height: 120px;
|
|
}
|
|
</style>
|
|
{% endblock %}
|