169 lines
5.5 KiB
TypeScript
169 lines
5.5 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useSession } from 'next-auth/react';
|
|
|
|
type Club = {
|
|
id: string;
|
|
name: string;
|
|
sportType: string;
|
|
description?: string;
|
|
};
|
|
|
|
export function ClubManagement() {
|
|
const { data: session } = useSession();
|
|
const [clubs, setClubs] = useState<Club[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [isCreating, setIsCreating] = useState(false);
|
|
const [newClub, setNewClub] = useState({ name: '', sportType: 'Tennis', description: '' });
|
|
|
|
useEffect(() => {
|
|
const fetchClubsLocally = async () => {
|
|
try {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs`, {
|
|
headers: { Authorization: `Bearer ${session?.accessToken}` },
|
|
});
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
setClubs(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch clubs', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (session) fetchClubsLocally();
|
|
}, [session]);
|
|
|
|
const fetchClubs = async () => {
|
|
try {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs`, {
|
|
headers: { Authorization: `Bearer ${session?.accessToken}` },
|
|
});
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
setClubs(data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch clubs', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleCreate = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
try {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${session?.accessToken}`,
|
|
},
|
|
body: JSON.stringify({
|
|
name: newClub.name,
|
|
sportType: newClub.sportType === 'Tennis' ? 0 : 1, // Mapping Enum or keep string if api accepts
|
|
description: newClub.description,
|
|
}),
|
|
});
|
|
if (res.ok) {
|
|
setNewClub({ name: '', sportType: 'Tennis', description: '' });
|
|
setIsCreating(false);
|
|
fetchClubs();
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
const handleDelete = async (id: string) => {
|
|
if (!confirm('Are you sure you want to delete this club?')) return;
|
|
try {
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs/${id}`, {
|
|
method: 'DELETE',
|
|
headers: { Authorization: `Bearer ${session?.accessToken}` },
|
|
});
|
|
if (res.ok) {
|
|
fetchClubs();
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
if (loading) return <div>Loading clubs...</div>;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between">
|
|
<h2 className="text-xl font-semibold">All Clubs</h2>
|
|
<button
|
|
onClick={() => setIsCreating(true)}
|
|
className="bg-blue-600 text-white px-4 py-2 rounded shadow hover:bg-blue-700"
|
|
>
|
|
Create New Club
|
|
</button>
|
|
</div>
|
|
|
|
{isCreating && (
|
|
<form onSubmit={handleCreate} className="bg-white p-4 rounded shadow space-y-4 border">
|
|
<h3 className="font-semibold text-lg">New Club</h3>
|
|
<div>
|
|
<label className="block text-sm font-medium">Name</label>
|
|
<input
|
|
required
|
|
className="mt-1 block w-full p-2 border rounded"
|
|
value={newClub.name}
|
|
onChange={e => setNewClub({ ...newClub, name: e.target.value })}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium">Sport Type</label>
|
|
<select
|
|
className="mt-1 block w-full p-2 border rounded"
|
|
value={newClub.sportType}
|
|
onChange={e => setNewClub({ ...newClub, sportType: e.target.value })}
|
|
>
|
|
<option value="Tennis">Tennis</option>
|
|
<option value="Cycling">Cycling</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium">Description</label>
|
|
<textarea
|
|
className="mt-1 block w-full p-2 border rounded"
|
|
value={newClub.description}
|
|
onChange={e => setNewClub({ ...newClub, description: e.target.value })}
|
|
/>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<button type="submit" className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">Save</button>
|
|
<button type="button" onClick={() => setIsCreating(false)} className="px-4 py-2 border rounded hover:bg-gray-50">Cancel</button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{clubs.map(club => (
|
|
<div key={club.id} className="bg-white p-4 rounded shadow border">
|
|
<h3 className="font-bold text-lg">{club.name}</h3>
|
|
<p className="text-sm text-gray-500 mb-2">{club.sportType}</p>
|
|
<p className="text-sm line-clamp-2 mb-4">{club.description || 'No description'}</p>
|
|
<div className="flex justify-end gap-2">
|
|
<button
|
|
onClick={() => handleDelete(club.id)}
|
|
className="text-red-600 hover:text-red-800 text-sm font-medium"
|
|
>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{clubs.length === 0 && <p className="text-gray-500 col-span-full">No clubs found.</p>}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|