All checks were successful
Docker Deploy / build-and-push (push) Successful in 4m6s
210 lines
6.4 KiB
Plaintext
210 lines
6.4 KiB
Plaintext
---
|
|
import DashboardLayout from '../../../../layouts/DashboardLayout.astro';
|
|
import { Icon } from 'astro-icon/components';
|
|
import { db } from '../../../../db';
|
|
import { clients, members } from '../../../../db/schema';
|
|
import { eq, and } from 'drizzle-orm';
|
|
|
|
const user = Astro.locals.user;
|
|
if (!user) return Astro.redirect('/login');
|
|
|
|
const { id } = Astro.params;
|
|
if (!id) return Astro.redirect('/dashboard/clients');
|
|
|
|
// Get current team from cookie
|
|
const currentTeamId = Astro.cookies.get('currentTeamId')?.value;
|
|
|
|
const userMemberships = await db.select()
|
|
.from(members)
|
|
.where(eq(members.userId, user.id))
|
|
.all();
|
|
|
|
if (userMemberships.length === 0) return Astro.redirect('/dashboard');
|
|
|
|
// Use current team or fallback to first membership
|
|
const userMembership = currentTeamId
|
|
? userMemberships.find(m => m.organizationId === currentTeamId) || userMemberships[0]
|
|
: userMemberships[0];
|
|
|
|
const client = await db.select()
|
|
.from(clients)
|
|
.where(and(
|
|
eq(clients.id, id),
|
|
eq(clients.organizationId, userMembership.organizationId)
|
|
))
|
|
.get();
|
|
|
|
if (!client) return Astro.redirect('/dashboard/clients');
|
|
---
|
|
|
|
<DashboardLayout title={`Edit ${client.name} - Chronus`}>
|
|
<div class="max-w-2xl mx-auto">
|
|
<div class="flex items-center gap-3 mb-6">
|
|
<a href={`/dashboard/clients/${client.id}`} class="btn btn-ghost btn-sm">
|
|
<Icon name="heroicons:arrow-left" class="w-5 h-5" />
|
|
</a>
|
|
<h1 class="text-3xl font-bold">Edit Client</h1>
|
|
</div>
|
|
|
|
<form method="POST" action={`/api/clients/${client.id}/update`} class="card bg-base-100 shadow-xl border border-base-200">
|
|
<div class="card-body">
|
|
<div class="form-control">
|
|
<label class="label" for="name">
|
|
<span class="label-text">Client Name</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="name"
|
|
name="name"
|
|
value={client.name}
|
|
placeholder="Acme Corp"
|
|
class="input input-bordered w-full"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-control">
|
|
<label class="label" for="email">
|
|
<span class="label-text">Email (optional)</span>
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
value={client.email || ''}
|
|
placeholder="jason.borne@cia.com"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-control">
|
|
<label class="label" for="phone">
|
|
<span class="label-text">Phone (optional)</span>
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
id="phone"
|
|
name="phone"
|
|
value={client.phone || ''}
|
|
placeholder="+1 (780) 420-1337"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div class="divider">Address Details</div>
|
|
|
|
<div class="form-control">
|
|
<label class="label" for="street">
|
|
<span class="label-text">Street Address (optional)</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="street"
|
|
name="street"
|
|
value={client.street || ''}
|
|
placeholder="123 Business Rd"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="form-control">
|
|
<label class="label" for="city">
|
|
<span class="label-text">City (optional)</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="city"
|
|
name="city"
|
|
value={client.city || ''}
|
|
placeholder="Edmonton"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-control">
|
|
<label class="label" for="state">
|
|
<span class="label-text">State / Province (optional)</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="state"
|
|
name="state"
|
|
value={client.state || ''}
|
|
placeholder="AB"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="form-control">
|
|
<label class="label" for="zip">
|
|
<span class="label-text">Zip / Postal Code (optional)</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="zip"
|
|
name="zip"
|
|
value={client.zip || ''}
|
|
placeholder="10001"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-control">
|
|
<label class="label" for="country">
|
|
<span class="label-text">Country (optional)</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="country"
|
|
name="country"
|
|
value={client.country || ''}
|
|
placeholder="Canada"
|
|
class="input input-bordered w-full"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-actions justify-between mt-6">
|
|
<button
|
|
type="button"
|
|
class="btn btn-error btn-outline"
|
|
onclick={`document.getElementById('delete_modal').showModal()`}
|
|
>
|
|
Delete Client
|
|
</button>
|
|
|
|
<div class="flex gap-2">
|
|
<a href={`/dashboard/clients/${client.id}`} class="btn btn-ghost">Cancel</a>
|
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Delete Confirmation Modal -->
|
|
<dialog id="delete_modal" class="modal">
|
|
<div class="modal-box">
|
|
<h3 class="font-bold text-lg text-error">Delete Client?</h3>
|
|
<p class="py-4">
|
|
Are you sure you want to delete <strong>{client.name}</strong>?
|
|
This action cannot be undone and will delete all associated time entries.
|
|
</p>
|
|
<div class="modal-action">
|
|
<form method="dialog">
|
|
<button class="btn">Cancel</button>
|
|
</form>
|
|
<form method="POST" action={`/api/clients/${client.id}/delete`}>
|
|
<button type="submit" class="btn btn-error">Delete</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button>close</button>
|
|
</form>
|
|
</dialog>
|
|
</DashboardLayout>
|