Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 230 additions & 0 deletions frontend/app/agencies/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
"use client";

import { useState, useEffect } from "react";
import { useParams, useRouter } from "next/navigation";
import { useAgency, useUpdateAgency } from "@/hooks/useApi";
import type { Agency } from "@/types";

export default function AgencyEditPage() {
const params = useParams();
const router = useRouter();
const agencyId = params.id as string;

const [formData, setFormData] = useState<Partial<Agency>>({});

const { data: agency, isLoading } = useAgency(agencyId);

useEffect(() => {
if (agency) setFormData(agency);
}, [agency]);

const updateMutation = useUpdateAgency(agencyId);

const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
updateMutation.mutate(formData, {
onSuccess: () => router.push(`/agencies/${agencyId}`),
});
};

if (isLoading) {
return (
<div className="min-h-screen flex flex-col">
<header className="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
<div className="max-w-6xl mx-auto px-6 py-6 flex justify-between items-center">
<h1 className="text-2xl font-bold">Loading…</h1>
<button
onClick={() => router.back()}
className="px-4 py-2 bg-white/20 rounded hover:bg-white/30 transition"
>
Back
</button>
</div>
</header>
<main className="flex-1 max-w-6xl mx-auto px-6 py-8 w-full">
<div className="animate-pulse">Loading…</div>
</main>
</div>
);
}

if (!agency) {
return (
<div className="min-h-screen flex flex-col">
<header className="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
<div className="max-w-6xl mx-auto px-6 py-6 flex justify-between items-center">
<h1 className="text-2xl font-bold">Error</h1>
<button
onClick={() => router.back()}
className="px-4 py-2 bg-white/20 rounded hover:bg-white/30 transition"
>
Back
</button>
</div>
</header>
<main className="flex-1 max-w-6xl mx-auto px-6 py-8 w-full">
<div className="bg-red-50 border border-red-200 rounded p-4 text-red-800">
<p>Agency not found.</p>
</div>
</main>
</div>
);
}

return (
<div className="min-h-screen flex flex-col">
<header className="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
<div className="max-w-6xl mx-auto px-6 py-6 flex justify-between items-center">
<h1 className="text-2xl font-bold">Edit {agency.name}</h1>
<button
onClick={() => router.back()}
className="px-4 py-2 bg-white/20 rounded hover:bg-white/30 transition"
>
Back
</button>
</div>
</header>

<main className="flex-1 max-w-6xl mx-auto px-6 py-8 w-full">
<form onSubmit={handleSubmit} className="max-w-2xl space-y-6">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we edit the field, is it possible to autofill the fields with what the field has currently (like a get request to query)?

One problem I'm noticing when I test it is that if I don't add an email when I'm editing an agency, it will error out because it is mandatory in the request, may need to auto fill the fields just to prevent that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait just realized this might be DEV-76 that Brian is doing rn (@kenzysoror if you want to confirm that), might need to ignore this then 🔥

Copy link
Member

@kenzysoror kenzysoror Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup this is a really great point, and you're right about it being scoped within #26! i would say it's safe to ignore here :)

<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
Name
</label>
<input
type="text"
id="name"
name="name"
value={formData.name || ""}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
required
/>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email
</label>
<input
type="email"
id="email"
name="email"
value={formData.email || ""}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
required
/>
</div>

<div>
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">
Phone
</label>
<input
type="tel"
id="phone"
name="phone"
value={formData.phone || ""}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
/>
</div>
</div>

<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
Address
</label>
<input
type="text"
id="address"
name="address"
value={formData.address || ""}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
/>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="city" className="block text-sm font-medium text-gray-700">
City
</label>
<input
type="text"
id="city"
name="city"
value={formData.city || ""}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
/>
</div>

<div>
<label htmlFor="province" className="block text-sm font-medium text-gray-700">
Province
</label>
<input
type="text"
id="province"
name="province"
value={formData.province || ""}
onChange={handleChange}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
/>
</div>
</div>

<div>
<label htmlFor="description" className="block text-sm font-medium text-gray-700">
Description
</label>
<textarea
id="description"
name="description"
value={formData.description || ""}
onChange={handleChange}
rows={4}
className="mt-1 block w-full rounded-md border-gray-300 border shadow-sm focus:border-blue-500 focus:ring-blue-500 p-2"
/>
</div>

<div className="flex gap-3 pt-6">
<button
type="submit"
disabled={updateMutation.isPending}
className="px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition disabled:opacity-50 disabled:cursor-not-allowed"
>
{updateMutation.isPending ? "Saving…" : "Save Changes"}
</button>
<button
type="button"
onClick={() => router.back()}
className="px-6 py-2 bg-gray-300 text-gray-900 rounded hover:bg-gray-400 transition"
>
Cancel
</button>
</div>

{updateMutation.error && (
<div className="bg-red-50 border border-red-200 rounded p-4 text-red-800">
<p>Error saving changes. Please try again.</p>
</div>
)}
</form>
</main>
</div>
);
}
146 changes: 146 additions & 0 deletions frontend/app/agencies/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"use client";

import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { useAgency } from "@/hooks/useApi";
import type { Agency } from "@/types";

Check warning on line 6 in frontend/app/agencies/[id]/page.tsx

View workflow job for this annotation

GitHub Actions / run-lint

'Agency' is defined but never used. Allowed unused vars must match /^_/u

export default function AgencyDetailPage() {
const params = useParams();
const router = useRouter();
const agencyId = params.id as string;

const { data: agency, isLoading, error } = useAgency(agencyId);

if (isLoading) {
return (
<div className="min-h-screen flex flex-col">
<header className="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
<div className="max-w-6xl mx-auto px-6 py-6 flex justify-between items-center">
<h1 className="text-2xl font-bold">Loading…</h1>
<button
onClick={() => router.back()}
className="px-4 py-2 bg-white/20 rounded hover:bg-white/30 transition"
>
Back
</button>
</div>
</header>
<main className="flex-1 max-w-6xl mx-auto px-6 py-8 w-full">
<div className="animate-pulse">Loading agency details…</div>
</main>
</div>
);
}

if (error || !agency) {
return (
<div className="min-h-screen flex flex-col">
<header className="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
<div className="max-w-6xl mx-auto px-6 py-6 flex justify-between items-center">
<h1 className="text-2xl font-bold">Error</h1>
<button
onClick={() => router.back()}
className="px-4 py-2 bg-white/20 rounded hover:bg-white/30 transition"
>
Back
</button>
</div>
</header>
<main className="flex-1 max-w-6xl mx-auto px-6 py-8 w-full">
<div className="bg-red-50 border border-red-200 rounded p-4 text-red-800">
<p>Failed to load agency details.</p>
</div>
</main>
</div>
);
}

return (
<div className="min-h-screen flex flex-col">
<header className="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white shadow-lg">
<div className="max-w-6xl mx-auto px-6 py-6 flex justify-between items-center">
<h1 className="text-2xl font-bold">{agency.name}</h1>
<div className="flex gap-3">
<Link
href={`/agencies/${agencyId}/edit`}
className="px-4 py-2 bg-amber-500 hover:bg-amber-600 rounded transition"
>
Edit
</Link>
<button
onClick={() => router.back()}
className="px-4 py-2 bg-white/20 rounded hover:bg-white/30 transition"
>
Back
</button>
</div>
</div>
</header>

<main className="flex-1 max-w-6xl mx-auto px-6 py-8 w-full">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700">
Email
</label>
<p className="mt-1 text-gray-900">{agency.email}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Phone
</label>
<p className="mt-1 text-gray-900">{agency.phone}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
Status
</label>
<p className="mt-1">
<span className="inline-flex items-center rounded-full bg-blue-50 px-3 py-1 text-xs font-medium text-blue-700 border border-blue-200 capitalize">
{agency.status || "unset"}
</span>
</p>
</div>
</div>

<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700">
Address
</label>
<p className="mt-1 text-gray-900">{agency.address}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">
City
</label>
<p className="mt-1 text-gray-900">
{agency.city}, {agency.province}
</p>
</div>
</div>
</div>

{agency.description && (
<div className="mt-8">
<label className="block text-sm font-medium text-gray-700">
Description
</label>
<p className="mt-1 text-gray-900">{agency.description}</p>
</div>
)}

<div className="mt-8 pt-6 border-t">
<p className="text-xs text-gray-500">
Created: {new Date(agency.created_at).toLocaleDateString()}
</p>
<p className="text-xs text-gray-500">
Updated: {new Date(agency.updated_at).toLocaleDateString()}
</p>
</div>
</main>
</div>
);
}
Loading