darkwatch/client/src/pages/CampaignList.tsx

81 lines
2.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { getCampaigns, createCampaign, deleteCampaign } from "../api";
import type { Campaign } from "../types";
import styles from "./CampaignList.module.css";
export default function CampaignList() {
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
const [newName, setNewName] = useState("");
const navigate = useNavigate();
useEffect(() => {
getCampaigns().then(setCampaigns);
}, []);
async function handleCreate(e: React.FormEvent) {
e.preventDefault();
if (!newName.trim()) return;
try {
const campaign = await createCampaign(newName.trim());
setCampaigns((prev) => [campaign, ...prev]);
setNewName("");
} catch (err) {
console.error("Failed to create campaign:", err);
}
}
async function handleDelete(e: React.MouseEvent, id: number) {
e.stopPropagation();
try {
await deleteCampaign(id);
setCampaigns((prev) => prev.filter((c) => c.id !== id));
} catch (err) {
console.error("Failed to delete campaign:", err);
}
}
return (
<div className={styles.container}>
<form className={styles.createForm} onSubmit={handleCreate}>
<input
className={styles.createInput}
type="text"
placeholder="New campaign name..."
value={newName}
onChange={(e) => setNewName(e.target.value)}
/>
<button className={styles.createBtn} type="submit">
+ Create
</button>
</form>
<div className={styles.campaignGrid}>
{campaigns.length === 0 && (
<p className={styles.empty}>No campaigns yet. Create one above!</p>
)}
{campaigns.map((c) => (
<div
key={c.id}
className={styles.campaignCard}
onClick={() => navigate(`/campaign/${c.id}`)}
>
<div>
<div className={styles.campaignName}>{c.name}</div>
<div className={styles.campaignDate}>
{new Date(c.created_at).toLocaleDateString()}
</div>
</div>
<button
className={styles.deleteBtn}
onClick={(e) => handleDelete(e, c.id)}
title="Delete campaign"
>
</button>
</div>
))}
</div>
</div>
);
}