feat: table view for mobiles
This commit is contained in:
@ -6,9 +6,11 @@ import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
|||||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||||
import { useDrawersContext } from "@/drawers/DrawersContext";
|
import { useDrawersContext } from "@/drawers/DrawersContext";
|
||||||
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema } from "@/lib/client";
|
||||||
|
|
||||||
const DealsTable: FC = () => {
|
const DealsTable: FC = () => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
const { selectedProject } = useProjectsContext();
|
const { selectedProject } = useProjectsContext();
|
||||||
const { deals, paginationInfo, page, setPage, sortingForm, dealsCrud } =
|
const { deals, paginationInfo, page, setPage, sortingForm, dealsCrud } =
|
||||||
useDealsContext();
|
useDealsContext();
|
||||||
@ -33,9 +35,11 @@ const DealsTable: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
|
p={isMobile ? "xs" : ""}
|
||||||
gap={"xs"}
|
gap={"xs"}
|
||||||
h={"100%"}>
|
h={"100%"}>
|
||||||
<BaseTable
|
<BaseTable
|
||||||
|
withTableBorder
|
||||||
records={[...deals]}
|
records={[...deals]}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
sortStatus={{
|
sortStatus={{
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { IconEdit } from "@tabler/icons-react";
|
import { IconEdit } from "@tabler/icons-react";
|
||||||
import { DataTableColumn } from "mantine-datatable";
|
import { DataTableColumn } from "mantine-datatable";
|
||||||
import { ActionIcon, Tooltip } from "@mantine/core";
|
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
|
||||||
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema } from "@/lib/client";
|
||||||
import { utcDateTimeToLocalString } from "@/utils/datetime";
|
import { utcDateTimeToLocalString } from "@/utils/datetime";
|
||||||
|
|
||||||
@ -10,44 +11,43 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useDealsTableColumns = ({ onEditClick }: Props) => {
|
const useDealsTableColumns = ({ onEditClick }: Props) => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
accessor: "actions",
|
accessor: "actions",
|
||||||
title: "Действия",
|
title: isMobile ? "" : "Действия",
|
||||||
sortable: false,
|
sortable: false,
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
width: "0%",
|
width: "0%",
|
||||||
render: deal => (
|
render: deal => (
|
||||||
<Tooltip label="Редактировать">
|
<ActionIconWithTip
|
||||||
<ActionIcon
|
tipLabel={"Редактировать"}
|
||||||
bdrs={"md"}
|
onClick={() => onEditClick(deal)}
|
||||||
size={"lg"}
|
variant={isMobile ? "subtle" : "default"}>
|
||||||
onClick={() => onEditClick(deal)}
|
<IconEdit />
|
||||||
variant={"default"}>
|
</ActionIconWithTip>
|
||||||
<IconEdit />
|
|
||||||
</ActionIcon>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "id",
|
accessor: "id",
|
||||||
title: "Номер",
|
title: isMobile ? "№" : "Номер",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
width: "30%",
|
width: "20%",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: "name",
|
accessor: "name",
|
||||||
title: "Название",
|
title: "Название",
|
||||||
width: "40%",
|
width: "45%",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Дата создания",
|
title: "Дата создания",
|
||||||
accessor: "createdAt",
|
accessor: "createdAt",
|
||||||
render: deal => utcDateTimeToLocalString(deal.createdAt),
|
render: deal => utcDateTimeToLocalString(deal.createdAt),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
width: "30%",
|
width: "35%",
|
||||||
},
|
},
|
||||||
] as DataTableColumn<DealSchema>[],
|
] as DataTableColumn<DealSchema>[],
|
||||||
[onEditClick]
|
[onEditClick]
|
||||||
|
|||||||
@ -12,7 +12,11 @@ import { DealsFiltersForm } from "@/app/deals/hooks/useDealsFilters";
|
|||||||
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
|
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
|
||||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
|
|
||||||
export type View = "board" | "table" | "schedule";
|
export enum View {
|
||||||
|
BOARD = "board",
|
||||||
|
TABLE = "table",
|
||||||
|
SCHEDULE = "schedule"
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
view: View;
|
view: View;
|
||||||
@ -42,7 +46,7 @@ const TopToolPanel: FC<Props> = ({ view, setView }) => {
|
|||||||
onChange: (values: DealsFiltersForm) =>
|
onChange: (values: DealsFiltersForm) =>
|
||||||
dealsFiltersForm.setValues(values),
|
dealsFiltersForm.setValues(values),
|
||||||
project: selectedProject,
|
project: selectedProject,
|
||||||
boardAndStatusEnabled: view === "table",
|
boardAndStatusEnabled: view === View.TABLE,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,15 +16,15 @@ type Props = {
|
|||||||
const ViewSelector: FC<Props> = ({ value, onChange }) => {
|
const ViewSelector: FC<Props> = ({ value, onChange }) => {
|
||||||
const views = [
|
const views = [
|
||||||
{
|
{
|
||||||
value: "board" as View,
|
value: View.BOARD,
|
||||||
icon: <IconLayoutDashboard />,
|
icon: <IconLayoutDashboard />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "table" as View,
|
value: View.TABLE,
|
||||||
icon: <IconMenu2 />,
|
icon: <IconMenu2 />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "schedule" as View,
|
value: View.SCHEDULE,
|
||||||
icon: <IconCalendarWeekFilled />,
|
icon: <IconCalendarWeekFilled />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { Box } from "@mantine/core";
|
||||||
import TopToolPanel, {
|
import TopToolPanel from "@/app/deals/components/desktop/TopToolPanel/TopToolPanel";
|
||||||
View,
|
|
||||||
} from "@/app/deals/components/desktop/TopToolPanel/TopToolPanel";
|
|
||||||
import {
|
import {
|
||||||
BoardView,
|
BoardView,
|
||||||
ScheduleView,
|
ScheduleView,
|
||||||
@ -12,11 +10,14 @@ import {
|
|||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
import { DealsContextProvider } from "@/app/deals/contexts/DealsContext";
|
import { DealsContextProvider } from "@/app/deals/contexts/DealsContext";
|
||||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
|
import useView from "@/app/deals/hooks/useView";
|
||||||
|
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
||||||
|
|
||||||
const PageBody = () => {
|
const PageBody = () => {
|
||||||
const { selectedBoard } = useBoardsContext();
|
const { selectedBoard } = useBoardsContext();
|
||||||
const { selectedProject } = useProjectsContext();
|
const { selectedProject } = useProjectsContext();
|
||||||
const [view, setView] = useState<View>("board");
|
|
||||||
|
const { view, setView } = useView();
|
||||||
|
|
||||||
const getViewContent = () => {
|
const getViewContent = () => {
|
||||||
switch (view) {
|
switch (view) {
|
||||||
@ -42,7 +43,11 @@ const PageBody = () => {
|
|||||||
view={view}
|
view={view}
|
||||||
setView={setView}
|
setView={setView}
|
||||||
/>
|
/>
|
||||||
{getViewContent()}
|
<PageBlock
|
||||||
|
fullScreenMobile
|
||||||
|
style={{ flex: 1 }}>
|
||||||
|
<Box h={"100%"}>{getViewContent()}</Box>
|
||||||
|
</PageBlock>
|
||||||
</DealsContextProvider>
|
</DealsContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import { Space } from "@mantine/core";
|
import { Space } from "@mantine/core";
|
||||||
import MainBlockHeader from "@/app/deals/components/mobile/MainBlockHeader/MainBlockHeader";
|
import MainBlockHeader from "@/app/deals/components/mobile/MainBlockHeader/MainBlockHeader";
|
||||||
import Funnel from "@/app/deals/components/shared/Funnel/Funnel";
|
import Funnel from "@/app/deals/components/shared/Funnel/Funnel";
|
||||||
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
|
||||||
|
|
||||||
export const BoardView = () => (
|
export const BoardView = () => (
|
||||||
<PageBlock style={{ height: "100%" }}>
|
<>
|
||||||
<MainBlockHeader />
|
<MainBlockHeader />
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
<Funnel />
|
<Funnel />
|
||||||
</PageBlock>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
|
||||||
|
|
||||||
export const ScheduleView = () => {
|
export const ScheduleView = () => {
|
||||||
return <PageBlock>-</PageBlock>;
|
return <>-</>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import DealsTable from "@/app/deals/components/desktop/DealsTable/DealsTable";
|
import DealsTable from "@/app/deals/components/desktop/DealsTable/DealsTable";
|
||||||
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
|
||||||
|
|
||||||
export const TableView = () => (
|
export const TableView = () => <DealsTable />;
|
||||||
<PageBlock style={{ height: "100%" }}>
|
|
||||||
<DealsTable />
|
|
||||||
</PageBlock>
|
|
||||||
);
|
|
||||||
|
|||||||
34
src/app/deals/hooks/useView.ts
Normal file
34
src/app/deals/hooks/useView.ts
Normal file
@ -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<View>(() => {
|
||||||
|
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;
|
||||||
@ -1,33 +1,29 @@
|
|||||||
import { IconCircleDotted, IconLayoutKanban } from "@tabler/icons-react";
|
import { Group, Stack, Text } from "@mantine/core";
|
||||||
import FooterButtons, {
|
import FooterClickable from "@/components/layout/Footer/components/FooterClickable";
|
||||||
LinkData,
|
import buttonsData from "@/components/layout/Footer/data/buttonsData";
|
||||||
} from "@/components/layout/Footer/FooterButtons";
|
import styles from "./Footer.module.css";
|
||||||
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
return <FooterButtons buttonsData={buttonsData} />;
|
return (
|
||||||
|
<Group
|
||||||
|
className={styles.container}
|
||||||
|
p={"xs"}
|
||||||
|
wrap={"nowrap"}
|
||||||
|
justify={"space-between"}>
|
||||||
|
{buttonsData.map(data => (
|
||||||
|
<FooterClickable
|
||||||
|
href={data.href}
|
||||||
|
key={data.label}>
|
||||||
|
<Stack
|
||||||
|
gap={0}
|
||||||
|
align={"center"}>
|
||||||
|
<data.icon />
|
||||||
|
<Text>{data.label}</Text>
|
||||||
|
</Stack>
|
||||||
|
</FooterClickable>
|
||||||
|
))}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
|||||||
@ -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 (
|
|
||||||
<Group
|
|
||||||
className={styles.container}
|
|
||||||
p={"xs"}
|
|
||||||
wrap={"nowrap"}
|
|
||||||
justify={"space-between"}>
|
|
||||||
{buttonsData.map(data => (
|
|
||||||
<FooterClickable
|
|
||||||
href={data.href}
|
|
||||||
key={data.label}>
|
|
||||||
<Stack
|
|
||||||
gap={0}
|
|
||||||
align={"center"}>
|
|
||||||
<data.icon />
|
|
||||||
<Text>{data.label}</Text>
|
|
||||||
</Stack>
|
|
||||||
</FooterClickable>
|
|
||||||
))}
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FooterButtons;
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import styles from "./Footer.module.css";
|
import styles from "../Footer.module.css";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
href: string;
|
href: string;
|
||||||
31
src/components/layout/Footer/data/buttonsData.ts
Normal file
31
src/components/layout/Footer/data/buttonsData.ts
Normal file
@ -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;
|
||||||
9
src/components/layout/Footer/types/LinkData.ts
Normal file
9
src/components/layout/Footer/types/LinkData.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { IconPlus } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
type LinkData = {
|
||||||
|
icon: typeof IconPlus;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LinkData;
|
||||||
Reference in New Issue
Block a user