feat: boards and statuses editing and creating for mobiles

This commit is contained in:
2025-08-08 15:01:10 +04:00
parent f52fde0097
commit afad1b4605
12 changed files with 109 additions and 23 deletions

View File

@ -30,6 +30,7 @@
"libphonenumber-js": "^1.12.10", "libphonenumber-js": "^1.12.10",
"next": "15.3.3", "next": "15.3.3",
"react": "19.1.0", "react": "19.1.0",
"react-device-detect": "^2.2.3",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-imask": "^7.6.1", "react-imask": "^7.6.1",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",

View File

@ -44,6 +44,7 @@ const Board: FC<Props> = ({ board }) => {
/> />
</> </>
)} )}
modalTitle={"Редактирование доски"}
/> />
</Group> </Group>
); );

View File

@ -1,5 +1,6 @@
import React, { FC } from "react"; import React, { FC } from "react";
import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react"; import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react";
import { isMobile } from "react-device-detect";
import { Box, Group, Menu, Text } from "@mantine/core"; import { Box, Group, Menu, Text } from "@mantine/core";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext"; import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import { BoardSchema } from "@/lib/client"; import { BoardSchema } from "@/lib/client";
@ -19,7 +20,11 @@ const BoardMenu: FC<Props> = ({ isHovered, board, startEditing }) => {
<Box <Box
style={{ style={{
opacity: opacity:
isHovered || selectedBoard?.id === board.id ? 1 : 0, isMobile ||
isHovered ||
selectedBoard?.id === board.id
? 1
: 0,
cursor: "pointer", cursor: "pointer",
}} }}
onClick={e => { onClick={e => {

View File

@ -16,6 +16,7 @@ const CreateBoardButton = () => {
<IconPlus /> <IconPlus />
</Box> </Box>
)} )}
modalTitle={"Создание доски"}
/> />
</Box> </Box>
); );

View File

@ -26,6 +26,7 @@ const CreateStatusButton = () => {
<Text>Создать колонку</Text> <Text>Создать колонку</Text>
</Box> </Box>
)} )}
modalTitle={"Создание колонки"}
/> />
</Box> </Box>
); );

View File

@ -28,7 +28,10 @@ const Funnel: FC = () => {
<ScrollArea <ScrollArea
offsetScrollbars={"x"} offsetScrollbars={"x"}
scrollbarSize={"0.5rem"}> scrollbarSize={"0.5rem"}>
<Group align={"start"}> <Group
align={"start"}
wrap={"nowrap"}
gap={"sm"}>
<FunnelDnd <FunnelDnd
containers={sortedStatuses} containers={sortedStatuses}
items={deals} items={deals}

View File

@ -64,6 +64,7 @@ const StatusColumnWrapper = ({
/> />
</> </>
)} )}
modalTitle={"Редактирование статуса"}
/> />
</Group> </Group>
{children} {children}

View File

@ -1,6 +1,8 @@
import React, { FC, ReactNode, useEffect, useRef, useState } from "react"; import React, { FC, ReactNode, useEffect, useRef, useState } from "react";
import { TextInput } from "@mantine/core"; import { TextInput } from "@mantine/core";
import { Styles } from "@mantine/core/lib/core/styles-api/styles-api.types"; import { Styles } from "@mantine/core/lib/core/styles-api/styles-api.types";
import { isMobile } from "react-device-detect";
import { modals } from "@mantine/modals";
type Props = { type Props = {
defaultValue?: string; defaultValue?: string;
@ -8,6 +10,7 @@ type Props = {
placeholder?: string; placeholder?: string;
getChildren: (startEditing: () => void) => ReactNode; getChildren: (startEditing: () => void) => ReactNode;
inputStyles?: Styles<any>; inputStyles?: Styles<any>;
modalTitle?: string;
}; };
const InPlaceInput: FC<Props> = ({ const InPlaceInput: FC<Props> = ({
@ -15,11 +18,11 @@ const InPlaceInput: FC<Props> = ({
placeholder, placeholder,
inputStyles, inputStyles,
getChildren, getChildren,
modalTitle = "",
defaultValue = "", defaultValue = "",
}) => { }) => {
const [isWriting, setIsWriting] = useState<boolean>(false); const [isWriting, setIsWriting] = useState<boolean>(false);
const [value, setValue] = useState<string>(defaultValue); const [value, setValue] = useState<string>(defaultValue);
console.log(value);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => { useEffect(() => {
@ -45,8 +48,21 @@ const InPlaceInput: FC<Props> = ({
}, [isWriting, value]); }, [isWriting, value]);
const onStartCreating = () => { const onStartCreating = () => {
setValue(defaultValue); if (!isMobile) {
setIsWriting(true); setValue(defaultValue);
setIsWriting(true);
return;
}
modals.openContextModal({
modal: "enterNameModal",
title: modalTitle,
withCloseButton: true,
innerProps: {
onComplete,
defaultValue,
},
});
}; };
const onCancelCreating = () => { const onCancelCreating = () => {

View File

@ -0,0 +1,51 @@
"use client";
import { Button, Flex, rem, TextInput } from "@mantine/core";
import { useForm } from "@mantine/form";
import { ContextModalProps } from "@mantine/modals";
type Props = {
onComplete: (value: string) => void;
defaultValue?: string;
};
type FormType = {
name?: string;
};
const EnterNameModal = ({
id,
context,
innerProps,
}: ContextModalProps<Props>) => {
const form = useForm<FormType>({
initialValues: {
name: innerProps.defaultValue,
},
validate: {
name: name => !name && "Введите название",
},
});
const onSubmit = (values: FormType) => {
innerProps.onComplete(values.name!);
context.closeModal(id);
};
return (
<form onSubmit={form.onSubmit(values => onSubmit(values))}>
<Flex
gap={rem(10)}
direction={"column"}>
<TextInput {...form.getInputProps("name")} />
<Button
variant={"default"}
type={"submit"}>
Сохранить
</Button>
</Flex>
</form>
);
};
export default EnterNameModal;

View File

@ -1,16 +0,0 @@
"use client"
import { Flex, rem, Text } from "@mantine/core";
import { ContextModalProps } from "@mantine/modals";
const TestModal = ({ id, context, innerProps }: ContextModalProps) => {
return (
<Flex
gap={rem(10)}
direction={"column"}>
<Text>Hi</Text>
</Flex>
);
};
export default TestModal;

View File

@ -1,5 +1,5 @@
import TestModal from "@/modals/TestModal/TestModal"; import EnterNameModal from "@/modals/EnterNameModal/EnterNameModal";
export const modals = { export const modals = {
testModal: TestModal, enterNameModal: EnterNameModal,
}; };

View File

@ -6103,6 +6103,7 @@ __metadata:
postcss-simple-vars: "npm:^7.0.1" postcss-simple-vars: "npm:^7.0.1"
prettier: "npm:^3.5.3" prettier: "npm:^3.5.3"
react: "npm:19.1.0" react: "npm:19.1.0"
react-device-detect: "npm:^2.2.3"
react-dom: "npm:19.1.0" react-dom: "npm:19.1.0"
react-imask: "npm:^7.6.1" react-imask: "npm:^7.6.1"
react-redux: "npm:^9.2.0" react-redux: "npm:^9.2.0"
@ -11543,6 +11544,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-device-detect@npm:^2.2.3":
version: 2.2.3
resolution: "react-device-detect@npm:2.2.3"
dependencies:
ua-parser-js: "npm:^1.0.33"
peerDependencies:
react: ">= 0.14.0"
react-dom: ">= 0.14.0"
checksum: 10c0/396bbeeab0cb21da084c67434d204c9cf502fad6c683903313084d3f6487950a36a34f9bf67ccf5c1772a1bb5b79a2a4403fcfe6b51d93877db4c2d9f3a3a925
languageName: node
linkType: hard
"react-docgen-typescript@npm:^2.2.2": "react-docgen-typescript@npm:^2.2.2":
version: 2.4.0 version: 2.4.0
resolution: "react-docgen-typescript@npm:2.4.0" resolution: "react-docgen-typescript@npm:2.4.0"
@ -13653,6 +13666,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ua-parser-js@npm:^1.0.33":
version: 1.0.40
resolution: "ua-parser-js@npm:1.0.40"
bin:
ua-parser-js: script/cli.js
checksum: 10c0/2b6ac642c74323957dae142c31f72287f2420c12dced9603d989b96c132b80232779c429b296d7de4012ef8b64e0d8fadc53c639ef06633ce13d785a78b5be6c
languageName: node
linkType: hard
"ufo@npm:^1.5.4": "ufo@npm:^1.5.4":
version: 1.6.1 version: 1.6.1
resolution: "ufo@npm:1.6.1" resolution: "ufo@npm:1.6.1"