Apply linter formatting to components, locales, and pages
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
24fe6b89a9
commit
afe47554c8
21 changed files with 408 additions and 217 deletions
|
|
@ -32,7 +32,7 @@ export function ActionCard({
|
|||
? "bg-[rgb(128,78,236)] text-white border-transparent shadow-lg"
|
||||
: isPrimary
|
||||
? "bg-primary text-white hover:shadow-lg"
|
||||
: "bg-white border border-[#EAECF0] hover:shadow-md",
|
||||
: "bg-white dark:bg-gray-800 border border-[#EAECF0] dark:border-gray-700 hover:shadow-md",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -40,7 +40,7 @@ export function ActionCard({
|
|||
<div
|
||||
className={cn(
|
||||
"w-10 h-10 rounded-[8px] flex items-center justify-center flex-shrink-0",
|
||||
isActive ? "bg-white/20" : "bg-[#F4F3FF]",
|
||||
isActive ? "bg-white/20" : "bg-[#F4F3FF] dark:bg-purple-900/20",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
|
|
@ -57,7 +57,7 @@ export function ActionCard({
|
|||
<span
|
||||
className={cn(
|
||||
"block font-semibold text-lg leading-tight",
|
||||
isActive ? "text-white" : "text-gray-900",
|
||||
isActive ? "text-white" : "text-gray-900 dark:text-gray-100",
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
|
|
@ -65,7 +65,7 @@ export function ActionCard({
|
|||
<p
|
||||
className={cn(
|
||||
"text-sm mt-0.5",
|
||||
isActive ? "text-purple-100" : "text-gray-500",
|
||||
isActive ? "text-purple-100" : "text-gray-500 dark:text-gray-400",
|
||||
)}
|
||||
>
|
||||
{description}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { FolderPlus, MessageCircle, PlusCircle, UserPlus } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ActionCard } from "./ActionCard";
|
||||
|
||||
export interface DashboardActionCardsProps {
|
||||
|
|
@ -17,6 +18,7 @@ export function DashboardActionCards({
|
|||
onInviteTeam,
|
||||
onSendMessage,
|
||||
}: DashboardActionCardsProps) {
|
||||
const { t } = useTranslation("pages");
|
||||
const [selected, setSelected] = useState<CardId | null>(null);
|
||||
|
||||
const handleClick = (id: CardId, callback?: () => void) => {
|
||||
|
|
@ -28,32 +30,32 @@ export function DashboardActionCards({
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-5">
|
||||
<ActionCard
|
||||
icon={<FolderPlus className="w-6 h-6" />}
|
||||
label="Create Project"
|
||||
description="Set goals and scope"
|
||||
label={t("dashboard.actionCards.createProject.label")}
|
||||
description={t("dashboard.actionCards.createProject.description")}
|
||||
isSelected={selected === "createProject"}
|
||||
onClick={() => handleClick("createProject", onCreateProject)}
|
||||
/>
|
||||
|
||||
<ActionCard
|
||||
icon={<PlusCircle className="w-6 h-6" />}
|
||||
label="Create Task"
|
||||
description="Break work into actions"
|
||||
label={t("dashboard.actionCards.createTask.label")}
|
||||
description={t("dashboard.actionCards.createTask.description")}
|
||||
isSelected={selected === "createTask"}
|
||||
onClick={() => handleClick("createTask", onCreateTask)}
|
||||
/>
|
||||
|
||||
<ActionCard
|
||||
icon={<UserPlus className="w-6 h-6" />}
|
||||
label="Invite Team"
|
||||
description="Add collaborators instantly"
|
||||
label={t("dashboard.actionCards.inviteTeam.label")}
|
||||
description={t("dashboard.actionCards.inviteTeam.description")}
|
||||
isSelected={selected === "inviteTeam"}
|
||||
onClick={() => handleClick("inviteTeam", onInviteTeam)}
|
||||
/>
|
||||
|
||||
<ActionCard
|
||||
icon={<MessageCircle className="w-6 h-6" />}
|
||||
label="Send Message"
|
||||
description="Communicate updates fast"
|
||||
label={t("dashboard.actionCards.sendMessage.label")}
|
||||
description={t("dashboard.actionCards.sendMessage.description")}
|
||||
isSelected={selected === "sendMessage"}
|
||||
onClick={() => handleClick("sendMessage", onSendMessage)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -18,19 +18,23 @@ const STATUS_BADGE: Record<
|
|||
{ className: string; labelKey: string }
|
||||
> = {
|
||||
todo: {
|
||||
className: "bg-blue-50 text-blue-600",
|
||||
className:
|
||||
"bg-blue-50 text-blue-600 dark:bg-blue-950/30 dark:text-blue-400",
|
||||
labelKey: "dashboard.taskList.status.todo",
|
||||
},
|
||||
in_progress: {
|
||||
className: "bg-yellow-50 text-yellow-600",
|
||||
className:
|
||||
"bg-yellow-50 text-yellow-600 dark:bg-yellow-950/30 dark:text-yellow-400",
|
||||
labelKey: "dashboard.taskList.status.inProgress",
|
||||
},
|
||||
in_review: {
|
||||
className: "bg-purple-50 text-purple-600",
|
||||
className:
|
||||
"bg-purple-50 text-purple-600 dark:bg-purple-950/30 dark:text-purple-400",
|
||||
labelKey: "dashboard.taskList.status.inReview",
|
||||
},
|
||||
done: {
|
||||
className: "bg-green-50 text-green-600",
|
||||
className:
|
||||
"bg-green-50 text-green-600 dark:bg-green-950/30 dark:text-green-400",
|
||||
labelKey: "dashboard.taskList.status.done",
|
||||
},
|
||||
};
|
||||
|
|
@ -59,67 +63,71 @@ function TaskRow({
|
|||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between gap-4 p-4 hover:bg-gray-50 transition-colors border-b border-gray-200 cursor-pointer"
|
||||
className="grid grid-cols-[auto_1fr_1fr_auto_auto] items-center gap-4 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors border-b border-gray-200 dark:border-gray-700 cursor-pointer"
|
||||
onClick={() => {
|
||||
if (task.tablos) {
|
||||
navigate(`/tablos/${task.tablos.id}?section=tasks`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* Checkbox + Title */}
|
||||
<div className="flex items-center gap-3 min-w-0 flex-1">
|
||||
<button
|
||||
className={cn(
|
||||
"w-6 h-6 rounded-full border-2 flex items-center justify-center shrink-0",
|
||||
isDone
|
||||
? "bg-purple-600 border-purple-600"
|
||||
: "border-gray-300 hover:border-purple-400",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onToggleDone(task);
|
||||
}}
|
||||
>
|
||||
{isDone && <CheckCircle2 className="w-4 h-4 text-white" />}
|
||||
</button>
|
||||
<p
|
||||
className={cn(
|
||||
"text-sm font-medium truncate",
|
||||
isDone ? "line-through text-gray-400" : "text-gray-900",
|
||||
)}
|
||||
>
|
||||
{task.title}
|
||||
</p>
|
||||
</div>
|
||||
{/* Checkbox */}
|
||||
<button
|
||||
className={cn(
|
||||
"w-6 h-6 rounded-full border-2 flex items-center justify-center shrink-0",
|
||||
isDone
|
||||
? "bg-purple-600 border-purple-600"
|
||||
: "border-gray-300 hover:border-purple-400 dark:border-gray-600 dark:hover:border-purple-500",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onToggleDone(task);
|
||||
}}
|
||||
>
|
||||
{isDone && <CheckCircle2 className="w-4 h-4 text-white" />}
|
||||
</button>
|
||||
|
||||
{/* Title */}
|
||||
<p
|
||||
className={cn(
|
||||
"text-sm font-medium truncate",
|
||||
isDone
|
||||
? "line-through text-gray-400 dark:text-gray-500"
|
||||
: "text-gray-900 dark:text-gray-100",
|
||||
)}
|
||||
>
|
||||
{task.title}
|
||||
</p>
|
||||
|
||||
{/* Tablo */}
|
||||
{task.tablos && (
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<div
|
||||
className={cn(
|
||||
"w-6 h-6 rounded-lg flex items-center justify-center text-xs shrink-0",
|
||||
task.tablos.color || "bg-gray-400",
|
||||
)}
|
||||
>
|
||||
<span className="text-white font-bold text-[10px]">
|
||||
{task.tablos.name.charAt(0).toUpperCase()}
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{task.tablos && (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"w-6 h-6 rounded-lg flex items-center justify-center text-xs shrink-0",
|
||||
task.tablos.color || "bg-gray-400",
|
||||
)}
|
||||
>
|
||||
<span className="text-white font-bold text-[10px]">
|
||||
{task.tablos.name.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300 hidden sm:inline truncate">
|
||||
{task.tablos.name}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm text-[#0C111D] hidden sm:inline max-w-[140px] truncate">
|
||||
{task.tablos.name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Date */}
|
||||
<span className="text-sm text-[#0C111D] shrink-0 hidden md:inline">
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400 hidden md:inline whitespace-nowrap">
|
||||
{formattedDate}
|
||||
</span>
|
||||
|
||||
{/* Status badge */}
|
||||
<span
|
||||
className={cn(
|
||||
"px-3 py-1 rounded-full text-xs font-medium shrink-0",
|
||||
"px-3 py-1 rounded-full text-xs font-medium whitespace-nowrap",
|
||||
badge.className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -153,13 +161,13 @@ export function DashboardTaskList() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-white rounded-2xl border border-gray-100">
|
||||
<div className="flex items-center justify-between px-4 py-5 border-b border-gray-200">
|
||||
<h2 className="text-2xl font-semibold text-gray-900">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-2xl border border-gray-100 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between px-4 py-5 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
{t("dashboard.taskList.title")}
|
||||
</h2>
|
||||
<button
|
||||
className="flex items-center gap-2 px-4 py-2 bg-white rounded-lg border border-gray-200 hover:bg-gray-50"
|
||||
className="flex items-center gap-2 px-4 py-2 bg-white dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300"
|
||||
onClick={() => setIsTaskModalOpen(true)}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
|
|
|
|||
|
|
@ -16,25 +16,29 @@ function useStatusConfig(status: string): StatusConfig {
|
|||
case "todo":
|
||||
return {
|
||||
label: t("tablo.status.todo"),
|
||||
badgeClass: "bg-blue-50 text-blue-600 border-blue-200",
|
||||
badgeClass:
|
||||
"bg-blue-50 text-blue-600 border-blue-200 dark:bg-blue-950/30 dark:text-blue-400 dark:border-blue-800",
|
||||
progressColor: "bg-blue-500",
|
||||
};
|
||||
case "in_progress":
|
||||
return {
|
||||
label: t("tablo.status.inProgress"),
|
||||
badgeClass: "bg-yellow-50 text-yellow-600 border-yellow-200",
|
||||
badgeClass:
|
||||
"bg-yellow-50 text-yellow-600 border-yellow-200 dark:bg-yellow-950/30 dark:text-yellow-400 dark:border-yellow-800",
|
||||
progressColor: "bg-purple-500",
|
||||
};
|
||||
case "done":
|
||||
return {
|
||||
label: t("tablo.status.done"),
|
||||
badgeClass: "bg-green-50 text-green-600 border-green-200",
|
||||
badgeClass:
|
||||
"bg-green-50 text-green-600 border-green-200 dark:bg-green-950/30 dark:text-green-400 dark:border-green-800",
|
||||
progressColor: "bg-green-500",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
label: t("tablo.status.todo"),
|
||||
badgeClass: "bg-blue-50 text-blue-600 border-blue-200",
|
||||
badgeClass:
|
||||
"bg-blue-50 text-blue-600 border-blue-200 dark:bg-blue-950/30 dark:text-blue-400 dark:border-blue-800",
|
||||
progressColor: "bg-blue-500",
|
||||
};
|
||||
}
|
||||
|
|
@ -79,7 +83,7 @@ export function ProjectCard({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-white rounded-2xl p-4 border border-[#EAECF0] hover:shadow-md transition-shadow cursor-pointer",
|
||||
"bg-white dark:bg-gray-800 rounded-2xl p-4 border border-[#EAECF0] dark:border-gray-700 hover:shadow-md transition-shadow cursor-pointer",
|
||||
className,
|
||||
)}
|
||||
onClick={() => onClick?.(tablo.id)}
|
||||
|
|
@ -95,7 +99,7 @@ export function ProjectCard({
|
|||
{statusConfig.label}
|
||||
</span>
|
||||
<button
|
||||
className="text-gray-400 hover:text-red-500"
|
||||
className="text-gray-400 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onMenuClick?.(tablo.id);
|
||||
|
|
@ -125,13 +129,13 @@ export function ProjectCard({
|
|||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 flex-1 truncate">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100 flex-1 truncate">
|
||||
{tablo.name}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{/* Date */}
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 mb-4">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 mb-4">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span>{formattedDate}</span>
|
||||
</div>
|
||||
|
|
@ -139,10 +143,14 @@ export function ProjectCard({
|
|||
{/* Progress */}
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center justify-between text-sm mb-2">
|
||||
<span className="text-gray-600">{t("tablo.card.progress")}:</span>
|
||||
<span className="font-semibold text-gray-900">{progress}%</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">
|
||||
{t("tablo.card.progress")}:
|
||||
</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-100 rounded-full h-2">
|
||||
<div className="w-full bg-gray-100 dark:bg-gray-700 rounded-full h-2">
|
||||
<div
|
||||
className={cn(
|
||||
"h-2 rounded-full transition-all",
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ export function ProjectCardList({
|
|||
return (
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-semibold text-gray-900">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
{t("tablo.projectList.title")}
|
||||
</h2>
|
||||
{onSeeAllClick && (
|
||||
<button
|
||||
className="flex items-center gap-1 text-purple-600 hover:text-purple-700 font-medium"
|
||||
className="flex items-center gap-1 text-purple-600 hover:text-purple-500 dark:text-purple-400 dark:hover:text-purple-300 font-medium"
|
||||
onClick={onSeeAllClick}
|
||||
>
|
||||
{t("tablo.projectList.seeAll")}
|
||||
|
|
@ -54,7 +54,7 @@ export function ProjectCardList({
|
|||
{hasMore && (
|
||||
<div className="flex justify-center mt-6">
|
||||
<button
|
||||
className="flex items-center gap-1.5 text-purple-600 hover:text-purple-700 font-medium text-sm"
|
||||
className="flex items-center gap-1.5 text-purple-600 hover:text-purple-500 dark:text-purple-400 dark:hover:text-purple-300 font-medium text-sm"
|
||||
onClick={() => setExpanded((prev) => !prev)}
|
||||
>
|
||||
{expanded ? (
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
"labels": {
|
||||
"title": "Title *",
|
||||
"tablo": "Tablo *",
|
||||
"tablo": "Project *",
|
||||
"date": "Date *",
|
||||
"startTime": "Start *",
|
||||
"endTime": "End",
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"placeholders": {
|
||||
"title": "Add a title",
|
||||
"tablo": "Select a tablo",
|
||||
"tablo": "Select a project",
|
||||
"description": "Add a description (optional)"
|
||||
},
|
||||
"buttons": {
|
||||
|
|
@ -80,10 +80,10 @@
|
|||
"required": "*"
|
||||
},
|
||||
"deleteTabloModal": {
|
||||
"title": "Delete tablo",
|
||||
"title": "Delete project",
|
||||
"subtitle": "This action is irreversible",
|
||||
"confirmQuestion": "Are you sure you want to delete the tablo",
|
||||
"warning": "All data associated with this tablo will be permanently lost.",
|
||||
"confirmQuestion": "Are you sure you want to delete the project",
|
||||
"warning": "All data associated with this project will be permanently lost.",
|
||||
"buttons": {
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
|
|
@ -105,11 +105,11 @@
|
|||
"importing": "Importing..."
|
||||
},
|
||||
"checkbox": {
|
||||
"createNewTablo": "Create a new tablo"
|
||||
"createNewTablo": "Create a new project"
|
||||
},
|
||||
"placeholders": {
|
||||
"newTabloName": "New tablo name",
|
||||
"selectTablo": "Select an existing tablo"
|
||||
"newTabloName": "New project name",
|
||||
"selectTablo": "Select an existing project"
|
||||
},
|
||||
"messages": {
|
||||
"eventsFound": "{{count}} event(s) found",
|
||||
|
|
@ -133,12 +133,12 @@
|
|||
"description": "No events to import"
|
||||
},
|
||||
"tabloRequired": {
|
||||
"title": "Tablo required",
|
||||
"description": "Please select a tablo or create a new one"
|
||||
"title": "Project required",
|
||||
"description": "Please select a project or create a new one"
|
||||
},
|
||||
"tabloNameRequired": {
|
||||
"title": "Tablo name required",
|
||||
"description": "Please enter a name for the new tablo"
|
||||
"title": "Project name required",
|
||||
"description": "Please enter a name for the new project"
|
||||
},
|
||||
"importSuccess": {
|
||||
"title": "Import successful",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"createTablo": {
|
||||
"title": "Create a new tablo",
|
||||
"nameLabel": "Tablo name",
|
||||
"namePlaceholder": "Enter tablo name"
|
||||
"title": "Create a new project",
|
||||
"nameLabel": "Project name",
|
||||
"namePlaceholder": "Enter project name"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@
|
|||
"shareNoteDescription": "Control who can access this note",
|
||||
"publicAccess": "Public Access",
|
||||
"publicAccessDescription": "Anyone with the link can view this note",
|
||||
"shareWithAllTablos": "Share with All Tablos",
|
||||
"shareWithAllTablosDescription": "Members of all your tablos can view this note",
|
||||
"shareWithAllTablos": "Share with All Projects",
|
||||
"shareWithAllTablosDescription": "Members of all your projects can view this note",
|
||||
"close": "Close",
|
||||
"saveNoteBeforeSharing": "Please save the note before sharing",
|
||||
"sharingSettingsUpdated": "Sharing settings updated",
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
"skipTutorial": "Skip onboarding",
|
||||
"back": "Back",
|
||||
"next": "Next",
|
||||
"getStarted": "Create Tablo",
|
||||
"getStarted": "Create Project",
|
||||
"steps": {
|
||||
"welcome": {
|
||||
"title": "Welcome to XTablo",
|
||||
"description": "Here, you will create your first Tablo - a clear, elegant project tracking space shareable with your clients or team.",
|
||||
"description": "Here, you will create your first project - a clear, elegant project tracking space shareable with your clients or team.",
|
||||
"content": {
|
||||
"intro": "In less than 2 minutes, you will have a ready-to-use Tablo, structured around your services.",
|
||||
"cta": "Create my first Tablo"
|
||||
"intro": "In less than 2 minutes, you will have a ready-to-use project, structured around your services.",
|
||||
"cta": "Create my first project"
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
"service": {
|
||||
"title": "What is your main service?",
|
||||
"description": "Define the service you want to track in this Tablo",
|
||||
"description": "Define the service you want to track in this project",
|
||||
"question": "What is your service?",
|
||||
"placeholder": "Ex: Web Development, Consulting..."
|
||||
},
|
||||
|
|
@ -36,9 +36,9 @@
|
|||
"step_placeholder": "Step {{index}}"
|
||||
},
|
||||
"project": {
|
||||
"title": "Project Name (Tablo)",
|
||||
"description": "Give your first Tablo a name",
|
||||
"question": "What would you like to call this tablo?",
|
||||
"title": "Project Name",
|
||||
"description": "Give your first project a name",
|
||||
"question": "What would you like to call this project?",
|
||||
"placeholder": "ex: Landing Page - Client X"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"tablo": {
|
||||
"title": "Tablos",
|
||||
"subtitle": "Manage your tablos and collaborations",
|
||||
"createButton": "New tablo",
|
||||
"title": "Projects",
|
||||
"subtitle": "Manage your projects and collaborations",
|
||||
"createButton": "New project",
|
||||
"emptyState": {
|
||||
"title": "No tablos found",
|
||||
"description": "Create your first tablo to start organizing your work",
|
||||
"button": "Create your first tablo"
|
||||
"title": "No projects found",
|
||||
"description": "Create your first project to start organizing your work",
|
||||
"button": "Create your first project"
|
||||
},
|
||||
"filter": {
|
||||
"all": "All",
|
||||
|
|
@ -30,13 +30,13 @@
|
|||
"contextMenu": {
|
||||
"openDiscussions": "Open discussions",
|
||||
"openPlanning": "Open planning",
|
||||
"delete": "Delete tablo"
|
||||
"delete": "Delete project"
|
||||
},
|
||||
"card": {
|
||||
"progress": "Progress"
|
||||
},
|
||||
"projectList": {
|
||||
"title": "My Tablos",
|
||||
"title": "My Projects",
|
||||
"seeAll": "See All",
|
||||
"showAll": "See {{count}} more",
|
||||
"showLess": "Show less"
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
"createEventType": "New type",
|
||||
"search": "Search for an event...",
|
||||
"filters": {
|
||||
"allTablos": "All boards",
|
||||
"allTablos": "All projects",
|
||||
"upcoming": "Upcoming",
|
||||
"past": "Past"
|
||||
},
|
||||
|
|
@ -96,10 +96,10 @@
|
|||
},
|
||||
"tasks": {
|
||||
"title": "My Tasks",
|
||||
"subtitle": "Manage all your tasks across all your tablos",
|
||||
"subtitle": "Manage all your tasks across all your projects",
|
||||
"search": "Search for a task...",
|
||||
"filters": {
|
||||
"allTablos": "All boards",
|
||||
"allTablos": "All projects",
|
||||
"allAssignees": "All assignees",
|
||||
"assignedToMe": "Assigned to me",
|
||||
"unassigned": "Unassigned"
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
"emptyState": {
|
||||
"title": "No tasks found",
|
||||
"noResults": "Try changing your search filters.",
|
||||
"noTasks": "Start by creating your first task in a tablo."
|
||||
"noTasks": "Start by creating your first task in a project."
|
||||
},
|
||||
"unassigned": "Unassigned",
|
||||
"pagination": {
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
},
|
||||
"view": {
|
||||
"kanban": "Kanban View",
|
||||
"aggregated": "By Tablo View"
|
||||
"aggregated": "By Project View"
|
||||
},
|
||||
"createTask": "New Task"
|
||||
},
|
||||
|
|
@ -161,6 +161,24 @@
|
|||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"actionCards": {
|
||||
"createProject": {
|
||||
"label": "Create Project",
|
||||
"description": "Set goals and scope"
|
||||
},
|
||||
"createTask": {
|
||||
"label": "Create Task",
|
||||
"description": "Break work into actions"
|
||||
},
|
||||
"inviteTeam": {
|
||||
"label": "Invite Team",
|
||||
"description": "Add collaborators instantly"
|
||||
},
|
||||
"sendMessage": {
|
||||
"label": "Send Message",
|
||||
"description": "Communicate updates fast"
|
||||
}
|
||||
},
|
||||
"taskList": {
|
||||
"title": "My Tasks",
|
||||
"addTask": "Add Task",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"title": "Planning",
|
||||
"allEvents": "All events",
|
||||
"allTablos": "All tablos",
|
||||
"selectTablo": "Select a tablo",
|
||||
"allTablos": "All projects",
|
||||
"selectTablo": "Select a project",
|
||||
"createEvent": "Create event",
|
||||
"importPlanning": "Import planning",
|
||||
"today": "Today",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"overview": {
|
||||
"title": "Overview",
|
||||
"description": "Configure the Stages of the tablo to clarify the major phases of your tablo.",
|
||||
"description": "Configure the Stages of the project to clarify the major phases of your project.",
|
||||
"overallProgress": "Overall Progress",
|
||||
"progressSummary": "{{done}} of {{total}} task(s) completed"
|
||||
},
|
||||
|
|
@ -9,10 +9,10 @@
|
|||
"nameRequired": "The Stage name is required",
|
||||
"namePlaceholder": "Stage Name",
|
||||
"deleteConfirm": "Are you sure you want to delete the Stage \"{{name}}\"? Associated tasks will remain available.",
|
||||
"noEtapes": "No Stages have been defined for this tablo yet.",
|
||||
"createFirstEtape": "Create your first Stage to structure the tablo tasks.",
|
||||
"onlyOwnerCanAdd": "Only the tablo owner can add Stages.",
|
||||
"onlyOwnerCanModify": "Only the tablo owner can modify Stages. Contact the administrator if you need a new Stage.",
|
||||
"noEtapes": "No Stages have been defined for this project yet.",
|
||||
"createFirstEtape": "Create your first Stage to structure the project tasks.",
|
||||
"onlyOwnerCanAdd": "Only the project owner can add Stages.",
|
||||
"onlyOwnerCanModify": "Only the project owner can modify Stages. Contact the administrator if you need a new Stage.",
|
||||
"stepNumber": "Stage {{number}}",
|
||||
"addNew": "New Stage"
|
||||
},
|
||||
|
|
@ -25,6 +25,6 @@
|
|||
},
|
||||
"events": {
|
||||
"title": "Upcoming events",
|
||||
"description": "Manage the future events of this tablo"
|
||||
"description": "Manage the future events of this project"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
"labels": {
|
||||
"title": "Titre *",
|
||||
"tablo": "Tablo *",
|
||||
"tablo": "Projet *",
|
||||
"date": "Date *",
|
||||
"startTime": "Début *",
|
||||
"endTime": "Fin",
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
},
|
||||
"placeholders": {
|
||||
"title": "Ajouter un titre",
|
||||
"tablo": "Sélectionner un tablo",
|
||||
"tablo": "Sélectionner un projet",
|
||||
"description": "Ajouter une description (optionnel)"
|
||||
},
|
||||
"buttons": {
|
||||
|
|
@ -80,10 +80,10 @@
|
|||
"required": "*"
|
||||
},
|
||||
"deleteTabloModal": {
|
||||
"title": "Supprimer le tablo",
|
||||
"title": "Supprimer le projet",
|
||||
"subtitle": "Cette action est irréversible",
|
||||
"confirmQuestion": "Êtes-vous sûr de vouloir supprimer le tablo",
|
||||
"warning": "Toutes les données associées à ce tablo seront perdues définitivement.",
|
||||
"confirmQuestion": "Êtes-vous sûr de vouloir supprimer le projet",
|
||||
"warning": "Toutes les données associées à ce projet seront perdues définitivement.",
|
||||
"buttons": {
|
||||
"cancel": "Annuler",
|
||||
"delete": "Supprimer",
|
||||
|
|
@ -105,11 +105,11 @@
|
|||
"importing": "Import en cours..."
|
||||
},
|
||||
"checkbox": {
|
||||
"createNewTablo": "Créer un nouveau tablo"
|
||||
"createNewTablo": "Créer un nouveau projet"
|
||||
},
|
||||
"placeholders": {
|
||||
"newTabloName": "Nom du nouveau tablo",
|
||||
"selectTablo": "Sélectionner un tablo existant"
|
||||
"newTabloName": "Nom du nouveau projet",
|
||||
"selectTablo": "Sélectionner un projet existant"
|
||||
},
|
||||
"messages": {
|
||||
"eventsFound": "{{count}} événement(s) trouvé(s)",
|
||||
|
|
@ -133,12 +133,12 @@
|
|||
"description": "Aucun événement à importer"
|
||||
},
|
||||
"tabloRequired": {
|
||||
"title": "Tablo requis",
|
||||
"description": "Veuillez sélectionner un tablo ou créer un nouveau"
|
||||
"title": "Projet requis",
|
||||
"description": "Veuillez sélectionner un projet ou en créer un nouveau"
|
||||
},
|
||||
"tabloNameRequired": {
|
||||
"title": "Nom du tablo requis",
|
||||
"description": "Veuillez saisir un nom pour le nouveau tablo"
|
||||
"title": "Nom du projet requis",
|
||||
"description": "Veuillez saisir un nom pour le nouveau projet"
|
||||
},
|
||||
"importSuccess": {
|
||||
"title": "Import réussi",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"createTablo": {
|
||||
"title": "Créer un nouveau tablo",
|
||||
"nameLabel": "Nom du tablo",
|
||||
"namePlaceholder": "Entrez le nom du tablo"
|
||||
"title": "Créer un nouveau projet",
|
||||
"nameLabel": "Nom du projet",
|
||||
"namePlaceholder": "Entrez le nom du projet"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@
|
|||
"shareNoteDescription": "Contrôlez qui peut accéder à cette note",
|
||||
"publicAccess": "Accès public",
|
||||
"publicAccessDescription": "Toute personne avec le lien peut voir cette note",
|
||||
"shareWithAllTablos": "Partager avec tous les tablos",
|
||||
"shareWithAllTablosDescription": "Les membres de tous vos tablos peuvent voir cette note",
|
||||
"shareWithAllTablos": "Partager avec tous les projets",
|
||||
"shareWithAllTablosDescription": "Les membres de tous vos projets peuvent voir cette note",
|
||||
"close": "Fermer",
|
||||
"saveNoteBeforeSharing": "Veuillez enregistrer la note avant de la partager",
|
||||
"sharingSettingsUpdated": "Paramètres de partage mis à jour",
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
"skipTutorial": "Passer l'onboarding",
|
||||
"back": "Retour",
|
||||
"next": "Suivant",
|
||||
"getStarted": "Créer le Tablo",
|
||||
"getStarted": "Créer le projet",
|
||||
"steps": {
|
||||
"welcome": {
|
||||
"title": "Bienvenue sur XTablo",
|
||||
"description": "Ici, vous allez créer votre premier Tablo - un espace de suivi projet, clair, élégant et partageable avec vos clients.",
|
||||
"description": "Ici, vous allez créer votre premier projet - un espace de suivi clair, élégant et partageable avec vos clients.",
|
||||
"content": {
|
||||
"intro": "En moins de 2 minutes, vous aurez un Tablo prêt à l'emploi, structuré autour de vos services.",
|
||||
"cta": "Créer mon premier Tablo"
|
||||
"intro": "En moins de 2 minutes, vous aurez un projet prêt à l'emploi, structuré autour de vos services.",
|
||||
"cta": "Créer mon premier projet"
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
"service": {
|
||||
"title": "Quel est votre service principal ?",
|
||||
"description": "Définissez le service que vous souhaitez suivre dans ce Tablo",
|
||||
"description": "Définissez le service que vous souhaitez suivre dans ce projet",
|
||||
"question": "Quel est votre service ?",
|
||||
"placeholder": "Ex: Création de site web, Consulting..."
|
||||
},
|
||||
|
|
@ -36,9 +36,9 @@
|
|||
"step_placeholder": "Étape {{index}}"
|
||||
},
|
||||
"project": {
|
||||
"title": "Nom du projet (tablo)",
|
||||
"description": "Donnez un nom à votre premier Tablo",
|
||||
"question": "Comment souhaitez-vous appeler ce tablo ?",
|
||||
"title": "Nom du projet",
|
||||
"description": "Donnez un nom à votre premier projet",
|
||||
"question": "Comment souhaitez-vous appeler ce projet ?",
|
||||
"placeholder": "ex : Landing Page - Client X"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"tablo": {
|
||||
"title": "Tablos",
|
||||
"subtitle": "Gérez vos tablos et collaborations",
|
||||
"createButton": "Nouveau tablo",
|
||||
"title": "Projets",
|
||||
"subtitle": "Gérez vos projets et collaborations",
|
||||
"createButton": "Nouveau projet",
|
||||
"emptyState": {
|
||||
"title": "Aucun tablo trouvé",
|
||||
"description": "Créez votre premier tablo pour commencer à organiser votre travail",
|
||||
"button": "Créer votre premier tablo"
|
||||
"title": "Aucun projet trouvé",
|
||||
"description": "Créez votre premier projet pour commencer à organiser votre travail",
|
||||
"button": "Créer votre premier projet"
|
||||
},
|
||||
"filter": {
|
||||
"all": "Tous",
|
||||
|
|
@ -30,13 +30,13 @@
|
|||
"contextMenu": {
|
||||
"openDiscussions": "Ouvrir les discussions",
|
||||
"openPlanning": "Ouvrir le planning",
|
||||
"delete": "Supprimer le tablo"
|
||||
"delete": "Supprimer le projet"
|
||||
},
|
||||
"card": {
|
||||
"progress": "Progression"
|
||||
},
|
||||
"projectList": {
|
||||
"title": "Mes Tablos",
|
||||
"title": "Mes Projets",
|
||||
"seeAll": "Voir tout",
|
||||
"showAll": "Voir {{count}} de plus",
|
||||
"showLess": "Réduire"
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
"createEventType": "Nouveau type",
|
||||
"search": "Rechercher un événement...",
|
||||
"filters": {
|
||||
"allTablos": "Tous les tablos",
|
||||
"allTablos": "Tous les projets",
|
||||
"upcoming": "À venir",
|
||||
"past": "Passés"
|
||||
},
|
||||
|
|
@ -96,10 +96,10 @@
|
|||
},
|
||||
"tasks": {
|
||||
"title": "Mes Tâches",
|
||||
"subtitle": "Gérez toutes vos tâches à travers tous vos tablos",
|
||||
"subtitle": "Gérez toutes vos tâches à travers tous vos projets",
|
||||
"search": "Rechercher une tâche...",
|
||||
"filters": {
|
||||
"allTablos": "Tous les tablos",
|
||||
"allTablos": "Tous les projets",
|
||||
"allAssignees": "Tous les assignés",
|
||||
"assignedToMe": "Assignées à moi",
|
||||
"unassigned": "Non assignées"
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
"emptyState": {
|
||||
"title": "Aucune tâche trouvée",
|
||||
"noResults": "Essayez de modifier vos filtres de recherche.",
|
||||
"noTasks": "Commencez par créer votre première tâche dans un tablo."
|
||||
"noTasks": "Commencez par créer votre première tâche dans un projet."
|
||||
},
|
||||
"unassigned": "Non assignée",
|
||||
"pagination": {
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
},
|
||||
"view": {
|
||||
"kanban": "Vue Kanban",
|
||||
"aggregated": "Vue par tablo"
|
||||
"aggregated": "Vue par projet"
|
||||
},
|
||||
"createTask": "Nouvelle tâche"
|
||||
},
|
||||
|
|
@ -161,6 +161,24 @@
|
|||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"actionCards": {
|
||||
"createProject": {
|
||||
"label": "Créer un projet",
|
||||
"description": "Définir les objectifs et le périmètre"
|
||||
},
|
||||
"createTask": {
|
||||
"label": "Créer une tâche",
|
||||
"description": "Découper le travail en actions"
|
||||
},
|
||||
"inviteTeam": {
|
||||
"label": "Inviter l'équipe",
|
||||
"description": "Ajouter des collaborateurs"
|
||||
},
|
||||
"sendMessage": {
|
||||
"label": "Envoyer un message",
|
||||
"description": "Communiquer rapidement"
|
||||
}
|
||||
},
|
||||
"taskList": {
|
||||
"title": "Mes Tâches",
|
||||
"addTask": "Ajouter",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"title": "Planning",
|
||||
"allEvents": "Tous les événements",
|
||||
"allTablos": "Tous les tablos",
|
||||
"selectTablo": "Sélectionner un tablo",
|
||||
"allTablos": "Tous les projets",
|
||||
"selectTablo": "Sélectionner un projet",
|
||||
"createEvent": "Créer un événement",
|
||||
"importPlanning": "Importer un planning",
|
||||
"today": "Aujourd'hui",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"overview": {
|
||||
"title": "Vue d'ensemble",
|
||||
"description": "Configurez les Étapes du tablo pour clarifier les grandes phases de votre tablo.",
|
||||
"description": "Configurez les Étapes du projet pour clarifier les grandes phases de votre projet.",
|
||||
"overallProgress": "Progression globale",
|
||||
"progressSummary": "{{done}} sur {{total}} tâche(s) terminée(s)"
|
||||
},
|
||||
|
|
@ -9,10 +9,10 @@
|
|||
"nameRequired": "Le nom de l'Étape est requis",
|
||||
"namePlaceholder": "Nom de l'Étape",
|
||||
"deleteConfirm": "Êtes-vous sûr de vouloir supprimer l'Étape \"{{name}}\" ? Les tâches associées resteront disponibles.",
|
||||
"noEtapes": "Aucune Étape n'a encore été définie pour ce tablo.",
|
||||
"createFirstEtape": "Créez votre première Étape pour structurer les tâches du tablo.",
|
||||
"onlyOwnerCanAdd": "Seul le propriétaire du tablo peut ajouter des Étapes.",
|
||||
"onlyOwnerCanModify": "Seul le propriétaire du tablo peut modifier les Étapes. Contactez l'administrateur si vous avez besoin d'une nouvelle Étape.",
|
||||
"noEtapes": "Aucune Étape n'a encore été définie pour ce projet.",
|
||||
"createFirstEtape": "Créez votre première Étape pour structurer les tâches du projet.",
|
||||
"onlyOwnerCanAdd": "Seul le propriétaire du projet peut ajouter des Étapes.",
|
||||
"onlyOwnerCanModify": "Seul le propriétaire du projet peut modifier les Étapes. Contactez l'administrateur si vous avez besoin d'une nouvelle Étape.",
|
||||
"stepNumber": "Étape {{number}}",
|
||||
"addNew": "Ajouter l'Étape"
|
||||
},
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
"events": {
|
||||
"title": "Événements à venir",
|
||||
"description": "Gérez les événements futurs de ce tablo",
|
||||
"description": "Gérez les événements futurs de ce projet",
|
||||
"createEvent": "Créer un événement"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,12 @@ import {
|
|||
UserPlusIcon,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
import {
|
||||
Link,
|
||||
useNavigate,
|
||||
useParams,
|
||||
useSearchParams,
|
||||
} from "react-router-dom";
|
||||
import { TabloDiscussionSection } from "../components/TabloDiscussionSection";
|
||||
import { TabloEventsSection } from "../components/TabloEventsSection";
|
||||
import { TabloFilesSection } from "../components/TabloFilesSection";
|
||||
|
|
@ -30,19 +35,48 @@ import { useTablosList } from "../hooks/tablos";
|
|||
function getStatusConfig(status: string) {
|
||||
switch (status) {
|
||||
case "in_progress":
|
||||
return { label: "En cours", badgeClass: "bg-yellow-50 text-yellow-700 border border-yellow-200 dark:bg-yellow-950/30 dark:text-yellow-400 dark:border-yellow-800", progress: 50 };
|
||||
return {
|
||||
label: "En cours",
|
||||
badgeClass:
|
||||
"bg-yellow-50 text-yellow-700 border border-yellow-200 dark:bg-yellow-950/30 dark:text-yellow-400 dark:border-yellow-800",
|
||||
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 };
|
||||
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 };
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Tabs ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
type TabSection = "overview" | "board" | "list" | "roadmap" | "calendar" | "files" | "discussion" | "events" | "tasks";
|
||||
type TabSection =
|
||||
| "overview"
|
||||
| "board"
|
||||
| "list"
|
||||
| "roadmap"
|
||||
| "calendar"
|
||||
| "files"
|
||||
| "discussion"
|
||||
| "events"
|
||||
| "tasks";
|
||||
|
||||
const TABS: { id: TabSection; label: string; icon: React.ElementType; disabled?: boolean }[] = [
|
||||
const TABS: {
|
||||
id: TabSection;
|
||||
label: string;
|
||||
icon: React.ElementType;
|
||||
disabled?: boolean;
|
||||
}[] = [
|
||||
{ id: "overview", label: "Aperçu", icon: LayoutDashboardIcon },
|
||||
{ id: "tasks", label: "Tâches", icon: KanbanIcon },
|
||||
{ id: "files", label: "Fichiers", icon: FolderIcon },
|
||||
|
|
@ -74,8 +108,13 @@ export const TabloDetailsPage = () => {
|
|||
setTablo(found);
|
||||
} else {
|
||||
toast.add(
|
||||
{ title: "Projet introuvable", description: "Le projet demandé n'existe pas ou vous n'y avez pas accès", type: "error" },
|
||||
{ timeout: 5000 }
|
||||
{
|
||||
title: "Projet introuvable",
|
||||
description:
|
||||
"Le projet demandé n'existe pas ou vous n'y avez pas accès",
|
||||
type: "error",
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
navigate("/tablos");
|
||||
}
|
||||
|
|
@ -84,11 +123,15 @@ export const TabloDetailsPage = () => {
|
|||
|
||||
// Tasks for this tablo (used in overview)
|
||||
const { data: allTasks = [] } = useAllTasks();
|
||||
const tabloTasks = (allTasks as KanbanTask[]).filter((t) => t.tablo_id === tabloId);
|
||||
const tabloTasks = (allTasks as KanbanTask[]).filter(
|
||||
(t) => t.tablo_id === tabloId,
|
||||
);
|
||||
|
||||
// Files for this tablo (used in overview)
|
||||
const { data: filesData } = useTabloFileNames(tabloId ?? "");
|
||||
const fileNames = (filesData?.fileNames ?? []).filter((f) => !f.startsWith("."));
|
||||
const fileNames = (filesData?.fileNames ?? []).filter(
|
||||
(f) => !f.startsWith("."),
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
|
@ -100,28 +143,38 @@ export const TabloDetailsPage = () => {
|
|||
|
||||
if (!tablo) return null;
|
||||
|
||||
const { label: statusLabel, badgeClass, progress } = getStatusConfig(tablo.status);
|
||||
const {
|
||||
label: statusLabel,
|
||||
badgeClass,
|
||||
progress,
|
||||
} = getStatusConfig(tablo.status);
|
||||
const isAdmin = tablo.is_admin;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* ── Header ──────────────────────────────────────────────────────── */}
|
||||
<div className="px-4 pt-10">
|
||||
<div className="px-4 pt-4">
|
||||
<div className="flex flex-col md:flex-row items-start justify-between mb-6 border-b border-[#F2F4F7] dark:border-gray-700 pb-5 gap-5 md:gap-0">
|
||||
<div className="flex items-center gap-4">
|
||||
<div
|
||||
className={cn(
|
||||
"w-12 h-12 rounded-lg flex items-center justify-center shrink-0 overflow-hidden text-white font-bold text-xl",
|
||||
!tablo.image && (tablo.color || "bg-gray-400")
|
||||
!tablo.image && (tablo.color || "bg-gray-400"),
|
||||
)}
|
||||
>
|
||||
{tablo.image ? (
|
||||
<img src={tablo.image} alt={tablo.name} className="w-full h-full object-cover" />
|
||||
<img
|
||||
src={tablo.image}
|
||||
alt={tablo.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
tablo.name.charAt(0).toUpperCase()
|
||||
)}
|
||||
</div>
|
||||
<h1 className="text-xl md:text-3xl font-bold text-foreground">{tablo.name}</h1>
|
||||
<h1 className="text-xl md:text-3xl font-bold text-foreground">
|
||||
{tablo.name}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
|
|
@ -146,22 +199,38 @@ export const TabloDetailsPage = () => {
|
|||
<div className="flex flex-wrap items-center gap-6 text-sm border-b border-[#F2F4F7] dark:border-gray-700 pb-4 mb-4">
|
||||
<div className="flex items-center gap-2 md:border-r border-[#D0D5DD] dark:border-gray-600 pr-4">
|
||||
<span className="text-muted-foreground">Rôle :</span>
|
||||
<span className="text-foreground font-medium">{isAdmin ? "Admin" : "Invité"}</span>
|
||||
<span className="text-foreground font-medium">
|
||||
{isAdmin ? "Admin" : "Invité"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 md:border-r border-[#D0D5DD] dark:border-gray-600 pr-4">
|
||||
<span className="text-muted-foreground">Créé le :</span>
|
||||
<span className="text-foreground">
|
||||
{new Intl.DateTimeFormat("fr-FR", { year: "numeric", month: "short", day: "2-digit" }).format(new Date(tablo.created_at))}
|
||||
{new Intl.DateTimeFormat("fr-FR", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
}).format(new Date(tablo.created_at))}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 md:border-r border-[#D0D5DD] dark:border-gray-600 pr-4">
|
||||
<span className="text-muted-foreground">Statut :</span>
|
||||
<span className={cn("px-3 py-1 rounded-full text-xs font-medium", badgeClass)}>{statusLabel}</span>
|
||||
<span
|
||||
className={cn(
|
||||
"px-3 py-1 rounded-full text-xs font-medium",
|
||||
badgeClass,
|
||||
)}
|
||||
>
|
||||
{statusLabel}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground">Progression :</span>
|
||||
<div className="w-24 h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
||||
<div className="h-full bg-green-500 rounded-full" style={{ width: `${progress}%` }} />
|
||||
<div
|
||||
className="h-full bg-green-500 rounded-full"
|
||||
style={{ width: `${progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-foreground font-medium">{progress}%</span>
|
||||
</div>
|
||||
|
|
@ -179,13 +248,15 @@ export const TabloDetailsPage = () => {
|
|||
key={tab.id}
|
||||
type="button"
|
||||
disabled={tab.disabled}
|
||||
onClick={() => !tab.disabled && setSearchParams({ section: tab.id })}
|
||||
onClick={() =>
|
||||
!tab.disabled && setSearchParams({ section: tab.id })
|
||||
}
|
||||
className={cn(
|
||||
"flex items-center gap-2 pb-3 px-1 text-sm font-semibold transition-colors border-b-2",
|
||||
isActive
|
||||
? "text-[#804EEC] border-[#804EEC]"
|
||||
: "text-[#667085] border-transparent hover:text-gray-900 dark:hover:text-gray-100",
|
||||
tab.disabled && "opacity-40 cursor-not-allowed"
|
||||
tab.disabled && "opacity-40 cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<tab.icon className="w-4 h-4" />
|
||||
|
|
@ -210,16 +281,22 @@ export const TabloDetailsPage = () => {
|
|||
<div className="lg:col-span-2 space-y-6">
|
||||
{/* Description */}
|
||||
<div className="bg-white dark:bg-card rounded-xl border border-border p-6 sm:p-8 shadow-sm">
|
||||
<h2 className="text-xl sm:text-2xl font-bold text-foreground mb-4">Description du projet</h2>
|
||||
<h2 className="text-xl sm:text-2xl font-bold text-foreground mb-4">
|
||||
Description du projet
|
||||
</h2>
|
||||
<p className="text-muted-foreground leading-relaxed text-sm sm:text-base">
|
||||
Ce projet regroupe les tâches, fichiers et événements associés. Utilisez les onglets ci-dessus pour naviguer entre les différentes sections.
|
||||
Ce projet regroupe les tâches, fichiers et événements
|
||||
associés. Utilisez les onglets ci-dessus pour naviguer entre
|
||||
les différentes sections.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Tasks */}
|
||||
<div className="bg-white dark:bg-card rounded-xl border border-gray-100 dark:border-gray-700 shadow-sm overflow-hidden">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between px-4 sm:px-6 py-4 border-b border-gray-200 dark:border-gray-700 gap-3">
|
||||
<h2 className="text-xl sm:text-2xl font-semibold text-gray-900 dark:text-gray-100">Mes tâches</h2>
|
||||
<h2 className="text-xl sm:text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
||||
Mes tâches
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSearchParams({ section: "tasks" })}
|
||||
|
|
@ -231,7 +308,9 @@ export const TabloDetailsPage = () => {
|
|||
</div>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{tabloTasks.length === 0 ? (
|
||||
<div className="p-6 text-center text-muted-foreground text-sm">Aucune tâche</div>
|
||||
<div className="p-6 text-center text-muted-foreground text-sm">
|
||||
Aucune tâche
|
||||
</div>
|
||||
) : (
|
||||
tabloTasks.slice(0, 5).map((task) => (
|
||||
<div
|
||||
|
|
@ -244,7 +323,14 @@ export const TabloDetailsPage = () => {
|
|||
) : (
|
||||
<div className="w-5 h-5 rounded-full border-2 border-gray-300 dark:border-gray-600 shrink-0" />
|
||||
)}
|
||||
<p className={cn("text-sm font-medium truncate", task.status === "done" ? "line-through text-gray-400" : "text-gray-900 dark:text-gray-100")}>
|
||||
<p
|
||||
className={cn(
|
||||
"text-sm font-medium truncate",
|
||||
task.status === "done"
|
||||
? "line-through text-gray-400"
|
||||
: "text-gray-900 dark:text-gray-100",
|
||||
)}
|
||||
>
|
||||
{task.title}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -268,24 +354,40 @@ export const TabloDetailsPage = () => {
|
|||
{/* Files */}
|
||||
<div className="bg-white dark:bg-card rounded-xl border border-border p-5 sm:p-6 shadow-sm">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-bold text-foreground">Fichiers</h3>
|
||||
<button type="button" onClick={() => setSearchParams({ section: "files" })} className="text-sm text-[#804EEC] hover:underline">
|
||||
<h3 className="text-lg font-bold text-foreground">
|
||||
Fichiers
|
||||
</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSearchParams({ section: "files" })}
|
||||
className="text-sm text-[#804EEC] hover:underline"
|
||||
>
|
||||
Voir tout
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{fileNames.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">Aucun fichier</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Aucun fichier
|
||||
</p>
|
||||
) : (
|
||||
fileNames.slice(0, 5).map((fileName) => (
|
||||
<div key={fileName} className="flex items-start gap-3 p-3 hover:bg-accent dark:hover:bg-gray-800 rounded-lg transition-colors">
|
||||
<div
|
||||
key={fileName}
|
||||
className="flex items-start gap-3 p-3 hover:bg-accent dark:hover:bg-gray-800 rounded-lg transition-colors"
|
||||
>
|
||||
<div className="w-10 h-10 bg-red-100 dark:bg-red-900/30 rounded-lg flex items-center justify-center shrink-0">
|
||||
<FileTextIcon className="w-4 h-4 text-red-500" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium text-foreground text-sm truncate">{fileName}</p>
|
||||
<p className="font-medium text-foreground text-sm truncate">
|
||||
{fileName}
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" className="text-muted-foreground hover:text-foreground p-1 shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
className="text-muted-foreground hover:text-foreground p-1 shrink-0"
|
||||
>
|
||||
<EllipsisVerticalIcon className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -296,22 +398,57 @@ export const TabloDetailsPage = () => {
|
|||
|
||||
{/* Info */}
|
||||
<div className="bg-white dark:bg-card rounded-xl border border-border p-5 sm:p-6 shadow-sm">
|
||||
<h3 className="text-lg font-bold text-foreground mb-4">Informations</h3>
|
||||
<h3 className="text-lg font-bold text-foreground mb-4">
|
||||
Informations
|
||||
</h3>
|
||||
<dl className="space-y-3 text-sm">
|
||||
<div className="flex justify-between"><dt className="text-muted-foreground">Tâches</dt><dd className="font-medium text-foreground">{tabloTasks.length}</dd></div>
|
||||
<div className="flex justify-between"><dt className="text-muted-foreground">Fichiers</dt><dd className="font-medium text-foreground">{fileNames.length}</dd></div>
|
||||
<div className="flex justify-between"><dt className="text-muted-foreground">Statut</dt><dd className={cn("px-2 py-0.5 rounded-full text-xs font-medium", badgeClass)}>{statusLabel}</dd></div>
|
||||
<div className="flex justify-between"><dt className="text-muted-foreground">Rôle</dt><dd className="font-medium text-foreground">{isAdmin ? "Admin" : "Invité"}</dd></div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-muted-foreground">Tâches</dt>
|
||||
<dd className="font-medium text-foreground">
|
||||
{tabloTasks.length}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-muted-foreground">Fichiers</dt>
|
||||
<dd className="font-medium text-foreground">
|
||||
{fileNames.length}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-muted-foreground">Statut</dt>
|
||||
<dd
|
||||
className={cn(
|
||||
"px-2 py-0.5 rounded-full text-xs font-medium",
|
||||
badgeClass,
|
||||
)}
|
||||
>
|
||||
{statusLabel}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-muted-foreground">Rôle</dt>
|
||||
<dd className="font-medium text-foreground">
|
||||
{isAdmin ? "Admin" : "Invité"}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeSection === "tasks" && <TabloTasksSection tablo={tablo} isAdmin={isAdmin} />}
|
||||
{activeSection === "files" && <TabloFilesSection tablo={tablo} isAdmin={isAdmin} />}
|
||||
{activeSection === "discussion" && <TabloDiscussionSection tablo={tablo} isAdmin={isAdmin} />}
|
||||
{activeSection === "events" && <TabloEventsSection tablo={tablo} isAdmin={isAdmin} />}
|
||||
{activeSection === "tasks" && (
|
||||
<TabloTasksSection tablo={tablo} isAdmin={isAdmin} />
|
||||
)}
|
||||
{activeSection === "files" && (
|
||||
<TabloFilesSection tablo={tablo} isAdmin={isAdmin} />
|
||||
)}
|
||||
{activeSection === "discussion" && (
|
||||
<TabloDiscussionSection tablo={tablo} isAdmin={isAdmin} />
|
||||
)}
|
||||
{activeSection === "events" && (
|
||||
<TabloEventsSection tablo={tablo} isAdmin={isAdmin} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export const TabloPage = () => {
|
|||
{
|
||||
title: t("common:error"),
|
||||
description:
|
||||
"Vous êtes en mode lecture seule. Vous ne pouvez pas créer de tablo.",
|
||||
"Vous êtes en mode lecture seule. Vous ne pouvez pas créer de projet.",
|
||||
type: "error",
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
|
|
@ -327,7 +327,7 @@ export const TabloPage = () => {
|
|||
</Text>
|
||||
</div>
|
||||
<Button onClick={openCreateModal} disabled={isReadOnly}>
|
||||
<Plus /> Nouveau tablo
|
||||
<Plus /> Nouveau projet
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -336,7 +336,7 @@ export const TabloPage = () => {
|
|||
<div className="flex justify-center items-center min-h-64">
|
||||
<div className="text-center">
|
||||
<p className="text-destructive mb-2">
|
||||
Erreur lors du chargement des tablos
|
||||
Erreur lors du chargement des projets
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{error instanceof Error
|
||||
|
|
@ -507,13 +507,13 @@ export const TabloPage = () => {
|
|||
}}
|
||||
>
|
||||
<header className="px-6 pt-6 pb-4">
|
||||
<p className="text-base text-[#475467] mb-2 font-medium">
|
||||
<p className="text-base text-[#475467] dark:text-gray-400 mb-2 font-medium">
|
||||
{formattedDate}
|
||||
</p>
|
||||
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||
<h1 className="text-[24px] font-medium text-[#475467]">
|
||||
<h1 className="text-[24px] font-medium text-[#475467] dark:text-gray-400">
|
||||
{getGreeting()},{" "}
|
||||
<span className="text-gray-900 font-medium">
|
||||
<span className="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{user.first_name ?? user.name}
|
||||
</span>
|
||||
!
|
||||
|
|
|
|||
Loading…
Reference in a new issue