Some checks failed
Docker Deploy / build-and-push (push) Has been cancelled
181 lines
5.8 KiB
Plaintext
181 lines
5.8 KiB
Plaintext
---
|
|
import DashboardLayout from '../../../../layouts/DashboardLayout.astro';
|
|
import { Icon } from 'astro-icon/components';
|
|
import { db } from '../../../../db';
|
|
import { invoices, members } from '../../../../db/schema';
|
|
import { eq, and } from 'drizzle-orm';
|
|
|
|
const { id } = Astro.params;
|
|
const user = Astro.locals.user;
|
|
|
|
if (!user || !id) {
|
|
return Astro.redirect('/dashboard/invoices');
|
|
}
|
|
|
|
// Fetch invoice
|
|
const invoice = await db.select()
|
|
.from(invoices)
|
|
.where(eq(invoices.id, id))
|
|
.get();
|
|
|
|
if (!invoice) {
|
|
return Astro.redirect('/404');
|
|
}
|
|
|
|
// Verify membership
|
|
const membership = await db.select()
|
|
.from(members)
|
|
.where(and(
|
|
eq(members.userId, user.id),
|
|
eq(members.organizationId, invoice.organizationId)
|
|
))
|
|
.get();
|
|
|
|
if (!membership) {
|
|
return Astro.redirect('/dashboard');
|
|
}
|
|
|
|
// Format dates for input[type="date"]
|
|
const issueDateStr = invoice.issueDate.toISOString().split('T')[0];
|
|
const dueDateStr = invoice.dueDate.toISOString().split('T')[0];
|
|
|
|
const discountValueDisplay = invoice.discountType === 'fixed'
|
|
? (invoice.discountValue || 0) / 100
|
|
: (invoice.discountValue || 0);
|
|
---
|
|
|
|
<DashboardLayout title={`Edit ${invoice.number} - Chronus`}>
|
|
<div class="max-w-3xl mx-auto">
|
|
<div class="mb-6">
|
|
<a href={`/dashboard/invoices/${invoice.id}`} class="btn btn-ghost btn-sm gap-2 pl-0 hover:bg-transparent text-base-content/60">
|
|
<Icon name="heroicons:arrow-left" class="w-4 h-4" />
|
|
Back to Invoice
|
|
</a>
|
|
<h1 class="text-3xl font-bold mt-2">Edit Details</h1>
|
|
</div>
|
|
|
|
<form method="POST" action={`/api/invoices/${invoice.id}/update`} class="card bg-base-100 shadow-xl border border-base-200">
|
|
<div class="card-body gap-6">
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Number -->
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">Number</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="number"
|
|
class="input input-bordered font-mono"
|
|
value={invoice.number}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<!-- Currency -->
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">Currency</span>
|
|
</label>
|
|
<select name="currency" class="select select-bordered w-full">
|
|
<option value="USD" selected={invoice.currency === 'USD'}>USD ($)</option>
|
|
<option value="EUR" selected={invoice.currency === 'EUR'}>EUR (€)</option>
|
|
<option value="GBP" selected={invoice.currency === 'GBP'}>GBP (£)</option>
|
|
<option value="CAD" selected={invoice.currency === 'CAD'}>CAD ($)</option>
|
|
<option value="AUD" selected={invoice.currency === 'AUD'}>AUD ($)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Issue Date -->
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">Issue Date</span>
|
|
</label>
|
|
<input
|
|
type="date"
|
|
name="issueDate"
|
|
class="input input-bordered"
|
|
value={issueDateStr}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<!-- Due Date -->
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">
|
|
{invoice.type === 'quote' ? 'Valid Until' : 'Due Date'}
|
|
</span>
|
|
</label>
|
|
<input
|
|
type="date"
|
|
name="dueDate"
|
|
class="input input-bordered"
|
|
value={dueDateStr}
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<!-- Discount -->
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">Discount</span>
|
|
</label>
|
|
<div class="join w-full">
|
|
<select name="discountType" class="select select-bordered join-item">
|
|
<option value="percentage" selected={!invoice.discountType || invoice.discountType === 'percentage'}>%</option>
|
|
<option value="fixed" selected={invoice.discountType === 'fixed'}>Fixed</option>
|
|
</select>
|
|
<input
|
|
type="number"
|
|
name="discountValue"
|
|
step="0.01"
|
|
min="0"
|
|
class="input input-bordered join-item w-full"
|
|
value={discountValueDisplay}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tax Rate -->
|
|
<div class="form-control">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">Tax Rate (%)</span>
|
|
</label>
|
|
<input
|
|
type="number"
|
|
name="taxRate"
|
|
step="0.01"
|
|
min="0"
|
|
max="100"
|
|
class="input input-bordered"
|
|
value={invoice.taxRate}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes -->
|
|
<div class="form-control flex flex-col">
|
|
<label class="label">
|
|
<span class="label-text font-semibold">Notes / Terms</span>
|
|
</label>
|
|
<textarea
|
|
name="notes"
|
|
class="textarea textarea-bordered h-32 font-mono text-sm"
|
|
placeholder="Payment terms, bank details, or thank you notes..."
|
|
>{invoice.notes}</textarea>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<div class="card-actions justify-end">
|
|
<a href={`/dashboard/invoices/${invoice.id}`} class="btn btn-ghost">Cancel</a>
|
|
<button type="submit" class="btn btn-primary">
|
|
Save Changes
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</DashboardLayout>
|