import { useMutation, useQuery } from "@tanstack/react-query"; import { useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { api, type AvailableCriterion } from "../api"; import { CriteriaControl, pickSource } from "../components/CriteriaControl"; import { EmptyState, ErrorState, LoadingState } from "../components/State"; type Draft = Record; export function WizardPage() { const navigate = useNavigate(); const categories = useQuery({ queryKey: ["product-categories"], queryFn: api.productCategories }); const [name, setName] = useState(""); const [categoryId, setCategoryId] = useState(""); const [market, setMarket] = useState("DE"); const [currency, setCurrency] = useState("EUR"); const [budget, setBudget] = useState(""); const [sourceOverride, setSourceOverride] = useState(null); const [selected, setSelected] = useState([]); const [draft, setDraft] = useState({}); const sources = useQuery({ queryKey: ["category-sources", categoryId], queryFn: () => api.categorySources(categoryId), enabled: Boolean(categoryId), }); const source = sourceOverride || pickSource(sources.data?.items || []); const criteria = useQuery({ queryKey: ["available-criteria", categoryId, source], queryFn: () => api.availableCriteria(categoryId, source!), enabled: Boolean(categoryId && source), }); const create = useMutation({ mutationFn: async () => { if (!source || !sources.data) throw new Error("Keine Source verfügbar"); const chosen = (criteria.data?.criteria || []).filter((item) => selected.includes(item.criterion_definition_id)); const project = await api.createProject({ name, product_category_id: categoryId, market, currency, target_budget: budget || null, criteria: { items: chosen.map((item) => ({ criterion_definition_id: item.criterion_definition_id, operator: draft[item.criterion_definition_id]?.operator || item.operators[0] || ">=", value: coerce(item, draft[item.criterion_definition_id]?.value), enabled_for_discovery: true, })), }, }); const sourceRow = sources.data.items.find((item) => item.source === source) || sources.data.items[0]; await api.createSearch(project.id, { source_id: sourceRow.source, source_category_id: sourceRow.source_category_id, }); return project; }, onSuccess: (project) => navigate(`/projects/${project.id}`), }); const sourceItems = sources.data?.items || []; const needsPicker = sourceItems.filter((item) => item.status === "ACTIVE").length > 1; const visible = useMemo(() => criteria.data?.criteria || [], [criteria.data]); if (categories.isLoading) return ; if (categories.isError) return ; return (

Neues Projekt

{categoryId && sources.isLoading ? : null} {categoryId && !sources.isLoading && sourceItems.length === 0 ? ( Keine Source für diese Produktkategorie verfügbar. ) : null} {needsPicker ? ( ) : source ? (

Quelle: {source}

) : null} {criteria.isLoading ? : null} {visible.length > 0 ? (
{visible.map((item) => { const on = selected.includes(item.criterion_definition_id); return (
{on ? ( ="} value={draft[item.criterion_definition_id]?.value} onChange={(next) => setDraft((current) => ({ ...current, [item.criterion_definition_id]: next }))} /> ) : null}
); })}
) : null} {create.isError ? : null}
); } function coerce(criterion: AvailableCriterion, value: unknown) { if (value == null || value === "") return value; if ((criterion.filter_type || "").startsWith("NUMERIC")) return Number(value); return value; }