diff --git a/apps/main/src/pages/tablos.tsx b/apps/main/src/pages/tablos.tsx
index 0bd3e3d..52f6bc7 100644
--- a/apps/main/src/pages/tablos.tsx
+++ b/apps/main/src/pages/tablos.tsx
@@ -1,7 +1,376 @@
-export function TablosPage() {
+import { cn } from "@xtablo/shared";
+import type { UserTablo } from "@xtablo/shared/types/tablos.types";
+import { LoadingSpinner } from "@ui/components/LoadingSpinner";
+import {
+ CalendarIcon,
+ FilterIcon,
+ Grid3x3Icon,
+ ListIcon,
+ PlusIcon,
+ SearchIcon,
+ Trash2Icon,
+} from "lucide-react";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { useNavigate, useSearchParams } from "react-router-dom";
+import { CreateTabloModal } from "../components/CreateTabloModal";
+import { DeleteTabloModal } from "../components/DeleteTabloModal";
+import { useCreateTablo, useDeleteTablo, useTablosList } from "../hooks/tablos";
+
+// ─── Status helpers ───────────────────────────────────────────────────────────
+
+function getStatusConfig(status: string) {
+ switch (status) {
+ case "in_progress":
+ return {
+ label: "En cours",
+ badgeClass: "bg-[#FFF4E2] text-[#DB9729] border border-[#DB9729]",
+ progress: 50,
+ };
+ case "done":
+ return {
+ label: "Terminé",
+ badgeClass: "bg-green-50 text-green-600 border border-green-200 dark:bg-green-950/30 dark:text-green-400 dark:border-green-800",
+ progress: 100,
+ };
+ default:
+ return {
+ label: "À faire",
+ badgeClass: "bg-blue-50 text-blue-600 border border-blue-200 dark:bg-blue-950/30 dark:text-blue-400 dark:border-blue-800",
+ progress: 0,
+ };
+ }
+}
+
+function formatDate(dateStr: string) {
+ return new Intl.DateTimeFormat("fr-FR", {
+ month: "short",
+ day: "2-digit",
+ year: "numeric",
+ }).format(new Date(dateStr));
+}
+
+// ─── Card view ────────────────────────────────────────────────────────────────
+
+function TabloCard({
+ tablo,
+ onClick,
+ onDelete,
+}: {
+ tablo: UserTablo;
+ onClick: (id: string) => void;
+ onDelete: (id: string) => void;
+}) {
+ const { t } = useTranslation("pages");
+ const { label, badgeClass, progress } = getStatusConfig(tablo.status);
+
return (
-
-
Tablos
+
onClick(tablo.id)}
+ >
+ {/* Status + delete */}
+
+
+ {label}
+
+
+
+
+ {/* Icon + name */}
+
+
+ {tablo.image ? (
+

+ ) : (
+ tablo.name.charAt(0).toUpperCase()
+ )}
+
+
+ {tablo.name}
+
+
+
+ {/* Date */}
+
+
+ {formatDate(tablo.created_at)}
+
+
+ {/* Progress */}
+
+
+ {t("tablo.card.progress")} :
+ {progress}%
+
+
+
+
+ {/* Footer */}
+
+
+ Créé le {formatDate(tablo.created_at)}
+
+
+
+ );
+}
+
+// ─── List row ─────────────────────────────────────────────────────────────────
+
+function TabloRow({
+ tablo,
+ onClick,
+ onDelete,
+}: {
+ tablo: UserTablo;
+ onClick: (id: string) => void;
+ onDelete: (id: string) => void;
+}) {
+ const { label, badgeClass, progress } = getStatusConfig(tablo.status);
+
+ return (
+
onClick(tablo.id)}
+ >
+
+
+
+ {tablo.image ? (
+ 
+ ) : (
+ tablo.name.charAt(0).toUpperCase()
+ )}
+
+ {tablo.name}
+
+ |
+
+ {label}
+ |
+
+
+
+ {formatDate(tablo.created_at)}
+
+ |
+
+
+ |
+
+
+ |
+
+ );
+}
+
+// ─── Page ─────────────────────────────────────────────────────────────────────
+
+export function TablosPage() {
+ const { t } = useTranslation("pages");
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const searchQuery = searchParams.get("q")?.toLowerCase() ?? "";
+
+ const [viewMode, setViewMode] = useState<"card" | "list">("card");
+ const [statusFilter, setStatusFilter] = useState("all");
+ const [showCreateModal, setShowCreateModal] = useState(false);
+ const [deleteTabloId, setDeleteTabloId] = useState
(null);
+
+ const { data: tablos = [], isLoading } = useTablosList();
+ const createTablo = useCreateTablo();
+ const { mutateAsync: deleteTablo, isPending: isDeleting } = useDeleteTablo();
+
+ const deleteTarget = tablos.find((t) => t.id === deleteTabloId) ?? null;
+
+ const filteredTablos = tablos.filter((tablo) => {
+ const matchesSearch = !searchQuery || tablo.name.toLowerCase().includes(searchQuery);
+ const matchesStatus = statusFilter === "all" || tablo.status === statusFilter;
+ return matchesSearch && matchesStatus;
+ });
+
+ const statusFilters = [
+ { value: "all", label: t("tablo.filter.all") },
+ { value: "todo", label: t("tablo.filter.todo") },
+ { value: "in_progress", label: t("tablo.filter.inProgress") },
+ { value: "done", label: t("tablo.filter.done") },
+ ];
+
+ return (
+
+ {/* Header */}
+
+
+ {t("tablo.projectList.title")}
+
+
+
+
+ {/* View tabs */}
+
+ {[
+ { id: "card" as const, label: t("tablo.view.grid"), Icon: Grid3x3Icon },
+ { id: "list" as const, label: t("tablo.view.list"), Icon: ListIcon },
+ ].map(({ id, label, Icon }) => (
+
+ ))}
+
+
+ {/* Search + status filter */}
+
+
+
+
+
+
+ {statusFilters.map((f) => (
+
+ ))}
+
+
+
+ {/* Content */}
+ {isLoading ? (
+
+
+
+ ) : filteredTablos.length === 0 ? (
+
+
+
+
+
Aucun projet trouvé
+
+ {searchQuery ? "Essayez un autre terme de recherche" : "Créez votre premier projet"}
+
+
+ ) : viewMode === "card" ? (
+
+ {filteredTablos.map((tablo) => (
+ navigate(`/tablos/${id}`)}
+ onDelete={setDeleteTabloId}
+ />
+ ))}
+
+ ) : (
+
+
+
+
+ | Projet |
+ Statut |
+ Créé le |
+ Progression |
+ |
+
+
+
+ {filteredTablos.map((tablo) => (
+ navigate(`/tablos/${id}`)}
+ onDelete={setDeleteTabloId}
+ />
+ ))}
+
+
+
+ )}
+
+ {/* Create modal */}
+ {showCreateModal && (
+
setShowCreateModal(false)}
+ onCreate={(tabloData) => {
+ createTablo.mutate({ ...tabloData, status: "todo" });
+ setShowCreateModal(false);
+ }}
+ />
+ )}
+
+ {/* Delete modal */}
+ setDeleteTabloId(null)}
+ onConfirm={async (id) => {
+ await deleteTablo(id);
+ setDeleteTabloId(null);
+ }}
+ isDeleting={isDeleting}
+ />
);
}