mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-20 19:39:59 -06:00
- Clients: add model, routes, and templates
- app/models/client.py
- app/routes/clients.py
- templates/clients/{create,edit,list,view}.html
- docs/CLIENT_MANAGEMENT_README.md
- Database: add enhanced init/verify scripts, migrations, and docs
- docker/{init-database-enhanced.py,start-enhanced.py,verify-database.py}
- docs/ENHANCED_DATABASE_STARTUP.md
- migrations/{add_analytics_column.sql,add_analytics_setting.py,migrate_to_client_model.py}
- Scripts: add version manager and docker network test helpers
- scripts/version-manager.{bat,ps1,py,sh}
- scripts/test-docker-network.{bat,sh}
- docs/VERSION_MANAGEMENT.md
- UI: tweak base stylesheet
- app/static/base.css
- Tests: add client system test
- test_client_system.py
131 lines
6.7 KiB
HTML
131 lines
6.7 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Edit Project - {{ app_name }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{{ url_for('projects.list_projects') }}">Projects</a></li>
|
|
<li class="breadcrumb-item"><a href="{{ url_for('projects.view_project', project_id=project.id) }}">{{ project.name }}</a></li>
|
|
<li class="breadcrumb-item active">Edit</li>
|
|
</ol>
|
|
</nav>
|
|
<h1 class="h3 mb-0">
|
|
<i class="fas fa-project-diagram text-primary"></i> Edit Project
|
|
</h1>
|
|
</div>
|
|
<div>
|
|
<a href="{{ url_for('projects.view_project', project_id=project.id) }}" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left"></i> Back to Project
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-info-circle"></i> Project Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="POST" action="{{ url_for('projects.edit_project', project_id=project.id) }}">
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="mb-3">
|
|
<label for="name" class="form-label">Project Name *</label>
|
|
<input type="text" class="form-control" id="name" name="name" required value="{{ request.form.get('name', project.name) }}">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="client_id" class="form-label">Client *</label>
|
|
<select class="form-control" id="client_id" name="client_id" required>
|
|
<option value="">Select a client...</option>
|
|
{% for client in clients %}
|
|
<option value="{{ client.id }}"
|
|
{% if request.form.get('client_id', project.client_id) == client.id %}selected{% endif %}
|
|
data-default-rate="{{ client.default_hourly_rate or '' }}">
|
|
{{ client.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<div class="form-text">
|
|
<a href="{{ url_for('clients.create_client') }}" class="text-decoration-none">
|
|
<i class="fas fa-plus"></i> Create new client
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="description" class="form-label">Description</label>
|
|
<textarea class="form-control" id="description" name="description" rows="3">{{ request.form.get('description', project.description or '') }}</textarea>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="billable" name="billable" {% if (request.form and request.form.get('billable')) or (not request.form and project.billable) %}checked{% endif %}>
|
|
<label class="form-check-label" for="billable">Billable</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="hourly_rate" class="form-label">Hourly Rate</label>
|
|
<input type="number" step="0.01" min="0" class="form-control" id="hourly_rate" name="hourly_rate" value="{{ request.form.get('hourly_rate', project.hourly_rate or '') }}" placeholder="e.g. 75.00">
|
|
<div class="form-text">Leave empty for non-billable projects</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="billing_ref" class="form-label">Billing Reference</label>
|
|
<input type="text" class="form-control" id="billing_ref" name="billing_ref" value="{{ request.form.get('billing_ref', project.billing_ref or '') }}" placeholder="Optional">
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{{ url_for('projects.view_project', project_id=project.id) }}" class="btn btn-secondary">
|
|
<i class="fas fa-times"></i> Cancel
|
|
</a>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save"></i> Save Changes
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const clientSelect = document.getElementById('client_id');
|
|
const hourlyRateInput = document.getElementById('hourly_rate');
|
|
|
|
clientSelect.addEventListener('change', function() {
|
|
const selectedOption = this.options[this.selectedIndex];
|
|
const defaultRate = selectedOption.getAttribute('data-default-rate');
|
|
|
|
if (defaultRate && !hourlyRateInput.value) {
|
|
hourlyRateInput.value = defaultRate;
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|
|
|