05/07 Phase 3: initial codes
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
{% extends "tenant/layouts/base.html" %}
|
||||
{% block title %}{{ 'Edit' if mode == 'edit' else 'New' }} Customer{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header fw-semibold">{{ 'Edit' if mode == 'edit' else 'New' }} Customer</div>
|
||||
<div class="card-body">
|
||||
{% if error %}<div class="alert alert-danger py-2">{{ error }}</div>{% endif %}
|
||||
<form method="POST" novalidate>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" name="name"
|
||||
value="{{ customer.name if customer else '' }}" required autofocus>
|
||||
</div>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col">
|
||||
<label class="form-label">Phone</label>
|
||||
<input type="tel" class="form-control" name="phone"
|
||||
value="{{ customer.phone if customer else '' }}">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" class="form-control" name="email"
|
||||
value="{{ customer.email if customer else '' }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Date of Birth</label>
|
||||
<input type="date" class="form-control" name="date_of_birth"
|
||||
value="{{ customer.date_of_birth.isoformat() if customer and customer.date_of_birth else '' }}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea class="form-control" name="notes" rows="3">{{ customer.notes if customer else '' }}</textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<a href="{{ url_for('customers.index') }}" class="btn btn-outline-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,41 @@
|
||||
{% extends "tenant/layouts/base.html" %}
|
||||
{% block title %}Customers{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="fw-bold mb-0"><i class="bi bi-people me-2"></i>Customers</h4>
|
||||
<a href="{{ url_for('customers.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-lg me-1"></i>New Customer
|
||||
</a>
|
||||
</div>
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-body py-2">
|
||||
<form method="GET" class="d-flex gap-2">
|
||||
<input type="search" class="form-control form-control-sm" name="q"
|
||||
value="{{ q }}" placeholder="Search by name, phone, or email…" autofocus>
|
||||
<button class="btn btn-outline-secondary btn-sm">Search</button>
|
||||
{% if q %}<a href="{{ url_for('customers.index') }}" class="btn btn-outline-danger btn-sm">Clear</a>{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light"><tr><th>Name</th><th>Phone</th><th>Email</th><th>Loyalty</th><th>No-Shows</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for c in customers %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('customers.view', customer_id=c.id) }}" class="text-decoration-none fw-semibold">{{ c.name }}</a></td>
|
||||
<td class="text-muted small">{{ c.phone or '—' }}</td>
|
||||
<td class="text-muted small">{{ c.email or '—' }}</td>
|
||||
<td><span class="badge bg-warning text-dark">{{ c.loyalty_points }} pts</span></td>
|
||||
<td>{% if c.no_show_count > 0 %}<span class="badge bg-danger">{{ c.no_show_count }}</span>{% else %}0{% endif %}</td>
|
||||
<td><a href="{{ url_for('customers.edit', customer_id=c.id) }}" class="btn btn-outline-secondary btn-sm">Edit</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="text-center text-muted py-4">No customers found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,58 @@
|
||||
{% extends "tenant/layouts/base.html" %}
|
||||
{% block title %}{{ customer.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="fw-bold mb-0">{{ customer.name }}</h4>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ url_for('customers.edit', customer_id=customer.id) }}" class="btn btn-outline-secondary btn-sm">Edit</a>
|
||||
<a href="{{ url_for('appointments.create') }}?customer_id={{ customer.id }}" class="btn btn-primary btn-sm">New Appointment</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<p class="mb-1"><i class="bi bi-telephone me-2 text-muted"></i>{{ customer.phone or '—' }}</p>
|
||||
<p class="mb-1"><i class="bi bi-envelope me-2 text-muted"></i>{{ customer.email or '—' }}</p>
|
||||
{% if customer.date_of_birth %}
|
||||
<p class="mb-1"><i class="bi bi-cake me-2 text-muted"></i>{{ customer.date_of_birth.strftime('%B %d') }}</p>
|
||||
{% endif %}
|
||||
<p class="mb-1"><i class="bi bi-star me-2 text-warning"></i>{{ customer.loyalty_points }} loyalty points</p>
|
||||
{% if customer.no_show_count > 0 %}
|
||||
<p class="mb-0"><i class="bi bi-x-circle me-2 text-danger"></i>{{ customer.no_show_count }} no-show(s)</p>
|
||||
{% endif %}
|
||||
{% if customer.notes %}
|
||||
<hr><p class="text-muted small mb-0">{{ customer.notes }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-header fw-semibold">Recent Appointments</div>
|
||||
{% if appointments %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Date</th><th>Service</th><th>Staff</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
{% for a in appointments %}
|
||||
<tr>
|
||||
<td class="small">{{ a.start_time.strftime('%Y-%m-%d %I:%M %p') }}</td>
|
||||
<td class="small">{{ a.service.name if a.service else '—' }}</td>
|
||||
<td class="small">{{ a.staff.name if a.staff else '—' }}</td>
|
||||
<td><span class="badge bg-{{ {'completed':'success','cancelled':'danger','no_show':'warning'}.get(a.status,'secondary') }} small">{{ a.status }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body text-muted small">No appointments yet.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ url_for('customers.index') }}" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left me-1"></i>Back
|
||||
</a>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user