feat: select view buttons

This commit is contained in:
2025-08-28 11:00:41 +04:00
parent e9b8cdb010
commit 4323695069
14 changed files with 220 additions and 22 deletions

View File

@ -0,0 +1,28 @@
"use client";
import { Group } from "@mantine/core";
import ViewSelector from "@/app/deals/components/desktop/ViewSelector/ViewSelector";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
import useIsMobile from "@/hooks/utils/useIsMobile";
const TopToolPanel = () => {
const { projects, setSelectedProjectId, selectedProject } =
useProjectsContext();
const isMobile = useIsMobile();
if (isMobile) return;
return (
<Group justify={"space-between"}>
<ViewSelector />
<ProjectSelect
data={projects}
value={selectedProject}
onChange={value => value && setSelectedProjectId(value.id)}
/>
</Group>
);
};
export default TopToolPanel;

View File

@ -0,0 +1,9 @@
.container {
width: 100%;
border-radius: var(--mantine-radius-xl);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
padding: var(--mantine-spacing-xs);
}

View File

@ -0,0 +1,34 @@
"use client";
import { FC, PropsWithChildren } from "react";
import { Button } from "@mantine/core";
import { useViewContext, View } from "@/app/deals/contexts/ViewContext";
import SmallPageBlock from "@/components/layout/SmallPageBlock/SmallPageBlock";
import style from "./ViewSelectButton.module.css";
type Props = {
viewName: View;
};
const ViewSelectButton: FC<PropsWithChildren<Props>> = ({
viewName,
children,
}) => {
const { view, setView } = useViewContext();
return (
<SmallPageBlock
active={view === viewName}
style={{ borderRadius: "var(--mantine-radius-xl)" }}>
<Button
unstyled
onClick={() => setView(viewName)}
radius="xl"
className={style.container}>
{children}
</Button>
</SmallPageBlock>
);
};
export default ViewSelectButton;

View File

@ -0,0 +1,25 @@
import {
IconCalendarWeekFilled,
IconLayoutDashboard,
IconMenu2,
} from "@tabler/icons-react";
import { Group } from "@mantine/core";
import ViewSelectButton from "@/app/deals/components/desktop/ViewSelectButton/ViewSelectButton";
const ViewSelector = () => {
return (
<Group>
<ViewSelectButton viewName={"board"}>
<IconLayoutDashboard />
</ViewSelectButton>
<ViewSelectButton viewName={"table"}>
<IconMenu2 />
</ViewSelectButton>
<ViewSelectButton viewName={"schedule"}>
<IconCalendarWeekFilled />
</ViewSelectButton>
</Group>
);
};
export default ViewSelector;

View File

@ -1,6 +1,6 @@
.container { .container {
@media (min-width: 48em) { @media (min-width: 48em) {
max-width: calc(100vw - 450px); max-width: calc(100vw - 210px - var(--mantine-spacing-md));
} }
@media (max-width: 48em) { @media (max-width: 48em) {
max-width: 100vw; max-width: 100vw;

View File

@ -0,0 +1,28 @@
"use client";
import { Space } from "@mantine/core";
import MainBlockHeader from "@/app/deals/components/mobile/MainBlockHeader/MainBlockHeader";
import Funnel from "@/app/deals/components/shared/Funnel/Funnel";
import { DealsContextProvider } from "@/app/deals/contexts/DealsContext";
import { useViewContext } from "@/app/deals/contexts/ViewContext";
const PageBody = () => {
const { view } = useViewContext();
if (view === "board")
return (
<>
<MainBlockHeader />
<Space h={"md"} />
<DealsContextProvider>
<Funnel />
</DealsContextProvider>
</>
);
if (view === "table") return <>-</>;
return <>-</>;
};
export default PageBody;

View File

@ -1,6 +1,6 @@
.container { .container {
height: calc(100vh - 150px); height: calc(100vh - 210px);
@media (max-width: 48em) { @media (max-width: 48em) {
width: 80vw; width: 80vw;
height: calc(100vh - 215px); height: calc(100vh - 215px);

View File

@ -0,0 +1,23 @@
"use client";
import { useState } from "react";
import makeContext from "@/lib/contextFactory/contextFactory";
export type View = "board" | "table" | "schedule";
type ViewContextState = {
view: View;
setView: (view: View) => void;
};
const useViewContextState = (): ViewContextState => {
const [view, setView] = useState<View>("board");
return {
view,
setView,
};
};
export const [ViewContextProvider, useViewContext] =
makeContext<ViewContextState>(useViewContextState, "View");

View File

@ -3,12 +3,11 @@ import {
HydrationBoundary, HydrationBoundary,
QueryClient, QueryClient,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import { Space } from "@mantine/core"; import PageBody from "@/app/deals/components/shared/PageBody/PageBody";
import Funnel from "@/app/deals/components/shared/Funnel/Funnel";
import Header from "@/app/deals/components/shared/Header/Header";
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext"; import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext"; import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext";
import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext"; import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext";
import { ViewContextProvider } from "@/app/deals/contexts/ViewContext";
import PageBlock from "@/components/layout/PageBlock/PageBlock"; import PageBlock from "@/components/layout/PageBlock/PageBlock";
import PageContainer from "@/components/layout/PageContainer/PageContainer"; import PageContainer from "@/components/layout/PageContainer/PageContainer";
import { import {
@ -16,7 +15,7 @@ import {
getProjectsOptions, getProjectsOptions,
} from "@/lib/client/@tanstack/react-query.gen"; } from "@/lib/client/@tanstack/react-query.gen";
import { combineProviders } from "@/utils/combineProviders"; import { combineProviders } from "@/utils/combineProviders";
import { DealsContextProvider } from "./contexts/DealsContext"; import TopToolPanel from "./components/desktop/TopToolPanel/TopToolPanel";
async function prefetchData() { async function prefetchData() {
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@ -39,18 +38,16 @@ export default async function DealsPage() {
[HydrationBoundary, { state: dehydrate(queryClient) }], [HydrationBoundary, { state: dehydrate(queryClient) }],
[ProjectsContextProvider], [ProjectsContextProvider],
[BoardsContextProvider], [BoardsContextProvider],
[StatusesContextProvider] [StatusesContextProvider],
[ViewContextProvider]
); );
return ( return (
<Providers> <Providers>
<PageContainer> <PageContainer>
<PageBlock className={"mobile-margin-height"}> <TopToolPanel />
<Header /> <PageBlock>
<Space h={"md"} /> <PageBody />
<DealsContextProvider>
<Funnel />
</DealsContextProvider>
</PageBlock> </PageBlock>
</PageContainer> </PageContainer>
</Providers> </Providers>

View File

@ -18,13 +18,6 @@
} }
} }
.mobile-margin-height {
height: var(--page-height);
@media (min-width: 48em) {
margin: var(--mantine-spacing-md);
}
}
.container-full-height { .container-full-height {
min-height: var(--page-height); min-height: var(--page-height);
height: var(--page-height); height: var(--page-height);

View File

@ -5,6 +5,7 @@
background-color: transparent; background-color: transparent;
@media (min-width: 48em) { @media (min-width: 48em) {
gap: rem(10); gap: var(--mantine-spacing-md);
padding: var(--mantine-spacing-md);
} }
} }

View File

@ -0,0 +1,33 @@
.container {
border-radius: var(--mantine-radius-lg);
@mixin dark {
background-color: var(--mantine-color-dark-7-5);
box-shadow: var(--dark-shadow);
}
@mixin light {
background-color: var(--color-light-whitesmoke);
box-shadow: var(--light-shadow);
}
}
.container-active {
@mixin dark {
color: dodgerblue;
}
@mixin light {
background-color: var(--color-light-aqua);
box-shadow: var(--light-thick-shadow);
}
}
.container:hover {
@mixin dark {
box-shadow: var(--dark-thick-shadow);
}
@mixin light {
background-color: var(--color-light-aqua);
box-shadow: var(--light-thick-shadow);
}
}

View File

@ -0,0 +1,24 @@
import { CSSProperties, FC, ReactNode } from "react";
import classNames from "classnames";
import { Box } from "@mantine/core";
import styles from "@/components/layout/SmallPageBlock/SmallPageBlock.module.css";
type Props = {
children: ReactNode;
style?: CSSProperties;
active?: boolean;
};
const SmallPageBlock: FC<Props> = ({ children, style, active = false }) => {
return (
<Box
className={classNames(
styles.container,
active && styles["container-active"]
)}
style={style}>
{children}
</Box>
);
};
export default SmallPageBlock;

View File

@ -37,7 +37,10 @@ const EnterNameModal = ({
<Flex <Flex
gap={rem(10)} gap={rem(10)}
direction={"column"}> direction={"column"}>
<TextInput {...form.getInputProps("name")} /> <TextInput
{...form.getInputProps("name")}
data-autofocus
/>
<Button <Button
variant={"default"} variant={"default"}
type={"submit"}> type={"submit"}>