256 lines
9.9 KiB
TypeScript
256 lines
9.9 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useMemo, useState } from "react";
|
||
import Link from "next/link";
|
||
import { useQuery } from "@tanstack/react-query";
|
||
import { api } from "@/lib/fetcher";
|
||
import { usePageTitle } from "@/hooks/usePageTitle";
|
||
|
||
type StructureInfos = {
|
||
raison_sociale?: string;
|
||
siret?: string;
|
||
forme_juridique?: string;
|
||
declaration?: string;
|
||
convention_collective?: string;
|
||
code_ape?: string;
|
||
rna?: string;
|
||
adresse_siege?: string;
|
||
presidente?: string;
|
||
tresoriere?: string;
|
||
|
||
contact_principal?: string;
|
||
email?: string;
|
||
telephone?: string;
|
||
signataire_contrats?: string;
|
||
signataire_delegation?: string; // "Oui"/"Non"
|
||
|
||
licence_spectacles?: string;
|
||
urssaf?: string;
|
||
audiens?: string;
|
||
conges_spectacles?: string;
|
||
pole_emploi_spectacle?: string;
|
||
recouvrement_pe_spectacle?: string;
|
||
afdas?: string;
|
||
fnas?: string;
|
||
fcap?: string;
|
||
};
|
||
|
||
type Spectacle = {
|
||
nom: string;
|
||
numero_objet?: string; // "N° objet"
|
||
declaration?: string | null; // ISO date/string from Airtable
|
||
};
|
||
|
||
type ClientInfo = {
|
||
id: string;
|
||
name: string;
|
||
api_name?: string;
|
||
} | null;
|
||
|
||
function Line({ label, value }: { label: string; value?: string | number | null }) {
|
||
return (
|
||
<div className="grid grid-cols-3 gap-2 border-b last:border-b-0 py-2">
|
||
<div className="text-slate-500">{label}</div>
|
||
<div className="col-span-2">{value ?? "—"}</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function fmtDateFR(d?: string | null) {
|
||
if (!d) return "—";
|
||
const t = new Date(d);
|
||
if (isNaN(t.getTime())) return d; // fallback to raw if unparsable
|
||
return t.toLocaleDateString("fr-FR");
|
||
}
|
||
|
||
export default function InformationsPage() {
|
||
usePageTitle("Informations de la structure");
|
||
|
||
const [page, setPage] = useState(1);
|
||
const limit = 10;
|
||
|
||
// Récupération dynamique des infos client via /api/me
|
||
const { data: clientInfo } = useQuery({
|
||
queryKey: ["client-info"],
|
||
queryFn: async () => {
|
||
try {
|
||
const res = await fetch("/api/me", {
|
||
cache: "no-store",
|
||
headers: { Accept: "application/json" },
|
||
credentials: "include"
|
||
});
|
||
if (!res.ok) return null;
|
||
const me = await res.json();
|
||
|
||
return {
|
||
id: me.active_org_id || null,
|
||
name: me.active_org_name || "Organisation",
|
||
api_name: me.active_org_api_name
|
||
} as ClientInfo;
|
||
} catch {
|
||
return null;
|
||
}
|
||
},
|
||
staleTime: 30_000, // Cache 30s
|
||
});
|
||
|
||
// 1) Infos structure
|
||
const { data: infosResp, isLoading: loadingInfos, isError: errInfos } = useQuery({
|
||
queryKey: ["structure-infos", clientInfo?.id], // Inclure l'ID client dans la queryKey
|
||
queryFn: () => api<{ infos: StructureInfos }>("/informations", {}, clientInfo), // Passer clientInfo au helper api()
|
||
enabled: !!clientInfo, // Ne pas exécuter si pas d'infos client
|
||
});
|
||
const structure = infosResp?.infos;
|
||
|
||
// 2) Productions (Supabase via API interne)
|
||
const { data: prods, isLoading: loadingProds, isError: errProds } = useQuery({
|
||
queryKey: ["spectacles", page, limit, clientInfo?.id], // Inclure l'ID client dans la queryKey
|
||
queryFn: () => api<{ items: Spectacle[]; total: number; hasMore: boolean }>(`/informations/productions?page=${page}&limit=${limit}`, {}, clientInfo), // Passer clientInfo au helper api()
|
||
enabled: !!clientInfo, // Ne pas exécuter si pas d'infos client
|
||
});
|
||
|
||
const total = prods?.total ?? 0;
|
||
const hasMore = prods?.hasMore ?? false;
|
||
|
||
return (
|
||
<main className="p-4 md:p-6 space-y-4">
|
||
<div className="flex items-center justify-between">
|
||
<h1 className="text-lg font-semibold">Vos informations</h1>
|
||
</div>
|
||
|
||
{/* Bandeau compte spécifique (optionnel) */}
|
||
{/* <div className="rounded-2xl border bg-amber-50 text-amber-900 border-amber-200 p-4 text-sm">
|
||
Compte spécifique — vous pouvez mettre ici un message d’info si besoin.
|
||
</div> */}
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 items-start">
|
||
<div className="space-y-4">
|
||
{/* Colonne gauche : Votre structure */}
|
||
<section className="rounded-2xl border bg-white">
|
||
<div className="px-4 py-3 border-b">
|
||
<h2 className="font-medium">Votre structure</h2>
|
||
</div>
|
||
|
||
<div className="p-4 text-sm">
|
||
{loadingInfos && <div className="text-slate-500">Chargement…</div>}
|
||
{errInfos && <div className="text-red-600">Erreur de chargement.</div>}
|
||
{!!structure && (
|
||
<div className="space-y-2">
|
||
<Line label="Raison sociale" value={structure.raison_sociale} />
|
||
<Line label="SIRET" value={structure.siret} />
|
||
<Line label="Forme juridique" value={structure.forme_juridique} />
|
||
<Line label="Déclaration" value={structure.declaration} />
|
||
<Line label="Convention collective" value={structure.convention_collective} />
|
||
<Line label="Code APE" value={structure.code_ape} />
|
||
<Line label="N° RNA" value={structure.rna} />
|
||
<Line label="Adresse siège" value={structure.adresse_siege} />
|
||
<Line label="Président" value={structure.presidente} />
|
||
<Line label="Trésorier" value={structure.tresoriere} />
|
||
</div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
{/* Vos productions */}
|
||
<section className="rounded-2xl border bg-white">
|
||
<div className="px-4 py-3 border-b">
|
||
<h2 className="font-medium">Vos productions</h2>
|
||
</div>
|
||
|
||
<div className="p-2">
|
||
{loadingProds && <div className="p-3 text-slate-500">Chargement…</div>}
|
||
{errProds && <div className="p-3 text-red-600">Erreur de chargement.</div>}
|
||
{!!prods?.items?.length ? (
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="text-left border-b">
|
||
<th className="px-3 py-2">Production</th>
|
||
<th className="px-3 py-2">Déclaration</th>
|
||
<th className="px-3 py-2">N° d’objet</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{prods.items.map((s, i) => (
|
||
<tr key={`${s.nom}-${s.numero_objet ?? i}`} className="border-b last:border-b-0">
|
||
<td className="px-3 py-2">{s.nom}</td>
|
||
<td className="px-3 py-2">{fmtDateFR(s.declaration)}</td>
|
||
<td className="px-3 py-2">{s.numero_objet ?? "—"}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
) : (
|
||
<div className="p-3 text-slate-500">Aucune production.</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex items-center justify-end gap-2 p-3 border-t">
|
||
<button
|
||
className="px-3 py-1.5 rounded-lg border bg-white hover:bg-slate-50"
|
||
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
||
disabled={page === 1}
|
||
>
|
||
Précédent
|
||
</button>
|
||
<div className="text-sm text-slate-500">Page {page}</div>
|
||
<button
|
||
className="px-3 py-1.5 rounded-lg border bg-white hover:bg-slate-50 disabled:opacity-50"
|
||
onClick={() => setPage((p) => (hasMore ? p + 1 : p))}
|
||
disabled={!hasMore}
|
||
>
|
||
Suivant
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
{/* Colonne droite : Contact + Caisses */}
|
||
<section className="rounded-2xl border bg-white">
|
||
<div className="px-4 py-3 border-b">
|
||
<h2 className="font-medium">Informations de contact</h2>
|
||
</div>
|
||
<div className="p-4 text-sm">
|
||
{!!structure ? (
|
||
<div className="space-y-2">
|
||
<Line label="Contact principal" value={structure.contact_principal} />
|
||
<Line label="Adresse email" value={structure.email} />
|
||
<Line label="Tél contact" value={structure.telephone} />
|
||
<Line label="Signataire des contrats" value={structure.signataire_contrats} />
|
||
<Line label="Signataire agissant par délégation ?" value={structure.signataire_delegation} />
|
||
</div>
|
||
) : (
|
||
<div className="text-slate-500">—</div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="rounded-2xl border bg-white">
|
||
<div className="px-4 py-3 border-b flex items-center justify-between">
|
||
<h2 className="font-medium">Caisses & organismes</h2>
|
||
</div>
|
||
<div className="p-4 text-sm">
|
||
{!!structure ? (
|
||
<div className="space-y-2">
|
||
<Line label="Licence spectacles" value={structure.licence_spectacles} />
|
||
<Line label="URSSAF" value={structure.urssaf} />
|
||
<Line label="AUDIENS" value={structure.audiens} />
|
||
<Line label="Congés Spectacles" value={structure.conges_spectacles} />
|
||
<Line label="Pôle Emploi Spectacle" value={structure.pole_emploi_spectacle} />
|
||
<Line label="Recouvrement PE Spectacle" value={structure.recouvrement_pe_spectacle} />
|
||
<Line label="AFDAS" value={structure.afdas} />
|
||
<Line label="FNAS" value={structure.fnas} />
|
||
<Line label="FCAP" value={structure.fcap} />
|
||
</div>
|
||
) : (
|
||
<div className="text-slate-500">—</div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|