fix: resolve lint and formatting issues in apps/main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arthur Belleville 2026-04-15 14:33:38 +02:00
parent f3fb08c9b2
commit e8044182d8
No known key found for this signature in database
23 changed files with 153 additions and 94 deletions

View file

@ -42,9 +42,7 @@ export function ChatHeader({
<div className="ml-3">
<h2 className="font-semibold text-gray-900 dark:text-gray-100">{tablo.name}</h2>
{memberCount > 0 && (
<p className="text-xs text-gray-500 dark:text-gray-400">
{memberCount} online
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">{memberCount} online</p>
)}
</div>
</>

View file

@ -1,5 +1,6 @@
import { cn } from "@xtablo/shared";
import type { KanbanTask, TaskStatus } from "@xtablo/shared-types";
import { TaskModal } from "@xtablo/tablo-views";
import { CheckCircle2, Plus } from "lucide-react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -7,7 +8,6 @@ import { useNavigate } from "react-router-dom";
import { useTablosList } from "../hooks/tablos";
import { useAllTasks, useUpdateTask } from "../hooks/tasks";
import { useUser } from "../providers/UserStoreProvider";
import { TaskModal } from "@xtablo/tablo-views";
type TaskWithTablo = KanbanTask & {
tablos: { id: string; name: string; color: string | null } | null;

View file

@ -54,11 +54,7 @@ export function Layout() {
aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"}
aria-expanded={isMobileMenuOpen}
>
{isMobileMenuOpen ? (
<XIcon className="h-6 w-6" />
) : (
<MenuIcon className="h-6 w-6" />
)}
{isMobileMenuOpen ? <XIcon className="h-6 w-6" /> : <MenuIcon className="h-6 w-6" />}
</Button>
{/* Mobile backdrop overlay */}
@ -66,9 +62,7 @@ export function Layout() {
className={twMerge(
"fixed inset-0 z-40 bg-black/50 md:hidden",
"transition-opacity duration-300 ease-in-out",
isMobileMenuOpen
? "opacity-100 pointer-events-auto"
: "opacity-0 pointer-events-none"
isMobileMenuOpen ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"
)}
onClick={closeMobileMenu}
aria-hidden="true"

View file

@ -301,11 +301,7 @@ export const SideNavigation = ({ isMobileMenuOpen }: { isMobileMenuOpen: boolean
className={twMerge(
"group isolate flex flex-col overflow-y-auto overflow-x-hidden bg-navbar-background transition-all duration-300",
"h-full md:h-screen",
isMobileMenuOpen
? "w-40"
: effectivelyCollapsed
? "w-16"
: "w-48",
isMobileMenuOpen ? "w-40" : effectivelyCollapsed ? "w-16" : "w-48",
"md:flex",
// On mobile in standalone mode, respect safe area insets
"pl-[env(safe-area-inset-left,0px)] pt-[env(safe-area-inset-top,0px)] pb-[env(safe-area-inset-bottom,0px)]"
@ -352,7 +348,11 @@ export const SideNavigation = ({ isMobileMenuOpen }: { isMobileMenuOpen: boolean
"hover:scale-110"
)}
>
{effectivelyCollapsed ? <PlusIcon aria-hidden="true" /> : <MinusIcon aria-hidden="true" />}
{effectivelyCollapsed ? (
<PlusIcon aria-hidden="true" />
) : (
<MinusIcon aria-hidden="true" />
)}
</Button>
)}
</div>

View file

@ -26,6 +26,7 @@ vi.mock("../hooks/auth", () => ({
import { useOrganization } from "../hooks/organization";
import { useSubscription } from "../hooks/stripe";
const mockUseOrganization = vi.mocked(useOrganization);
const mockUseSubscription = vi.mocked(useSubscription);
@ -51,7 +52,7 @@ const queryClient = new QueryClient({
function renderCard(
user: User,
orgData: ReturnType<typeof useOrganization>["data"],
subscription: ReturnType<typeof useSubscription>["data"] = undefined,
subscription: ReturnType<typeof useSubscription>["data"] = undefined
) {
mockUseOrganization.mockReturnValue({
data: orgData,
@ -75,7 +76,14 @@ function renderCard(
}
const baseOrg = {
organization: { id: 1, name: "Org", plan: "none", member_count: 1, tablo_count: 0, logo_url: null },
organization: {
id: 1,
name: "Org",
plan: "none",
member_count: 1,
tablo_count: 0,
logo_url: null,
},
members: [],
invites_sent: [],
trial_starts_at: "2026-01-01",
@ -123,9 +131,7 @@ describe("SubscriptionCard", () => {
it("shows billing owner restriction when user is not billing owner", () => {
const nonOwnerOrg = { ...baseOrg, is_billing_owner: false };
renderCard(baseUser, nonOwnerOrg);
expect(
screen.getByText(/Seul le propriétaire de facturation/)
).toBeInTheDocument();
expect(screen.getByText(/Seul le propriétaire de facturation/)).toBeInTheDocument();
expect(screen.queryByText(/Passer au plan/)).not.toBeInTheDocument();
});

View file

@ -1,6 +1,6 @@
import { TabloDiscussionSection } from "@xtablo/tablo-views";
import { describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "../utils/testHelpers";
import { TabloDiscussionSection } from "@xtablo/tablo-views";
vi.mock("@xtablo/tablo-views/hooks/useChat", () => ({
useChat: () => ({

View file

@ -1,6 +1,6 @@
import { TabloEventsSection } from "@xtablo/tablo-views";
import { describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "../utils/testHelpers";
import { TabloEventsSection } from "@xtablo/tablo-views";
vi.mock("@xtablo/tablo-views/hooks/events", () => ({
useEventsByTablo: () => ({

View file

@ -1,6 +1,6 @@
import { TabloFilesSection } from "@xtablo/tablo-views";
import { describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "../utils/testHelpers";
import { TabloFilesSection } from "@xtablo/tablo-views";
vi.mock("../hooks/files", () => ({
useTabloFileNames: () => ({
@ -29,7 +29,12 @@ describe("TabloFilesSection", () => {
it("renders without crashing", () => {
const { container } = renderWithProviders(
<TabloFilesSection tablo={mockTablo} isAdmin={true} currentUserId="test-user-id" currentUser={{ id: "test-user-id" }} />
<TabloFilesSection
tablo={mockTablo}
isAdmin={true}
currentUserId="test-user-id"
currentUser={{ id: "test-user-id" }}
/>
);
expect(container).toBeInTheDocument();
});

View file

@ -1,5 +1,6 @@
import { toast } from "@xtablo/shared";
import type { UserTablo } from "@xtablo/shared/types/tablos.types";
import { TabloHeaderActions } from "@xtablo/tablo-views";
import { Button } from "@xtablo/ui/components/button";
import { Input } from "@xtablo/ui/components/input";
import { TypographyH3, TypographyMuted, TypographyP } from "@xtablo/ui/components/typography";
@ -16,7 +17,6 @@ import {
} from "../hooks/tasks";
import { useUser } from "../providers/UserStoreProvider";
import { getEtapeProgressStats } from "../utils/etapeProgress";
import { TabloHeaderActions } from "@xtablo/tablo-views";
interface TabloOverviewSectionProps {
tablo: UserTablo;

View file

@ -23,6 +23,7 @@ vi.mock("../hooks/auth", () => ({
}));
import { useOrganization } from "../hooks/organization";
const mockUseOrganization = vi.mocked(useOrganization);
const baseUser: User = {
@ -63,7 +64,14 @@ function renderPanel(user: User, orgData: ReturnType<typeof useOrganization>["da
}
const noPlanOrg = {
organization: { id: 1, name: "Org", plan: "none", member_count: 1, tablo_count: 0, logo_url: null },
organization: {
id: 1,
name: "Org",
plan: "none",
member_count: 1,
tablo_count: 0,
logo_url: null,
},
members: [],
invites_sent: [],
trial_starts_at: "2026-01-01",
@ -129,9 +137,7 @@ describe("UpgradePanel", () => {
const soloButton = screen.getByText("Passer au plan Solo").closest("button");
expect(soloButton).toBeDisabled();
expect(
screen.getByText(/Seul le propriétaire de facturation/)
).toBeInTheDocument();
expect(screen.getByText(/Seul le propriétaire de facturation/)).toBeInTheDocument();
});
it("renders nothing when org data is not yet loaded", () => {

View file

@ -129,7 +129,8 @@ export function UpgradePanel() {
disabled={checkoutPending || !isBillingOwner}
className="w-full"
>
Passer au plan Teams ({requiredTeamQuantity} siège{requiredTeamQuantity > 1 ? "s" : ""})
Passer au plan Teams ({requiredTeamQuantity} siège
{requiredTeamQuantity > 1 ? "s" : ""})
</Button>
<Button

View file

@ -1,2 +1,2 @@
export { KanbanBoard, TaskModal } from "@xtablo/tablo-views";
export type { TabloMember } from "@xtablo/tablo-views";
export { KanbanBoard, TaskModal } from "@xtablo/tablo-views";

View file

@ -2,11 +2,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { TestUserStoreProvider, type User } from "../providers/UserStoreProvider";
import {
UpgradeBlockProvider,
useMaybeUpgradeBlock,
useUpgradeBlock,
} from "./UpgradeBlockContext";
import { UpgradeBlockProvider, useMaybeUpgradeBlock, useUpgradeBlock } from "./UpgradeBlockContext";
// Mock the organization hook
vi.mock("../hooks/organization", () => ({
@ -14,6 +10,7 @@ vi.mock("../hooks/organization", () => ({
}));
import { useOrganization } from "../hooks/organization";
const mockUseOrganization = vi.mocked(useOrganization);
const baseUser: User = {
@ -73,7 +70,14 @@ function renderWithUser(user: User | null, orgData: ReturnType<typeof useOrganiz
}
const compliantOrgData = {
organization: { id: 1, name: "Test Org", plan: "team", member_count: 2, tablo_count: 5, logo_url: null },
organization: {
id: 1,
name: "Test Org",
plan: "team",
member_count: 2,
tablo_count: 5,
logo_url: null,
},
members: [],
invites_sent: [],
trial_starts_at: "2026-01-01",

View file

@ -1,6 +1,6 @@
import React, { createContext, useContext } from "react";
import { getOrganizationUpgradeBlockReason, type UpgradeBlockReason } from "../hooks/stripe";
import { useOrganization } from "../hooks/organization";
import { getOrganizationUpgradeBlockReason, type UpgradeBlockReason } from "../hooks/stripe";
import { useMaybeUser } from "../providers/UserStoreProvider";
interface UpgradeBlockContextValue {

View file

@ -6,7 +6,6 @@ import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { match } from "ts-pattern";
import { api } from "../lib/api";
import { clearOrgIdCookie } from "./organization";
import {
DEFAULT_SIGNUP_BILLING_INTENT,
PENDING_BILLING_CHECKOUT_PLAN_KEY,
@ -14,6 +13,7 @@ import {
SignupBillingIntent,
} from "../lib/billing";
import { supabase } from "../lib/supabase";
import { clearOrgIdCookie } from "./organization";
export type User = SupabaseUser & {
user_metadata: {

View file

@ -32,10 +32,9 @@ export const useCreateClientInvite = () => {
return useMutation({
mutationFn: async ({ tabloId, email }: { tabloId: string; email: string }) => {
const { data } = await api.post<PendingClientInvite>(
`/api/v1/client-invites/${tabloId}`,
{ email }
);
const { data } = await api.post<PendingClientInvite>(`/api/v1/client-invites/${tabloId}`, {
email,
});
return data;
},
onSuccess: (_data, { tabloId }) => {

View file

@ -8,8 +8,8 @@ import type {
KanbanTaskUpdate,
TaskStatus,
} from "@xtablo/shared-types";
import { supabase } from "../lib/supabase";
import { useMaybeUpgradeBlock } from "../contexts/UpgradeBlockContext";
import { supabase } from "../lib/supabase";
type CreateEtapeInput = {
tabloId: string;

View file

@ -1242,8 +1242,12 @@
}
@keyframes slide {
0% { transform: translateX(-100vw); }
100% { transform: translateX(100vw); }
0% {
transform: translateX(-100vw);
}
100% {
transform: translateX(100vw);
}
}
.animate-slide {

View file

@ -1,10 +1,10 @@
import { ChatMessages, useChat, useChatUnread } from "@xtablo/tablo-views";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom";
import { ChatChannelPreview } from "../components/ChatChannelPreview";
import { ChatHeader } from "../components/ChatHeader";
import { ChatMessages, useChat, useChatUnread } from "@xtablo/tablo-views";
import { useTablosList, useTabloMembers } from "../hooks/tablos";
import { useTabloMembers, useTablosList } from "../hooks/tablos";
import { useUser } from "../providers/UserStoreProvider";
export function ChatPage() {

View file

@ -32,9 +32,9 @@ import {
useInviteOrganizationUser,
useOrganization,
useRemoveOrganizationMember,
useRemoveOrgLogo,
useUpdateOrganization,
useUploadOrgLogo,
useRemoveOrgLogo,
} from "../hooks/organization";
import { useRemoveAvatar, useUpdateProfile, useUploadAvatar } from "../hooks/profile";
import { useCookieConsent } from "../hooks/useCookieConsent";
@ -529,7 +529,9 @@ export default function SettingsPage() {
<div className="flex justify-end">
<Button
disabled={inviteOrganizationUserPending || !inviteEmail.trim() || isAtTeamMemberLimit}
disabled={
inviteOrganizationUserPending || !inviteEmail.trim() || isAtTeamMemberLimit
}
onClick={() => {
inviteOrganizationUser(inviteEmail.trim());
setInviteEmail("");

View file

@ -2,6 +2,16 @@ import { LoadingSpinner } from "@ui/components/LoadingSpinner";
import { cn, toast } from "@xtablo/shared";
import type { UserTablo } from "@xtablo/shared/types/tablos.types";
import type { KanbanTask } from "@xtablo/shared-types";
import {
EtapesSection,
RoadmapSection,
TabloDiscussionSection,
TabloEventsSection,
TabloFilesSection,
TabloTasksSection,
TaskModal,
useChatUnread,
} from "@xtablo/tablo-views";
import { Avatar, AvatarFallback, AvatarImage } from "@xtablo/ui/components/avatar";
import { Button } from "@xtablo/ui/components/button";
import {
@ -40,24 +50,25 @@ import {
import { useEffect, useState } from "react";
import { Link, useNavigate, useParams, useSearchParams } from "react-router-dom";
import {
EtapesSection,
RoadmapSection,
TabloDiscussionSection,
TabloEventsSection,
TabloFilesSection,
TabloTasksSection,
TaskModal,
useChatUnread,
} from "@xtablo/tablo-views";
import { useInviteUser } from "../hooks/invite";
import { useTabloFileNames, useDownloadTabloFile, useUploadTabloFile, useDeleteTabloFile } from "../hooks/tablo_data";
import { useTabloFolders, useCreateTabloFolder, useUpdateTabloFolder, useDeleteTabloFolder } from "../hooks/tablo_folders";
import { useCancelTabloInvite, usePendingTabloInvitesByTablo } from "../hooks/tablo_invites";
import {
usePendingClientInvites,
useCreateClientInvite,
useCancelClientInvite,
useCreateClientInvite,
usePendingClientInvites,
} from "../hooks/client_invites";
import { useEventsByTablo } from "../hooks/events";
import { useInviteUser } from "../hooks/invite";
import {
useDeleteTabloFile,
useDownloadTabloFile,
useTabloFileNames,
useUploadTabloFile,
} from "../hooks/tablo_data";
import {
useCreateTabloFolder,
useDeleteTabloFolder,
useTabloFolders,
useUpdateTabloFolder,
} from "../hooks/tablo_folders";
import { useCancelTabloInvite, usePendingTabloInvitesByTablo } from "../hooks/tablo_invites";
import {
useTabloMembers,
useTabloOverviewLayout,
@ -72,7 +83,6 @@ import {
useUpdateTask,
useUpdateTaskPositions,
} from "../hooks/tasks";
import { useEventsByTablo } from "../hooks/events";
import { useUser } from "../providers/UserStoreProvider";
import { getEtapeProgressStats } from "../utils/etapeProgress";
import {
@ -209,7 +219,8 @@ export const TabloDetailsPage = () => {
const { mutate: inviteUser, isPending: isInvitingUser } = useInviteUser();
const { data: pendingClientInvites } = usePendingClientInvites(tabloId ?? "");
const { mutate: createClientInvite, isPending: isCreatingClientInvite } = useCreateClientInvite();
const { mutate: cancelClientInvite, isPending: isCancellingClientInvite } = useCancelClientInvite();
const { mutate: cancelClientInvite, isPending: isCancellingClientInvite } =
useCancelClientInvite();
const { mutate: updateTask } = useUpdateTask();
const { mutate: updateTablo, mutateAsync: updateTabloAsync } = useUpdateTablo();
const { mutate: createTask } = useCreateTask();
@ -217,7 +228,11 @@ export const TabloDetailsPage = () => {
const { mutate: updateTaskPositions } = useUpdateTaskPositions();
// Files & folders hooks
const { data: foldersData, isLoading: foldersLoading, error: foldersError } = useTabloFolders(tabloId ?? "");
const {
data: foldersData,
isLoading: foldersLoading,
error: foldersError,
} = useTabloFolders(tabloId ?? "");
const { mutateAsync: downloadFile } = useDownloadTabloFile();
const { mutateAsync: uploadFile } = useUploadTabloFile();
const { mutateAsync: deleteFile } = useDeleteTabloFile();
@ -226,7 +241,11 @@ export const TabloDetailsPage = () => {
const { mutateAsync: deleteFolder } = useDeleteTabloFolder();
// Events hooks
const { data: events, isLoading: eventsLoading, error: eventsError } = useEventsByTablo(tabloId ?? null);
const {
data: events,
isLoading: eventsLoading,
error: eventsError,
} = useEventsByTablo(tabloId ?? null);
const isEmailValid = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@ -529,7 +548,12 @@ export const TabloDetailsPage = () => {
</div>
{/* ── Tab content ─────────────────────────────────────────────────── */}
<div className={cn("px-4 sm:px-6 pt-6 pb-8", activeSection === "discussion" && "flex flex-col flex-1 min-h-0 !px-0 !pt-0 !pb-0")}>
<div
className={cn(
"px-4 sm:px-6 pt-6 pb-8",
activeSection === "discussion" && "flex flex-col flex-1 min-h-0 !px-0 !pt-0 !pb-0"
)}
>
{activeSection === "overview" &&
(() => {
const overviewBlocks: Record<OverviewBlockId, React.ReactNode> = {
@ -826,9 +850,13 @@ export const TabloDetailsPage = () => {
onCreateTask={(task) => createTask(task)}
onUpdateTask={(task) => updateTask(task)}
onUpdateTaskPositions={(updates) => updateTaskPositions(updates)}
onUpdateTablo={(data) => updateTabloAsync({ ...data, name: data.name ?? undefined }).then(() => {})}
onUpdateTablo={(data) =>
updateTabloAsync({ ...data, name: data.name ?? undefined }).then(() => undefined)
}
onInviteUser={inviteUser}
onCancelInvite={(params) => cancelInvite({ ...params, inviteId: Number(params.inviteId) })}
onCancelInvite={(params) =>
cancelInvite({ ...params, inviteId: Number(params.inviteId) })
}
/>
)}
{activeSection === "files" && (
@ -849,15 +877,19 @@ export const TabloDetailsPage = () => {
isCancellingInvite={isCancellingInvite}
isCreatingFolder={isCreatingFolder}
isUpdatingFolder={isUpdatingFolder}
onCreateFile={(params) => uploadFile(params).then(() => {})}
onDeleteFile={(params) => deleteFile(params).then(() => {})}
onDownloadFile={(params) => downloadFile(params).then(() => {})}
onCreateFolder={(params) => createFolder(params).then(() => {})}
onUpdateFolder={(params) => updateFolder(params).then(() => {})}
onDeleteFolder={(params) => deleteFolder(params).then(() => {})}
onUpdateTablo={(data) => updateTabloAsync({ ...data, name: data.name ?? undefined }).then(() => {})}
onCreateFile={(params) => uploadFile(params).then(() => undefined)}
onDeleteFile={(params) => deleteFile(params).then(() => undefined)}
onDownloadFile={(params) => downloadFile(params).then(() => undefined)}
onCreateFolder={(params) => createFolder(params).then(() => undefined)}
onUpdateFolder={(params) => updateFolder(params).then(() => undefined)}
onDeleteFolder={(params) => deleteFolder(params).then(() => undefined)}
onUpdateTablo={(data) =>
updateTabloAsync({ ...data, name: data.name ?? undefined }).then(() => undefined)
}
onInviteUser={inviteUser}
onCancelInvite={(params) => cancelInvite({ ...params, inviteId: Number(params.inviteId) })}
onCancelInvite={(params) =>
cancelInvite({ ...params, inviteId: Number(params.inviteId) })
}
/>
)}
{activeSection === "discussion" && (
@ -882,10 +914,14 @@ export const TabloDetailsPage = () => {
pendingInvites={pendingInvites?.map((inv) => ({ ...inv, id: String(inv.id) }))}
isInvitingUser={isInvitingUser}
isCancellingInvite={isCancellingInvite}
onCreateEvent={() => {}}
onUpdateTablo={(data) => updateTabloAsync({ ...data, name: data.name ?? undefined }).then(() => {})}
onCreateEvent={() => undefined}
onUpdateTablo={(data) =>
updateTabloAsync({ ...data, name: data.name ?? undefined }).then(() => undefined)
}
onInviteUser={inviteUser}
onCancelInvite={(params) => cancelInvite({ ...params, inviteId: Number(params.inviteId) })}
onCancelInvite={(params) =>
cancelInvite({ ...params, inviteId: Number(params.inviteId) })
}
/>
)}
@ -895,8 +931,13 @@ export const TabloDetailsPage = () => {
tabloTasks={tabloTasks}
tabloId={tabloId ?? ""}
isAdmin={isAdmin}
onCreateTask={(task) => createTask({ ...task, status: task.status as "todo" | "in_progress" | "in_review" | "done" })}
onCreateEtape={(params) => createEtape(params).then(() => {})}
onCreateTask={(task) =>
createTask({
...task,
status: task.status as "todo" | "in_progress" | "in_review" | "done",
})
}
onCreateEtape={(params) => createEtape(params).then(() => undefined)}
isCreatingEtape={isCreatingEtape}
/>
)}
@ -1164,4 +1205,3 @@ export const TabloDetailsPage = () => {
</div>
);
};

View file

@ -3,6 +3,8 @@ import { DeleteTabloModal } from "@ui/components/DeleteTabloModal";
import { LoadingSpinner } from "@ui/components/LoadingSpinner";
import { toast } from "@xtablo/shared";
import { TabloInsert, UserTablo } from "@xtablo/shared/types/tablos.types";
import { TaskModal } from "@xtablo/tablo-views";
import { Badge } from "@xtablo/ui/components/badge";
import { Button } from "@xtablo/ui/components/button";
import {
Empty,
@ -40,11 +42,9 @@ import { useNavigate, useSearchParams } from "react-router-dom";
import { DashboardActionCards } from "src/components/DashboardActionCards";
import { DashboardTaskList } from "src/components/DashboardTaskList";
import { InviteOrganizationModal } from "src/components/InviteOrganizationModal";
import { TaskModal } from "@xtablo/tablo-views";
import { ProjectCardList } from "src/components/ProjectCardList";
import { Badge } from "@xtablo/ui/components/badge";
import { useCanCreateTablo, useCreateTablo, useDeleteTablo, useTablosList } from "../hooks/tablos";
import { useOrganization } from "../hooks/organization";
import { useCanCreateTablo, useCreateTablo, useDeleteTablo, useTablosList } from "../hooks/tablos";
import { useIsReadOnlyUser, useUser } from "../providers/UserStoreProvider";
function getTabloIcon(color: string | null | undefined) {

View file

@ -1,5 +1,6 @@
import { LoadingSpinner } from "@ui/components/LoadingSpinner";
import type { KanbanColumn, KanbanTask } from "@xtablo/shared-types";
import { GanttChart, TaskModal } from "@xtablo/tablo-views";
import { Button } from "@xtablo/ui/components/button";
import {
DropdownMenu,
@ -40,7 +41,6 @@ import { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate, useSearchParams } from "react-router-dom";
import { twMerge } from "tailwind-merge";
import { GanttChart, TaskModal } from "@xtablo/tablo-views";
import { useTablosList } from "../hooks/tablos";
import { useAllTasks, useUpdateTask } from "../hooks/tasks";
import { useUser } from "../providers/UserStoreProvider";