Skip to content

Commit dd59e09

Browse files
authored
Merge pull request #28 from JelangA/Kuda
Kuda
2 parents d03a689 + e17025b commit dd59e09

File tree

17 files changed

+1611
-29
lines changed

17 files changed

+1611
-29
lines changed
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
"use client";
2+
import React, { useEffect, useState, use } from "react";
3+
import DashboardSidebar from "@/components/DashboardSidebar";
4+
import Link from "next/link";
5+
import { useRouter } from "next/navigation";
6+
import apiClient from "@/lib/api";
7+
import { toast } from "react-hot-toast";
8+
9+
interface Product {
10+
id: string;
11+
title: string;
12+
price: number;
13+
inStock: number;
14+
}
15+
16+
interface Merchant {
17+
id: string;
18+
name: string;
19+
email: string | null;
20+
phone: string | null;
21+
address: string | null;
22+
description: string | null;
23+
status: string;
24+
products: Product[];
25+
}
26+
27+
interface MerchantDetailPageProps {
28+
params: Promise<{ id: string }>;
29+
}
30+
31+
export default function MerchantDetailPage({
32+
params,
33+
}: MerchantDetailPageProps) {
34+
// Unwrap params using React.use()
35+
const resolvedParams = use(params);
36+
const id = resolvedParams.id;
37+
38+
const [merchant, setMerchant] = useState<Merchant | null>(null);
39+
const [loading, setLoading] = useState(true);
40+
const [formData, setFormData] = useState({
41+
name: "",
42+
email: "",
43+
phone: "",
44+
address: "",
45+
description: "",
46+
status: "ACTIVE",
47+
});
48+
49+
const router = useRouter();
50+
51+
const fetchMerchant = async () => {
52+
try {
53+
setLoading(true);
54+
const response = await apiClient.get(`/api/merchants/${id}`);
55+
56+
if (!response.ok) {
57+
if (response.status === 404) {
58+
router.push("/admin/merchant");
59+
return;
60+
}
61+
throw new Error("Failed to fetch merchant");
62+
}
63+
64+
const data = await response.json();
65+
setMerchant(data);
66+
setFormData({
67+
name: data.name || "",
68+
email: data.email || "",
69+
phone: data.phone || "",
70+
address: data.address || "",
71+
description: data.description || "",
72+
status: data.status || "ACTIVE",
73+
});
74+
} catch (error) {
75+
console.error("Error fetching merchant:", error);
76+
toast.error("Failed to load merchant details");
77+
} finally {
78+
setLoading(false);
79+
}
80+
};
81+
82+
useEffect(() => {
83+
fetchMerchant();
84+
}, [id]);
85+
86+
const handleInputChange = (
87+
e: React.ChangeEvent<
88+
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
89+
>
90+
) => {
91+
const { name, value } = e.target;
92+
setFormData((prev) => ({ ...prev, [name]: value }));
93+
};
94+
95+
const handleSubmit = async (e: React.FormEvent) => {
96+
e.preventDefault();
97+
try {
98+
// This is the correct way to use apiClient.put
99+
// It should just take the URL and the data object
100+
const response = await apiClient.put(`/api/merchants/${id}`, formData);
101+
102+
if (!response.ok) {
103+
throw new Error("Failed to update merchant");
104+
}
105+
106+
toast.success("Merchant updated successfully");
107+
fetchMerchant(); // Refresh data
108+
} catch (error) {
109+
console.error("Error updating merchant:", error);
110+
toast.error("Failed to update merchant");
111+
}
112+
};
113+
114+
const handleDelete = async () => {
115+
if (!confirm("Are you sure you want to delete this merchant?")) {
116+
return;
117+
}
118+
119+
try {
120+
const response = await apiClient.delete(`/api/merchants/${id}`);
121+
122+
if (!response.ok) {
123+
const data = await response.json();
124+
throw new Error(data.error || "Failed to delete merchant");
125+
}
126+
127+
toast.success("Merchant deleted successfully");
128+
router.push("/admin/merchant");
129+
} catch (error) {
130+
console.error("Error deleting merchant:", error);
131+
toast.error(
132+
typeof error === "object" && error !== null && "message" in error
133+
? (error as { message?: string }).message || "Failed to delete merchant"
134+
: "Failed to delete merchant"
135+
);
136+
}
137+
};
138+
139+
if (loading) {
140+
return (
141+
<div className="flex h-screen">
142+
<DashboardSidebar />
143+
<div className="flex-1 p-10 flex items-center justify-center">
144+
Loading merchant details...
145+
</div>
146+
</div>
147+
);
148+
}
149+
150+
if (!merchant) {
151+
return (
152+
<div className="flex h-screen">
153+
<DashboardSidebar />
154+
<div className="flex-1 p-10 flex items-center justify-center">
155+
Merchant not found
156+
</div>
157+
</div>
158+
);
159+
}
160+
161+
return (
162+
<div className="flex h-screen">
163+
<DashboardSidebar />
164+
<div className="flex-1 p-10 overflow-y-auto">
165+
<div className="flex justify-between items-center mb-6">
166+
<h1 className="text-3xl font-bold">Merchant Details</h1>
167+
<div className="flex gap-4">
168+
<Link
169+
href="/admin/merchant"
170+
className="bg-gray-500 text-white px-6 py-2 rounded-md hover:bg-gray-600 transition"
171+
>
172+
Back to Merchants
173+
</Link>
174+
<button
175+
onClick={handleDelete}
176+
className="bg-red-500 text-white px-6 py-2 rounded-md hover:bg-red-600 transition"
177+
>
178+
Delete Merchant
179+
</button>
180+
</div>
181+
</div>
182+
183+
<div className="bg-white rounded-lg shadow-md p-6 mb-6">
184+
<form onSubmit={handleSubmit} className="grid grid-cols-1 md:grid-cols-2 gap-6">
185+
<div>
186+
<label className="block text-gray-700 font-medium mb-2">Name</label>
187+
<input
188+
type="text"
189+
name="name"
190+
value={formData.name}
191+
onChange={handleInputChange}
192+
className="w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
193+
required
194+
/>
195+
</div>
196+
<div>
197+
<label className="block text-gray-700 font-medium mb-2">Email</label>
198+
<input
199+
type="email"
200+
name="email"
201+
value={formData.email}
202+
onChange={handleInputChange}
203+
className="w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
204+
/>
205+
</div>
206+
<div>
207+
<label className="block text-gray-700 font-medium mb-2">Phone</label>
208+
<input
209+
type="text"
210+
name="phone"
211+
value={formData.phone}
212+
onChange={handleInputChange}
213+
className="w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
214+
/>
215+
</div>
216+
<div>
217+
<label className="block text-gray-700 font-medium mb-2">Status</label>
218+
<select
219+
name="status"
220+
value={formData.status}
221+
onChange={handleInputChange}
222+
className="w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
223+
>
224+
<option value="ACTIVE">Active</option>
225+
<option value="INACTIVE">Inactive</option>
226+
</select>
227+
</div>
228+
<div className="md:col-span-2">
229+
<label className="block text-gray-700 font-medium mb-2">Address</label>
230+
<input
231+
type="text"
232+
name="address"
233+
value={formData.address}
234+
onChange={handleInputChange}
235+
className="w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300"
236+
/>
237+
</div>
238+
<div className="md:col-span-2">
239+
<label className="block text-gray-700 font-medium mb-2">Description</label>
240+
<textarea
241+
name="description"
242+
value={formData.description}
243+
onChange={handleInputChange}
244+
className="w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300 h-32"
245+
></textarea>
246+
</div>
247+
<div className="md:col-span-2">
248+
<button
249+
type="submit"
250+
className="bg-blue-500 text-white px-6 py-2 rounded-md hover:bg-blue-600 transition"
251+
>
252+
Save Changes
253+
</button>
254+
</div>
255+
</form>
256+
</div>
257+
258+
<div className="bg-white rounded-lg shadow-md p-6">
259+
<h2 className="text-xl font-bold mb-4">Merchant Products</h2>
260+
{merchant.products.length > 0 ? (
261+
<table className="w-full">
262+
<thead>
263+
<tr className="border-b">
264+
<th className="py-3 text-left">Title</th>
265+
<th className="py-3 text-left">Price</th>
266+
<th className="py-3 text-left">In Stock</th>
267+
<th className="py-3 text-left">Actions</th>
268+
</tr>
269+
</thead>
270+
<tbody>
271+
{merchant.products.map((product) => (
272+
<tr key={product.id} className="border-b hover:bg-gray-50">
273+
<td className="py-4">{product.title}</td>
274+
<td className="py-4">${product.price / 100}</td>
275+
<td className="py-4">{product.inStock}</td>
276+
<td className="py-4">
277+
<Link
278+
href={`/admin/products/${product.id}`}
279+
className="text-blue-500 hover:underline"
280+
>
281+
View
282+
</Link>
283+
</td>
284+
</tr>
285+
))}
286+
</tbody>
287+
</table>
288+
) : (
289+
<p className="text-gray-500">No products for this merchant yet.</p>
290+
)}
291+
</div>
292+
</div>
293+
</div>
294+
);
295+
}

0 commit comments

Comments
 (0)