From b316cf4f7a99022f6c0c3878b45a5fb8f9aa53cf Mon Sep 17 00:00:00 2001 From: AlexSserb Date: Mon, 6 Oct 2025 09:37:58 +0400 Subject: [PATCH] feat: table view for mobiles --- .../desktop/DealsTable/DealsTable.tsx | 4 ++ .../DealsTable/useDealsTableColumns.tsx | 30 +++++------ .../desktop/TopToolPanel/TopToolPanel.tsx | 8 ++- .../desktop/ViewSelector/ViewSelector.tsx | 6 +-- .../components/shared/PageBody/PageBody.tsx | 17 +++--- .../components/shared/views/BoardView.tsx | 5 +- .../components/shared/views/ScheduleView.tsx | 4 +- .../components/shared/views/TableView.tsx | 7 +-- src/app/deals/hooks/useView.ts | 34 ++++++++++++ src/components/layout/Footer/Footer.tsx | 52 +++++++++---------- .../layout/Footer/FooterButtons.tsx | 39 -------------- .../{ => components}/FooterClickable.tsx | 2 +- .../layout/Footer/data/buttonsData.ts | 31 +++++++++++ .../layout/Footer/types/LinkData.ts | 9 ++++ 14 files changed, 142 insertions(+), 106 deletions(-) create mode 100644 src/app/deals/hooks/useView.ts delete mode 100644 src/components/layout/Footer/FooterButtons.tsx rename src/components/layout/Footer/{ => components}/FooterClickable.tsx (92%) create mode 100644 src/components/layout/Footer/data/buttonsData.ts create mode 100644 src/components/layout/Footer/types/LinkData.ts diff --git a/src/app/deals/components/desktop/DealsTable/DealsTable.tsx b/src/app/deals/components/desktop/DealsTable/DealsTable.tsx index 24fcbee..dcc91e3 100644 --- a/src/app/deals/components/desktop/DealsTable/DealsTable.tsx +++ b/src/app/deals/components/desktop/DealsTable/DealsTable.tsx @@ -6,9 +6,11 @@ import { useDealsContext } from "@/app/deals/contexts/DealsContext"; import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext"; import BaseTable from "@/components/ui/BaseTable/BaseTable"; import { useDrawersContext } from "@/drawers/DrawersContext"; +import useIsMobile from "@/hooks/utils/useIsMobile"; import { DealSchema } from "@/lib/client"; const DealsTable: FC = () => { + const isMobile = useIsMobile(); const { selectedProject } = useProjectsContext(); const { deals, paginationInfo, page, setPage, sortingForm, dealsCrud } = useDealsContext(); @@ -33,9 +35,11 @@ const DealsTable: FC = () => { return ( { + const isMobile = useIsMobile(); + return useMemo( () => [ { accessor: "actions", - title: "Действия", + title: isMobile ? "" : "Действия", sortable: false, textAlign: "center", width: "0%", render: deal => ( - - onEditClick(deal)} - variant={"default"}> - - - + onEditClick(deal)} + variant={isMobile ? "subtle" : "default"}> + + ), }, { accessor: "id", - title: "Номер", + title: isMobile ? "№" : "Номер", sortable: true, - width: "30%", + width: "20%", }, { accessor: "name", title: "Название", - width: "40%", + width: "45%", }, { title: "Дата создания", accessor: "createdAt", render: deal => utcDateTimeToLocalString(deal.createdAt), sortable: true, - width: "30%", + width: "35%", }, ] as DataTableColumn[], [onEditClick] diff --git a/src/app/deals/components/desktop/TopToolPanel/TopToolPanel.tsx b/src/app/deals/components/desktop/TopToolPanel/TopToolPanel.tsx index 7cce33a..117f304 100644 --- a/src/app/deals/components/desktop/TopToolPanel/TopToolPanel.tsx +++ b/src/app/deals/components/desktop/TopToolPanel/TopToolPanel.tsx @@ -12,7 +12,11 @@ import { DealsFiltersForm } from "@/app/deals/hooks/useDealsFilters"; import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock"; import useIsMobile from "@/hooks/utils/useIsMobile"; -export type View = "board" | "table" | "schedule"; +export enum View { + BOARD = "board", + TABLE = "table", + SCHEDULE = "schedule" +} type Props = { view: View; @@ -42,7 +46,7 @@ const TopToolPanel: FC = ({ view, setView }) => { onChange: (values: DealsFiltersForm) => dealsFiltersForm.setValues(values), project: selectedProject, - boardAndStatusEnabled: view === "table", + boardAndStatusEnabled: view === View.TABLE, }, }); }; diff --git a/src/app/deals/components/desktop/ViewSelector/ViewSelector.tsx b/src/app/deals/components/desktop/ViewSelector/ViewSelector.tsx index 8524cc6..7b972a3 100644 --- a/src/app/deals/components/desktop/ViewSelector/ViewSelector.tsx +++ b/src/app/deals/components/desktop/ViewSelector/ViewSelector.tsx @@ -16,15 +16,15 @@ type Props = { const ViewSelector: FC = ({ value, onChange }) => { const views = [ { - value: "board" as View, + value: View.BOARD, icon: , }, { - value: "table" as View, + value: View.TABLE, icon: , }, { - value: "schedule" as View, + value: View.SCHEDULE, icon: , }, ]; diff --git a/src/app/deals/components/shared/PageBody/PageBody.tsx b/src/app/deals/components/shared/PageBody/PageBody.tsx index 2fa4919..fa12af1 100644 --- a/src/app/deals/components/shared/PageBody/PageBody.tsx +++ b/src/app/deals/components/shared/PageBody/PageBody.tsx @@ -1,9 +1,7 @@ "use client"; -import { useState } from "react"; -import TopToolPanel, { - View, -} from "@/app/deals/components/desktop/TopToolPanel/TopToolPanel"; +import { Box } from "@mantine/core"; +import TopToolPanel from "@/app/deals/components/desktop/TopToolPanel/TopToolPanel"; import { BoardView, ScheduleView, @@ -12,11 +10,14 @@ import { import { useBoardsContext } from "@/app/deals/contexts/BoardsContext"; import { DealsContextProvider } from "@/app/deals/contexts/DealsContext"; import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext"; +import useView from "@/app/deals/hooks/useView"; +import PageBlock from "@/components/layout/PageBlock/PageBlock"; const PageBody = () => { const { selectedBoard } = useBoardsContext(); const { selectedProject } = useProjectsContext(); - const [view, setView] = useState("board"); + + const { view, setView } = useView(); const getViewContent = () => { switch (view) { @@ -42,7 +43,11 @@ const PageBody = () => { view={view} setView={setView} /> - {getViewContent()} + + {getViewContent()} + ); }; diff --git a/src/app/deals/components/shared/views/BoardView.tsx b/src/app/deals/components/shared/views/BoardView.tsx index ae71134..c065c03 100644 --- a/src/app/deals/components/shared/views/BoardView.tsx +++ b/src/app/deals/components/shared/views/BoardView.tsx @@ -1,12 +1,11 @@ import { Space } from "@mantine/core"; import MainBlockHeader from "@/app/deals/components/mobile/MainBlockHeader/MainBlockHeader"; import Funnel from "@/app/deals/components/shared/Funnel/Funnel"; -import PageBlock from "@/components/layout/PageBlock/PageBlock"; export const BoardView = () => ( - + <> - + ); diff --git a/src/app/deals/components/shared/views/ScheduleView.tsx b/src/app/deals/components/shared/views/ScheduleView.tsx index 61dea83..5df95ce 100644 --- a/src/app/deals/components/shared/views/ScheduleView.tsx +++ b/src/app/deals/components/shared/views/ScheduleView.tsx @@ -1,5 +1,3 @@ -import PageBlock from "@/components/layout/PageBlock/PageBlock"; - export const ScheduleView = () => { - return -; + return <>-; }; diff --git a/src/app/deals/components/shared/views/TableView.tsx b/src/app/deals/components/shared/views/TableView.tsx index c0755e2..51279d8 100644 --- a/src/app/deals/components/shared/views/TableView.tsx +++ b/src/app/deals/components/shared/views/TableView.tsx @@ -1,8 +1,3 @@ import DealsTable from "@/app/deals/components/desktop/DealsTable/DealsTable"; -import PageBlock from "@/components/layout/PageBlock/PageBlock"; -export const TableView = () => ( - - - -); +export const TableView = () => ; diff --git a/src/app/deals/hooks/useView.ts b/src/app/deals/hooks/useView.ts new file mode 100644 index 0000000..3da8be0 --- /dev/null +++ b/src/app/deals/hooks/useView.ts @@ -0,0 +1,34 @@ +import { useEffect, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { View } from "@/app/deals/components/desktop/TopToolPanel/TopToolPanel"; + +const useView = () => { + const router = useRouter(); + const searchParams = useSearchParams(); + const viewsArray = Object.values(View); + + const [view, setView] = useState(() => { + const urlView = searchParams.get("view") as View; + return urlView && viewsArray.includes(urlView) ? urlView : View.BOARD; + }); + + useEffect(() => { + const urlView = searchParams.get("view") as View; + if (urlView && viewsArray.includes(urlView)) { + setView(urlView); + } + }, [searchParams]); + + useEffect(() => { + const params = new URLSearchParams(searchParams.toString()); + if (params.get("view") === view) return; + router.replace(`?view=${view}`); + }, [view]); + + return { + view, + setView, + }; +}; + +export default useView; diff --git a/src/components/layout/Footer/Footer.tsx b/src/components/layout/Footer/Footer.tsx index 0b35482..1824425 100644 --- a/src/components/layout/Footer/Footer.tsx +++ b/src/components/layout/Footer/Footer.tsx @@ -1,33 +1,29 @@ -import { IconCircleDotted, IconLayoutKanban } from "@tabler/icons-react"; -import FooterButtons, { - LinkData, -} from "@/components/layout/Footer/FooterButtons"; - -const buttonsData: LinkData[] = [ - { - icon: IconLayoutKanban, - label: "Сделки", - href: "/deals", - }, - { - icon: IconCircleDotted, - label: "Label 1", - href: "/oiiai", - }, - { - icon: IconCircleDotted, - label: "Label 2", - href: "/opaopa", - }, - { - icon: IconCircleDotted, - label: "Label 3", - href: "/hihihaha", - }, -]; +import { Group, Stack, Text } from "@mantine/core"; +import FooterClickable from "@/components/layout/Footer/components/FooterClickable"; +import buttonsData from "@/components/layout/Footer/data/buttonsData"; +import styles from "./Footer.module.css"; const Footer = () => { - return ; + return ( + + {buttonsData.map(data => ( + + + + {data.label} + + + ))} + + ); }; export default Footer; diff --git a/src/components/layout/Footer/FooterButtons.tsx b/src/components/layout/Footer/FooterButtons.tsx deleted file mode 100644 index 9f671f8..0000000 --- a/src/components/layout/Footer/FooterButtons.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { IconPlus } from "@tabler/icons-react"; -import { Group, Stack, Text } from "@mantine/core"; -import FooterClickable from "@/components/layout/Footer/FooterClickable"; -import styles from "./Footer.module.css"; - -export type LinkData = { - icon: typeof IconPlus; - label: string; - href: string; -}; - -type Props = { - buttonsData: LinkData[]; -}; - -const FooterButtons = ({ buttonsData }: Props) => { - return ( - - {buttonsData.map(data => ( - - - - {data.label} - - - ))} - - ); -}; - -export default FooterButtons; diff --git a/src/components/layout/Footer/FooterClickable.tsx b/src/components/layout/Footer/components/FooterClickable.tsx similarity index 92% rename from src/components/layout/Footer/FooterClickable.tsx rename to src/components/layout/Footer/components/FooterClickable.tsx index b103b2f..87cb6ea 100644 --- a/src/components/layout/Footer/FooterClickable.tsx +++ b/src/components/layout/Footer/components/FooterClickable.tsx @@ -3,7 +3,7 @@ import { ReactNode } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import styles from "./Footer.module.css"; +import styles from "../Footer.module.css"; type Props = { href: string; diff --git a/src/components/layout/Footer/data/buttonsData.ts b/src/components/layout/Footer/data/buttonsData.ts new file mode 100644 index 0000000..d434c36 --- /dev/null +++ b/src/components/layout/Footer/data/buttonsData.ts @@ -0,0 +1,31 @@ +import { + IconCircleDotted, + IconLayoutKanban, + IconTable, +} from "@tabler/icons-react"; +import LinkData from "@/components/layout/Navbar/types/LinkData"; + +const buttonsData: LinkData[] = [ + { + icon: IconLayoutKanban, + label: "Воронка", + href: "/deals?view=board", + }, + { + icon: IconTable, + label: "Таблица", + href: "/deals?view=table", + }, + { + icon: IconCircleDotted, + label: "Label 2", + href: "/opaopa", + }, + { + icon: IconCircleDotted, + label: "Label 3", + href: "/hihihaha", + }, +]; + +export default buttonsData; diff --git a/src/components/layout/Footer/types/LinkData.ts b/src/components/layout/Footer/types/LinkData.ts new file mode 100644 index 0000000..b0911ca --- /dev/null +++ b/src/components/layout/Footer/types/LinkData.ts @@ -0,0 +1,9 @@ +import { IconPlus } from "@tabler/icons-react"; + +type LinkData = { + icon: typeof IconPlus; + label: string; + href: string; +}; + +export default LinkData;