Netdeploy2 Babyyyy
This commit is contained in:
31
templates/base.html
Normal file
31
templates/base.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>QuoteApp</title>
|
||||
<style>
|
||||
body { font-family: system-ui, -apple-system, sans-serif; margin: 20px; color:#111; }
|
||||
nav a { margin-right: 10px; }
|
||||
table { border-collapse: collapse; width: 100%; max-width: 900px; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; }
|
||||
th { background: #f5f5f5; text-align: left; }
|
||||
input, select, button { padding: 6px; }
|
||||
button { cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<a href="{{ url_for('quotes.dashboard') }}">Dashboard</a> |
|
||||
<a href="{{ url_for('clients.list_clients') }}">Clients</a> |
|
||||
<a href="{{ url_for('quotes.dashboard') }}">Quotes</a> |
|
||||
<a href="{{ url_for('invoices.list_invoices') }}">Invoices</a>
|
||||
</nav>
|
||||
|
||||
<hr>
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
32
templates/clients/list.html
Normal file
32
templates/clients/list.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>Clients</h2>
|
||||
|
||||
<p>
|
||||
<a href="{{ url_for('clients.new_client') }}">+ New Client</a>
|
||||
</p>
|
||||
|
||||
<table border="1" cellpadding="6">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Kind</th>
|
||||
</tr>
|
||||
|
||||
{% for c in clients %}
|
||||
<tr>
|
||||
<td>{{ c.id }}</td>
|
||||
<td>{{ c.name }}</td>
|
||||
<td>{{ c.email or "" }}</td>
|
||||
<td>{{ c.kind }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4">No clients yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
23
templates/clients/new.html
Normal file
23
templates/clients/new.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>New Client</h2>
|
||||
|
||||
<form method="POST">
|
||||
<p>
|
||||
<label>Name<br>
|
||||
<input name="name" required style="width: 360px;">
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>Email<br>
|
||||
<input name="email" style="width: 360px;">
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<button type="submit">Create Client</button>
|
||||
<a href="{{ url_for('clients.list_clients') }}" style="margin-left: 10px;">Cancel</a>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
34
templates/invoices/list.html
Normal file
34
templates/invoices/list.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>Invoices</h2>
|
||||
|
||||
<table border="1" cellpadding="6">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Client</th>
|
||||
<th>Status</th>
|
||||
<th>Total</th>
|
||||
<th>Paid</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
{% for inv in invoices %}
|
||||
<tr>
|
||||
<td>{{ inv.id }}</td>
|
||||
<td>{{ inv.client.name if inv.client else "" }}</td>
|
||||
<td>{{ inv.status }}</td>
|
||||
<td>${{ inv.total }}</td>
|
||||
<td>${{ inv.amount_paid }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('invoices.view_invoice', invoice_id=inv.id) }}">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="6">No invoices yet.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
41
templates/invoices/view.html
Normal file
41
templates/invoices/view.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>Invoice #{{ inv.id }}</h2>
|
||||
|
||||
<p>
|
||||
<strong>Client:</strong> {{ inv.client.name if inv.client else "" }}<br>
|
||||
<strong>Status:</strong> {{ inv.status }}<br>
|
||||
<strong>Total:</strong> ${{ inv.total }}<br>
|
||||
<strong>Paid:</strong> ${{ inv.amount_paid }}<br>
|
||||
</p>
|
||||
|
||||
<h3>Lines</h3>
|
||||
|
||||
<table border="1" cellpadding="6">
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>Qty</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Line Total</th>
|
||||
</tr>
|
||||
|
||||
{% for l in inv.lines %}
|
||||
<tr>
|
||||
<td>{{ l.description }}</td>
|
||||
<td>{{ l.qty }}</td>
|
||||
<td>${{ l.unit_price }}</td>
|
||||
<td>${{ (l.qty or 0) * (l.unit_price or 0) }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4">No lines found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p style="margin-top: 16px;">
|
||||
<a href="{{ url_for('invoices.list_invoices') }}">Back to invoices</a>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
58
templates/quotes/dashboard.html
Normal file
58
templates/quotes/dashboard.html
Normal file
@@ -0,0 +1,58 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>Dashboard</h2>
|
||||
|
||||
<p>
|
||||
<a href="{{ url_for('quotes.new_quote') }}">+ New Quote</a>
|
||||
</p>
|
||||
|
||||
<h3>Draft / Sent Quotes</h3>
|
||||
|
||||
<table border="1" cellpadding="6">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Status</th>
|
||||
<th>Total</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
{% for q in quotes if q.status in ['draft','sent'] %}
|
||||
<tr>
|
||||
<td>{{ q.id }}</td>
|
||||
<td>{{ q.title }}</td>
|
||||
<td>{{ q.status }}</td>
|
||||
<td>${{ q.total }}</td>
|
||||
<td><a href="{{ url_for('quotes.edit_quote', quote_id=q.id) }}">Open</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5">None</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Approved Quotes (Ready for Invoice)</h3>
|
||||
|
||||
<table border="1" cellpadding="6">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Total</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
{% for q in quotes if q.status == 'approved' %}
|
||||
<tr>
|
||||
<td>{{ q.id }}</td>
|
||||
<td>{{ q.title }}</td>
|
||||
<td>${{ q.total }}</td>
|
||||
<td><a href="{{ url_for('quotes.edit_quote', quote_id=q.id) }}">View</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="4">None</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
72
templates/quotes/edit.html
Normal file
72
templates/quotes/edit.html
Normal file
@@ -0,0 +1,72 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>Edit Quote #{{ q.id }}</h2>
|
||||
|
||||
<form method="POST">
|
||||
<p>
|
||||
<label>
|
||||
Title<br>
|
||||
<input name="title" value="{{ q.title or '' }}" style="width: 420px;">
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Category:</strong> {{ q.category }}<br>
|
||||
<strong>Status:</strong> {{ q.status }}
|
||||
</p>
|
||||
|
||||
<h3>Line Items</h3>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th style="width:110px;">Qty</th>
|
||||
<th style="width:140px;">Unit Price</th>
|
||||
<th style="width:140px;">Line Total</th>
|
||||
</tr>
|
||||
|
||||
{% for item in q.items %}
|
||||
<tr>
|
||||
<td>
|
||||
<!-- THIS is what makes saving work -->
|
||||
<input type="hidden" name="item_id" value="{{ item.id }}">
|
||||
|
||||
<input name="description" value="{{ item.description or '' }}" style="width: 100%;">
|
||||
</td>
|
||||
<td>
|
||||
<input name="qty" type="number" step="0.01" value="{{ item.qty or 0 }}" style="width: 100px;">
|
||||
</td>
|
||||
<td>
|
||||
<input name="unit_price" type="number" step="0.01" value="{{ item.unit_price or 0 }}" style="width: 120px;">
|
||||
</td>
|
||||
<td>
|
||||
${{ ((item.qty or 0) * (item.unit_price or 0)) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4">No items yet. Click “Add Item”.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p style="margin-top: 10px;">
|
||||
<button type="submit">Save Quote</button>
|
||||
<a href="{{ url_for('quotes.dashboard') }}" style="margin-left:10px;">Back</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="{{ url_for('quotes.add_item', quote_id=q.id) }}">
|
||||
<button type="submit">Add Item</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Total: ${{ q.total }}</h3>
|
||||
|
||||
<form method="POST" action="{{ url_for('invoices.create_from_quote', quote_id=q.id) }}">
|
||||
<button type="submit">Create Invoice from Quote</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
38
templates/quotes/new.html
Normal file
38
templates/quotes/new.html
Normal file
@@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<h2>New Quote</h2>
|
||||
|
||||
<form method="POST">
|
||||
<p>
|
||||
<label>Title<br>
|
||||
<input name="title" required style="width: 360px;">
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>Category<br>
|
||||
<select name="category" required>
|
||||
<option value="web_design">Web Design</option>
|
||||
<option value="it_services">IT Services</option>
|
||||
<option value="pc_repair">PC Repair</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>Client<br>
|
||||
<select name="client_id" required>
|
||||
{% for c in clients %}
|
||||
<option value="{{ c.id }}">{{ c.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
|
||||
<button type="submit">Create Quote</button>
|
||||
<a href="{{ url_for('quotes.dashboard') }}" style="margin-left: 10px;">Cancel</a>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user