feat: web sprint 250-262 — plans/clients/tickets/audit/profile/settings/leads/reports #26

Merged
kibin merged 11 commits from feat/web-sprint-250-262 into main 2026-04-01 08:09:45 +00:00
Showing only changes of commit 10531338d0 - Show all commits

View File

@@ -41,6 +41,7 @@ const emptyForm = {
export default function PlansPage() { export default function PlansPage() {
const qc = useQueryClient(); const qc = useQueryClient();
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [activeTab, setActiveTab] = useState<"active" | "archived">("active");
// Modals // Modals
const [showCreate, setShowCreate] = useState(false); const [showCreate, setShowCreate] = useState(false);
@@ -51,11 +52,12 @@ export default function PlansPage() {
const [createForm, setCreateForm] = useState({ ...emptyForm }); const [createForm, setCreateForm] = useState({ ...emptyForm });
const [editForm, setEditForm] = useState({ ...emptyForm }); const [editForm, setEditForm] = useState({ ...emptyForm });
// GET /plans returns a plain array (not paginated) // GET /plans with isActive filter
const { data: allPlans = [], isLoading, isError, refetch } = useQuery<Plan[]>({ const { data: allPlans = [], isLoading, isError, refetch } = useQuery<Plan[]>({
queryKey: ["plans"], queryKey: ["plans", activeTab],
queryFn: async () => { queryFn: async () => {
const res = await api.get<Plan[] | { data: Plan[] }>("/api/v1/plans"); const isActive = activeTab === "active";
const res = await api.get<Plan[] | { data: Plan[] }>(`/api/v1/plans?isActive=${isActive}`);
return Array.isArray(res.data) ? res.data : (res.data as any).data ?? []; return Array.isArray(res.data) ? res.data : (res.data as any).data ?? [];
}, },
}); });
@@ -149,6 +151,24 @@ export default function PlansPage() {
</div> </div>
</div> </div>
{/* Active / Archived tabs */}
<div className="flex gap-1 bg-gray-100 rounded-lg p-1 w-fit">
<button
onClick={() => { setActiveTab("active"); setSearch(""); }}
className={`px-4 py-1.5 rounded-md text-sm font-medium transition-colors ${activeTab === "active" ? "bg-white text-gray-900 shadow-sm" : "text-gray-500 hover:text-gray-700"}`}
data-testid="tab-active"
>
Active
</button>
<button
onClick={() => { setActiveTab("archived"); setSearch(""); }}
className={`px-4 py-1.5 rounded-md text-sm font-medium transition-colors ${activeTab === "archived" ? "bg-white text-gray-900 shadow-sm" : "text-gray-500 hover:text-gray-700"}`}
data-testid="tab-archived"
>
Archived
</button>
</div>
<Card> <Card>
<CardHeader> <CardHeader>
<input <input