Add CRUD tablos
This commit is contained in:
parent
976b51ca1b
commit
5366524602
7 changed files with 685 additions and 262 deletions
30
sql/08_create_tablos_table.sql
Normal file
30
sql/08_create_tablos_table.sql
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
-- Create tablos table
|
||||
CREATE TABLE IF NOT EXISTS tablos (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id UUID NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
image TEXT,
|
||||
color VARCHAR(50),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'todo',
|
||||
position INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE DEFAULT NULL,
|
||||
|
||||
-- Constraint to ensure status is one of the allowed values
|
||||
CONSTRAINT tablos_status_check CHECK (status IN ('todo', 'in_progress', 'done'))
|
||||
);
|
||||
|
||||
-- Enable Row Level Security
|
||||
ALTER TABLE tablos ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Create policy to allow users to see only their own tablos
|
||||
CREATE POLICY "Users can view own tablos" ON tablos
|
||||
FOR SELECT USING (auth.uid() = user_id);
|
||||
|
||||
-- Create policy to allow users to insert their own tablos
|
||||
CREATE POLICY "Users can insert own tablos" ON tablos
|
||||
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
-- Create policy to allow users to update their own tablos
|
||||
CREATE POLICY "Users can update own tablos" ON tablos
|
||||
FOR UPDATE USING (auth.uid() = user_id);
|
||||
|
|
@ -2,18 +2,16 @@ import { useState } from "react";
|
|||
import { ImageColorPicker } from "./ImageColorPicker";
|
||||
import { ClickOutside } from "./ClickOutside";
|
||||
import { StatusPicker } from "./StatusPicker";
|
||||
import { Database } from "@ui/types/database.types";
|
||||
|
||||
interface Tablo {
|
||||
id: number;
|
||||
name: string;
|
||||
image?: string;
|
||||
color?: string;
|
||||
status: "todo" | "in_progress" | "done";
|
||||
}
|
||||
type Tablo = Database["public"]["Tables"]["tablos"]["Row"];
|
||||
type StatusType = "todo" | "in_progress" | "done";
|
||||
|
||||
interface CreateTabloModalProps {
|
||||
onClose: () => void;
|
||||
onCreate: (tabloData: Omit<Tablo, "id">) => void;
|
||||
onCreate: (
|
||||
tabloData: Omit<Tablo, "id" | "user_id" | "created_at" | "deleted_at">
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const CreateTabloModal = ({
|
||||
|
|
@ -26,9 +24,7 @@ export const CreateTabloModal = ({
|
|||
"https://images.unsplash.com/photo-1553877522-43269d4ea984?w=400&h=250&fit=crop&crop=center"
|
||||
);
|
||||
const [selectedColor, setSelectedColor] = useState("bg-blue-500");
|
||||
const [selectedStatus, setSelectedStatus] = useState<
|
||||
"todo" | "in_progress" | "done"
|
||||
>("todo");
|
||||
const [selectedStatus, setSelectedStatus] = useState<StatusType>("todo");
|
||||
|
||||
const resetForm = () => {
|
||||
setNewTabloName("");
|
||||
|
|
@ -51,8 +47,8 @@ export const CreateTabloModal = ({
|
|||
name: newTabloName.trim(),
|
||||
status: selectedStatus,
|
||||
...(creationMode === "image"
|
||||
? { image: selectedImage }
|
||||
: { color: selectedColor }),
|
||||
? { image: selectedImage, color: null }
|
||||
: { image: null, color: selectedColor }),
|
||||
};
|
||||
onCreate(tabloData);
|
||||
resetForm();
|
||||
|
|
|
|||
116
ui/src/components/DeleteTabloModal.tsx
Normal file
116
ui/src/components/DeleteTabloModal.tsx
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import { ClickOutside } from "./ClickOutside";
|
||||
import { Database } from "@ui/types/database.types";
|
||||
|
||||
type Tablo = Database["public"]["Tables"]["tablos"]["Row"];
|
||||
|
||||
interface DeleteTabloModalProps {
|
||||
tablo: Tablo | null;
|
||||
onClose: () => void;
|
||||
onConfirm: (tabloId: number) => void;
|
||||
isDeleting: boolean;
|
||||
}
|
||||
|
||||
export const DeleteTabloModal = ({
|
||||
tablo,
|
||||
onClose,
|
||||
onConfirm,
|
||||
isDeleting,
|
||||
}: DeleteTabloModalProps) => {
|
||||
if (!tablo) return null;
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(tablo.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
|
||||
<ClickOutside onClickOutside={onClose}>
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-md mx-4">
|
||||
{/* Header */}
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-red-100 dark:bg-red-900/20 rounded-full flex items-center justify-center mr-4">
|
||||
<svg
|
||||
className="w-6 h-6 text-red-600 dark:text-red-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
Supprimer le tablo
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Cette action est irréversible
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-3">
|
||||
Êtes-vous sûr de vouloir supprimer le tablo{" "}
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
“{tablo.name}”
|
||||
</span>{" "}
|
||||
?
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Toutes les données associées à ce tablo seront perdues
|
||||
définitivement.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex justify-end space-x-3">
|
||||
<button
|
||||
type="button"
|
||||
className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-md transition-colors"
|
||||
onClick={onClose}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
onClick={handleConfirm}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting && (
|
||||
<svg
|
||||
className="w-4 h-4 animate-spin"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
)}
|
||||
{isDeleting ? "Suppression..." : "Supprimer"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ClickOutside>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -2,43 +2,40 @@ import { ClickOutside } from "./ClickOutside";
|
|||
import { useState } from "react";
|
||||
import { ImageColorPicker } from "./ImageColorPicker";
|
||||
import { StatusPicker } from "./StatusPicker";
|
||||
import { Database } from "@ui/types/database.types";
|
||||
|
||||
interface Tablo {
|
||||
id: number;
|
||||
name: string;
|
||||
image?: string;
|
||||
color?: string;
|
||||
status: "todo" | "in_progress" | "done";
|
||||
}
|
||||
type Tablo = Database["public"]["Tables"]["tablos"]["Row"];
|
||||
type StatusType = "todo" | "in_progress" | "done";
|
||||
|
||||
interface TabloModalProps {
|
||||
tablo: Tablo | null;
|
||||
onEdit: (updatedTablo: Tablo) => void;
|
||||
onClose: () => void;
|
||||
onSave?: (updatedTablo: Tablo) => void;
|
||||
}
|
||||
|
||||
export const TabloModal = ({ tablo, onClose, onSave }: TabloModalProps) => {
|
||||
export const TabloModal = ({ tablo, onClose, onEdit }: TabloModalProps) => {
|
||||
const [editData, setEditData] = useState<Tablo | null>(tablo);
|
||||
const [isEditingName, setIsEditingName] = useState(false);
|
||||
|
||||
const [creationMode, setCreationMode] = useState<"image" | "color">("color");
|
||||
const [selectedColor, setSelectedColor] = useState("bg-blue-500");
|
||||
const [selectedColor, setSelectedColor] = useState(
|
||||
tablo?.color || "bg-blue-500"
|
||||
);
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setEditData(null);
|
||||
};
|
||||
|
||||
const handleSaveEdit = () => {
|
||||
if (editData && onSave) {
|
||||
if (editData && onEdit) {
|
||||
// Clear the unused field based on selection
|
||||
const updatedTablo = {
|
||||
...editData,
|
||||
image: creationMode === "image" ? editData.image : undefined,
|
||||
color: creationMode === "color" ? editData.color : undefined,
|
||||
//TODO: image: creationMode === "image" ? editData.image : null,
|
||||
color: creationMode === "color" ? selectedColor : null,
|
||||
};
|
||||
onSave(updatedTablo);
|
||||
onEdit(updatedTablo);
|
||||
}
|
||||
setEditData(null);
|
||||
};
|
||||
|
||||
if (!tablo) return null;
|
||||
|
|
@ -93,7 +90,7 @@ export const TabloModal = ({ tablo, onClose, onSave }: TabloModalProps) => {
|
|||
<div className="space-y-4 mb-4">
|
||||
<div>
|
||||
<StatusPicker
|
||||
selectedStatus={currentData.status}
|
||||
selectedStatus={currentData.status as StatusType}
|
||||
setSelectedStatus={(status) =>
|
||||
setEditData((prev) => (prev ? { ...prev, status } : null))
|
||||
}
|
||||
|
|
|
|||
97
ui/src/hooks/tablos.ts
Normal file
97
ui/src/hooks/tablos.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Database } from "@ui/types/database.types";
|
||||
import { supabase } from "./auth";
|
||||
import { useSession } from "@ui/contexts/SessionContext";
|
||||
|
||||
type Tablo = Database["public"]["Tables"]["tablos"];
|
||||
|
||||
type TabloInsert = Tablo["Insert"];
|
||||
type TabloUpdate = Tablo["Update"];
|
||||
|
||||
// Fetch all tablos
|
||||
export const useTablosList = () => {
|
||||
return useQuery({
|
||||
queryKey: ["tablos"],
|
||||
queryFn: async () => {
|
||||
const { data, error } = await supabase
|
||||
.from("tablos")
|
||||
.select("*")
|
||||
.is("deleted_at", null)
|
||||
.order("position", { ascending: true });
|
||||
if (error) throw error;
|
||||
return data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Fetch single tablo
|
||||
export const useTablo = (id: number) => {
|
||||
return useQuery({
|
||||
queryKey: ["tablos", id],
|
||||
queryFn: async () => {
|
||||
const { data, error } = await supabase
|
||||
.from("tablos")
|
||||
.select("*")
|
||||
.eq("id", id)
|
||||
.is("deleted_at", null);
|
||||
if (error) throw error;
|
||||
return data[0];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Create new tablo
|
||||
export const useCreateTablo = () => {
|
||||
const { session } = useSession();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (tablo: Omit<TabloInsert, "user_id">) => {
|
||||
const { error } = await supabase.from("tablos").insert({
|
||||
...tablo,
|
||||
user_id: session?.user.id ?? "",
|
||||
});
|
||||
if (error) throw error;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tablos"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Update tablo
|
||||
export const useUpdateTablo = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ id, ...tablo }: TabloUpdate & { id: number }) => {
|
||||
const { error } = await supabase
|
||||
.from("tablos")
|
||||
.update(tablo)
|
||||
.eq("id", id);
|
||||
if (error) throw error;
|
||||
},
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tablos"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["tablos", id] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Delete tablo (soft delete)
|
||||
export const useDeleteTablo = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (id: number) => {
|
||||
const { error } = await supabase
|
||||
.from("tablos")
|
||||
.update({ deleted_at: new Date().toISOString() })
|
||||
.eq("id", id);
|
||||
if (error) throw error;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["tablos"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -1,66 +1,30 @@
|
|||
import { SignOutButton } from "@ui/components/SignOutButton";
|
||||
import { CreateTabloModal } from "@ui/components/CreateTabloModal";
|
||||
import { TabloModal } from "@ui/components/TabloModal";
|
||||
import { DeleteTabloModal } from "@ui/components/DeleteTabloModal";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
useTablosList,
|
||||
useCreateTablo,
|
||||
useUpdateTablo,
|
||||
useDeleteTablo,
|
||||
} from "@ui/hooks/tablos";
|
||||
import { Database } from "@ui/types/database.types";
|
||||
import { LoadingSpinner } from "@ui/components/LoadingSpinner";
|
||||
|
||||
interface Tablo {
|
||||
id: number;
|
||||
name: string;
|
||||
image?: string;
|
||||
color?: string;
|
||||
status: "todo" | "in_progress" | "done";
|
||||
}
|
||||
type Tablo = Database["public"]["Tables"]["tablos"]["Row"];
|
||||
|
||||
export const TabloPage = () => {
|
||||
const [contextMenuTablo, setContextMenuTablo] = useState<number | null>(null);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [viewingTablo, setViewingTablo] = useState<Tablo | null>(null);
|
||||
const [deletingTablo, setDeletingTablo] = useState<Tablo | null>(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
// Sample tablo data - in a real app this would come from an API
|
||||
const [tablos, setTablos] = useState<Tablo[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: "Projet Alpha",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1553877522-43269d4ea984?w=400&h=250&fit=crop&crop=center",
|
||||
status: "in_progress",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Marketing Q4",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=400&h=250&fit=crop&crop=center",
|
||||
status: "done",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Équipe Dev",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=400&h=250&fit=crop&crop=center",
|
||||
status: "todo",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Budget 2024",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1554224155-6726b3ff858f?w=400&h=250&fit=crop&crop=center",
|
||||
status: "in_progress",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Roadmap",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1611224923853-80b023f02d71?w=400&h=250&fit=crop&crop=center",
|
||||
status: "todo",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Support Client",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center",
|
||||
status: "done",
|
||||
},
|
||||
]);
|
||||
const { data: tablos, isLoading, error } = useTablosList();
|
||||
const createTabloMutation = useCreateTablo();
|
||||
const { mutateAsync: updateTablo } = useUpdateTablo();
|
||||
const { mutateAsync: deleteTablo } = useDeleteTablo();
|
||||
|
||||
const menuItems = [
|
||||
{ name: "Conversations" },
|
||||
|
|
@ -76,42 +40,30 @@ export const TabloPage = () => {
|
|||
setIsCreateModalOpen(false);
|
||||
};
|
||||
|
||||
const createNewTablo = (tabloData: Omit<Tablo, "id">) => {
|
||||
const newId = Math.max(...tablos.map((t) => t.id), 0) + 1;
|
||||
const newTablo: Tablo = {
|
||||
id: newId,
|
||||
...tabloData,
|
||||
};
|
||||
setTablos([...tablos, newTablo]);
|
||||
setIsCreateModalOpen(false);
|
||||
const createNewTablo = async (
|
||||
tabloData: Omit<Tablo, "id" | "user_id" | "created_at" | "deleted_at">
|
||||
) => {
|
||||
try {
|
||||
await createTabloMutation.mutateAsync(tabloData);
|
||||
setIsCreateModalOpen(false);
|
||||
} catch (error) {
|
||||
console.error("Error creating tablo:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Tablo movement functions
|
||||
// Tablo movement functions - simplified for now, would need position field in DB
|
||||
const moveTabloLeft = (tabloId: number) => {
|
||||
const currentIndex = tablos.findIndex((t) => t.id === tabloId);
|
||||
if (currentIndex > 0) {
|
||||
const newTablos = [...tablos];
|
||||
[newTablos[currentIndex - 1], newTablos[currentIndex]] = [
|
||||
newTablos[currentIndex],
|
||||
newTablos[currentIndex - 1],
|
||||
];
|
||||
setTablos(newTablos);
|
||||
}
|
||||
// TODO: Implement with proper position field in database
|
||||
console.log("Moving tablo left:", tabloId);
|
||||
};
|
||||
|
||||
const moveTabloRight = (tabloId: number) => {
|
||||
const currentIndex = tablos.findIndex((t) => t.id === tabloId);
|
||||
if (currentIndex < tablos.length - 1) {
|
||||
const newTablos = [...tablos];
|
||||
[newTablos[currentIndex], newTablos[currentIndex + 1]] = [
|
||||
newTablos[currentIndex + 1],
|
||||
newTablos[currentIndex],
|
||||
];
|
||||
setTablos(newTablos);
|
||||
}
|
||||
// TODO: Implement with proper position field in database
|
||||
console.log("Moving tablo right:", tabloId);
|
||||
};
|
||||
|
||||
const openTablo = (tabloId: number) => {
|
||||
if (!tablos) return;
|
||||
const tablo = tablos.find((t) => t.id === tabloId);
|
||||
if (tablo) {
|
||||
setViewingTablo(tablo);
|
||||
|
|
@ -122,7 +74,7 @@ export const TabloPage = () => {
|
|||
setViewingTablo(null);
|
||||
};
|
||||
|
||||
const getStatusLabel = (status: Tablo["status"]) => {
|
||||
const getStatusLabel = (status: string) => {
|
||||
switch (status) {
|
||||
case "todo":
|
||||
return "À faire";
|
||||
|
|
@ -135,7 +87,7 @@ export const TabloPage = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const getStatusBadgeColor = (status: Tablo["status"]) => {
|
||||
const getStatusBadgeColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "todo":
|
||||
return "bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300";
|
||||
|
|
@ -148,14 +100,144 @@ export const TabloPage = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const changeTabloStatus = (tabloId: number, newStatus: Tablo["status"]) => {
|
||||
setTablos(
|
||||
tablos.map((tablo) =>
|
||||
tablo.id === tabloId ? { ...tablo, status: newStatus } : tablo
|
||||
)
|
||||
);
|
||||
const changeTabloStatus = async (tabloId: number, newStatus: string) => {
|
||||
try {
|
||||
await updateTablo({
|
||||
id: tabloId,
|
||||
status: newStatus,
|
||||
});
|
||||
setContextMenuTablo(null);
|
||||
} catch (error) {
|
||||
console.error("Error updating tablo status:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onEditTablo = (tablo: Tablo) => {
|
||||
updateTablo(tablo, {
|
||||
onSuccess: () => {
|
||||
closeTabloModal();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteTablo = (tabloId: number) => {
|
||||
if (!tablos) return;
|
||||
const tablo = tablos.find((t) => t.id === tabloId);
|
||||
if (tablo) {
|
||||
setDeletingTablo(tablo);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDeleteTablo = async (tabloId: number) => {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteTablo(tabloId);
|
||||
setDeletingTablo(null);
|
||||
} catch (error) {
|
||||
console.error("Error deleting tablo:", error);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const cancelDeleteTablo = () => {
|
||||
setDeletingTablo(null);
|
||||
setIsDeleting(false);
|
||||
};
|
||||
|
||||
// Show loading state
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Tablos
|
||||
</h1>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700 hover:shadow-lg hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 shadow-md"
|
||||
onClick={openCreateModal}
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||
></path>
|
||||
</svg>
|
||||
<span>Nouveau tablo</span>
|
||||
</button>
|
||||
<SignOutButton />
|
||||
</div>
|
||||
</div>
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div className="flex justify-center items-center min-h-64">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Show error state
|
||||
if (error) {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Tablos
|
||||
</h1>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700 hover:shadow-lg hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 shadow-md"
|
||||
onClick={openCreateModal}
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||
></path>
|
||||
</svg>
|
||||
<span>Nouveau tablo</span>
|
||||
</button>
|
||||
<SignOutButton />
|
||||
</div>
|
||||
</div>
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div className="flex justify-center items-center min-h-64">
|
||||
<div className="text-center">
|
||||
<p className="text-red-600 dark:text-red-400 mb-2">
|
||||
Erreur lors du chargement des tablos
|
||||
</p>
|
||||
<p className="text-gray-500 dark:text-gray-400 text-sm">
|
||||
{error instanceof Error
|
||||
? error.message
|
||||
: "Une erreur inconnue s'est produite"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const renderTablo = (tablo: Tablo) => {
|
||||
return (
|
||||
<div
|
||||
|
|
@ -174,7 +256,7 @@ export const TabloPage = () => {
|
|||
}}
|
||||
>
|
||||
{/* Image or Color */}
|
||||
<div className="relative h-56">
|
||||
<div className="relative h-56 group">
|
||||
{tablo.image ? (
|
||||
<img
|
||||
src={tablo.image}
|
||||
|
|
@ -183,13 +265,40 @@ export const TabloPage = () => {
|
|||
/>
|
||||
) : (
|
||||
<div
|
||||
className={`w-full h-full ${tablo.color} flex items-center justify-center`}
|
||||
className={`w-full h-full ${
|
||||
tablo.color || "bg-gray-400"
|
||||
} flex items-center justify-center`}
|
||||
>
|
||||
<h3 className="text-white font-bold text-2xl text-center px-4">
|
||||
{tablo.name}
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Trash Icon */}
|
||||
<button
|
||||
className="absolute top-2 right-2 p-1.5 bg-red-500 hover:bg-red-600 text-white rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDeleteTablo(tablo.id);
|
||||
}}
|
||||
title="Supprimer le tablo"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
|
|
@ -275,7 +384,6 @@ export const TabloPage = () => {
|
|||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
changeTabloStatus(tablo.id, "todo");
|
||||
setContextMenuTablo(null);
|
||||
}}
|
||||
>
|
||||
<span>À faire</span>
|
||||
|
|
@ -288,7 +396,6 @@ export const TabloPage = () => {
|
|||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
changeTabloStatus(tablo.id, "in_progress");
|
||||
setContextMenuTablo(null);
|
||||
}}
|
||||
>
|
||||
<span>En cours</span>
|
||||
|
|
@ -301,7 +408,6 @@ export const TabloPage = () => {
|
|||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
changeTabloStatus(tablo.id, "done");
|
||||
setContextMenuTablo(null);
|
||||
}}
|
||||
>
|
||||
<span>Terminé</span>
|
||||
|
|
@ -326,6 +432,7 @@ export const TabloPage = () => {
|
|||
type="button"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700 hover:shadow-lg hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 shadow-md"
|
||||
onClick={openCreateModal}
|
||||
disabled={createTabloMutation.isPending}
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
|
|
@ -341,17 +448,50 @@ export const TabloPage = () => {
|
|||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||
></path>
|
||||
</svg>
|
||||
<span>Nouveau tablo</span>
|
||||
<span>
|
||||
{createTabloMutation.isPending ? "Création..." : "Nouveau tablo"}
|
||||
</span>
|
||||
</button>
|
||||
<SignOutButton />
|
||||
</div>
|
||||
</div>
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{/* Render tablos */}
|
||||
{tablos.map((tablo) => renderTablo(tablo))}
|
||||
</div>
|
||||
{tablos && tablos.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{/* Render tablos */}
|
||||
{tablos.map((tablo) => renderTablo(tablo))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-center items-center min-h-64">
|
||||
<div className="text-center">
|
||||
<p className="text-gray-500 dark:text-gray-400 mb-4">
|
||||
Aucun tablo trouvé
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-1.5 px-4 py-2 text-sm text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200"
|
||||
onClick={openCreateModal}
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||
></path>
|
||||
</svg>
|
||||
<span>Créer votre premier tablo</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
|
@ -365,7 +505,21 @@ export const TabloPage = () => {
|
|||
|
||||
{/* Tablo Details Modal */}
|
||||
{!!viewingTablo && (
|
||||
<TabloModal tablo={viewingTablo} onClose={closeTabloModal} />
|
||||
<TabloModal
|
||||
tablo={viewingTablo}
|
||||
onEdit={onEditTablo}
|
||||
onClose={closeTabloModal}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Delete Tablo Modal */}
|
||||
{!!deletingTablo && (
|
||||
<DeleteTabloModal
|
||||
tablo={deletingTablo}
|
||||
onClose={cancelDeleteTablo}
|
||||
onConfirm={confirmDeleteTablo}
|
||||
isDeleting={isDeleting}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,230 +4,263 @@ export type Json =
|
|||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json | undefined }
|
||||
| Json[]
|
||||
| Json[];
|
||||
|
||||
export type Database = {
|
||||
public: {
|
||||
Tables: {
|
||||
devis: {
|
||||
Row: {
|
||||
client_email: string
|
||||
created_at: string
|
||||
date: string
|
||||
due_date: string
|
||||
id: string
|
||||
items: Json
|
||||
notes: string | null
|
||||
number: string
|
||||
status: Database["public"]["Enums"]["devis_status"]
|
||||
subtotal: number
|
||||
tax: number
|
||||
terms: string | null
|
||||
total: number
|
||||
updated_at: string
|
||||
user_id: string
|
||||
}
|
||||
client_email: string;
|
||||
created_at: string;
|
||||
date: string;
|
||||
due_date: string;
|
||||
id: string;
|
||||
items: Json;
|
||||
notes: string | null;
|
||||
number: string;
|
||||
status: Database["public"]["Enums"]["devis_status"];
|
||||
subtotal: number;
|
||||
tax: number;
|
||||
terms: string | null;
|
||||
total: number;
|
||||
updated_at: string;
|
||||
user_id: string;
|
||||
};
|
||||
Insert: {
|
||||
client_email: string
|
||||
created_at?: string
|
||||
date: string
|
||||
due_date: string
|
||||
id?: string
|
||||
items?: Json
|
||||
notes?: string | null
|
||||
number: string
|
||||
status?: Database["public"]["Enums"]["devis_status"]
|
||||
subtotal: number
|
||||
tax: number
|
||||
terms?: string | null
|
||||
total: number
|
||||
updated_at?: string
|
||||
user_id: string
|
||||
}
|
||||
client_email: string;
|
||||
created_at?: string;
|
||||
date: string;
|
||||
due_date: string;
|
||||
id?: string;
|
||||
items?: Json;
|
||||
notes?: string | null;
|
||||
number: string;
|
||||
status?: Database["public"]["Enums"]["devis_status"];
|
||||
subtotal: number;
|
||||
tax: number;
|
||||
terms?: string | null;
|
||||
total: number;
|
||||
updated_at?: string;
|
||||
user_id: string;
|
||||
};
|
||||
Update: {
|
||||
client_email?: string
|
||||
created_at?: string
|
||||
date?: string
|
||||
due_date?: string
|
||||
id?: string
|
||||
items?: Json
|
||||
notes?: string | null
|
||||
number?: string
|
||||
status?: Database["public"]["Enums"]["devis_status"]
|
||||
subtotal?: number
|
||||
tax?: number
|
||||
terms?: string | null
|
||||
total?: number
|
||||
updated_at?: string
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
client_email?: string;
|
||||
created_at?: string;
|
||||
date?: string;
|
||||
due_date?: string;
|
||||
id?: string;
|
||||
items?: Json;
|
||||
notes?: string | null;
|
||||
number?: string;
|
||||
status?: Database["public"]["Enums"]["devis_status"];
|
||||
subtotal?: number;
|
||||
tax?: number;
|
||||
terms?: string | null;
|
||||
total?: number;
|
||||
updated_at?: string;
|
||||
user_id?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
feedbacks: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
fd_type: string
|
||||
id: number
|
||||
message: string
|
||||
user_id: string
|
||||
}
|
||||
created_at: string | null;
|
||||
fd_type: string;
|
||||
id: number;
|
||||
message: string;
|
||||
user_id: string;
|
||||
};
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
fd_type: string
|
||||
id?: number
|
||||
message: string
|
||||
user_id: string
|
||||
}
|
||||
created_at?: string | null;
|
||||
fd_type: string;
|
||||
id?: number;
|
||||
message: string;
|
||||
user_id: string;
|
||||
};
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
fd_type?: string
|
||||
id?: number
|
||||
message?: string
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
created_at?: string | null;
|
||||
fd_type?: string;
|
||||
id?: number;
|
||||
message?: string;
|
||||
user_id?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
profiles: {
|
||||
Row: {
|
||||
avatar_url: string | null
|
||||
email: string | null
|
||||
id: string
|
||||
name: string | null
|
||||
}
|
||||
avatar_url: string | null;
|
||||
email: string | null;
|
||||
id: string;
|
||||
name: string | null;
|
||||
};
|
||||
Insert: {
|
||||
avatar_url?: string | null
|
||||
email?: string | null
|
||||
id: string
|
||||
name?: string | null
|
||||
}
|
||||
avatar_url?: string | null;
|
||||
email?: string | null;
|
||||
id: string;
|
||||
name?: string | null;
|
||||
};
|
||||
Update: {
|
||||
avatar_url?: string | null
|
||||
email?: string | null
|
||||
id?: string
|
||||
name?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
}
|
||||
avatar_url?: string | null;
|
||||
email?: string | null;
|
||||
id?: string;
|
||||
name?: string | null;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
tablos: {
|
||||
Row: {
|
||||
color: string | null;
|
||||
created_at: string | null;
|
||||
deleted_at: string | null;
|
||||
id: number;
|
||||
image: string | null;
|
||||
name: string;
|
||||
status: string;
|
||||
user_id: string;
|
||||
};
|
||||
Insert: {
|
||||
color?: string | null;
|
||||
created_at?: string | null;
|
||||
deleted_at?: string | null;
|
||||
id?: number;
|
||||
image?: string | null;
|
||||
name: string;
|
||||
status?: string;
|
||||
user_id: string;
|
||||
};
|
||||
Update: {
|
||||
color?: string | null;
|
||||
created_at?: string | null;
|
||||
deleted_at?: string | null;
|
||||
id?: number;
|
||||
image?: string | null;
|
||||
name?: string;
|
||||
status?: string;
|
||||
user_id?: string;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
};
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
[_ in never]: never;
|
||||
};
|
||||
Functions: {
|
||||
[_ in never]: never
|
||||
}
|
||||
[_ in never]: never;
|
||||
};
|
||||
Enums: {
|
||||
devis_status: "draft" | "sent" | "accepted" | "rejected" | "expired"
|
||||
}
|
||||
devis_status: "draft" | "sent" | "accepted" | "rejected" | "expired";
|
||||
};
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
[_ in never]: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type DefaultSchema = Database[Extract<keyof Database, "public">]
|
||||
type DefaultSchema = Database[Extract<keyof Database, "public">];
|
||||
|
||||
export type Tables<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
||||
| { schema: keyof Database },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof Database
|
||||
schema: keyof Database;
|
||||
}
|
||||
? keyof (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
||||
Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
|
||||
: never = never,
|
||||
: never = never
|
||||
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
|
||||
? (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
||||
Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
||||
Row: infer R
|
||||
Row: infer R;
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])
|
||||
? (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
DefaultSchema["Views"])
|
||||
? (DefaultSchema["Tables"] &
|
||||
DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
|
||||
Row: infer R;
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type TablesInsert<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema["Tables"]
|
||||
| { schema: keyof Database },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof Database
|
||||
schema: keyof Database;
|
||||
}
|
||||
? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
: never = never
|
||||
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
|
||||
? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Insert: infer I
|
||||
Insert: infer I;
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Insert: infer I;
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type TablesUpdate<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema["Tables"]
|
||||
| { schema: keyof Database },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof Database
|
||||
schema: keyof Database;
|
||||
}
|
||||
? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
||||
: never = never,
|
||||
: never = never
|
||||
> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database }
|
||||
? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
||||
Update: infer U
|
||||
Update: infer U;
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
||||
Update: infer U;
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type Enums<
|
||||
DefaultSchemaEnumNameOrOptions extends
|
||||
| keyof DefaultSchema["Enums"]
|
||||
| { schema: keyof Database },
|
||||
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof Database
|
||||
schema: keyof Database;
|
||||
}
|
||||
? keyof Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
|
||||
: never = never,
|
||||
: never = never
|
||||
> = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database }
|
||||
? Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
||||
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
|
||||
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
|
||||
: never
|
||||
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
|
||||
: never;
|
||||
|
||||
export type CompositeTypes<
|
||||
PublicCompositeTypeNameOrOptions extends
|
||||
| keyof DefaultSchema["CompositeTypes"]
|
||||
| { schema: keyof Database },
|
||||
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof Database
|
||||
schema: keyof Database;
|
||||
}
|
||||
? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
|
||||
: never = never,
|
||||
: never = never
|
||||
> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
|
||||
? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
|
||||
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
|
||||
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
|
||||
: never
|
||||
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
|
||||
: never;
|
||||
|
||||
export const Constants = {
|
||||
public: {
|
||||
|
|
@ -235,4 +268,4 @@ export const Constants = {
|
|||
devis_status: ["draft", "sent", "accepted", "rejected", "expired"],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
} as const;
|
||||
|
|
|
|||
Loading…
Reference in a new issue