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:
parent
f3fb08c9b2
commit
e8044182d8
23 changed files with 153 additions and 94 deletions
|
|
@ -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>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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: () => ({
|
||||
|
|
|
|||
|
|
@ -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: () => ({
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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", () => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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 }) => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1242,8 +1242,12 @@
|
|||
}
|
||||
|
||||
@keyframes slide {
|
||||
0% { transform: translateX(-100vw); }
|
||||
100% { transform: translateX(100vw); }
|
||||
0% {
|
||||
transform: translateX(-100vw);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100vw);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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("");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Reference in a new issue