diff --git a/src/app/deals/components/shared/BoardMenu/BoardMenu.tsx b/src/app/deals/components/shared/BoardMenu/BoardMenu.tsx index 77f94c0..7d94065 100644 --- a/src/app/deals/components/shared/BoardMenu/BoardMenu.tsx +++ b/src/app/deals/components/shared/BoardMenu/BoardMenu.tsx @@ -1,6 +1,7 @@ import React, { FC } from "react"; import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react"; -import { Box, Group, Menu, Text } from "@mantine/core"; +import { Box, Menu } from "@mantine/core"; +import DropdownMenuItem from "@/components/ui/DropdownMenuItem/DropdownMenuItem"; import ThemeIcon from "@/components/ui/ThemeIcon/ThemeIcon"; import { BoardSchema } from "@/lib/client"; @@ -32,26 +33,16 @@ const BoardMenu: FC = ({ - { - e.stopPropagation(); - startEditing(); - }}> - - - Переименовать - - - { - e.stopPropagation(); - onDeleteBoard(board); - }}> - - - Удалить - - + } + label={"Переименовать"} + /> + onDeleteBoard(board)} + icon={} + label={"Удалить"} + /> ); diff --git a/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.module.css b/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.module.css deleted file mode 100644 index bf47dbe..0000000 --- a/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.module.css +++ /dev/null @@ -1,4 +0,0 @@ - -.header { - border-bottom: solid dodgerblue 3px; -} diff --git a/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.tsx b/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.tsx index ee84609..85123b9 100644 --- a/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.tsx +++ b/src/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader.tsx @@ -5,7 +5,6 @@ import { useBoardsContext } from "@/app/deals/contexts/BoardsContext"; import { useStatusesContext } from "@/app/deals/contexts/StatusesContext"; import InPlaceInput from "@/components/ui/InPlaceInput/InPlaceInput"; import { StatusSchema } from "@/lib/client"; -import styles from "./StatusColumnHeader.module.css"; type Props = { status: StatusSchema; @@ -29,7 +28,9 @@ const StatusColumnHeader: FC = ({ status, isDragging }) => { p={"sm"} wrap={"nowrap"} mb={"xs"} - className={styles.header}> + style={{ + borderBottom: `solid ${status.color} 3px`, + }}> handleSave(value)} @@ -53,6 +54,9 @@ const StatusColumnHeader: FC = ({ status, isDragging }) => { board={selectedBoard} status={status} handleEdit={startEditing} + onStatusColorChange={color => + statusesCrud.onUpdate(status.id, { color }) + } refetchStatuses={refetchStatuses} onDeleteStatus={statusesCrud.onDelete} /> diff --git a/src/app/deals/components/shared/StatusMenu/StatusMenu.tsx b/src/app/deals/components/shared/StatusMenu/StatusMenu.tsx index 8e5ae6a..78e5697 100644 --- a/src/app/deals/components/shared/StatusMenu/StatusMenu.tsx +++ b/src/app/deals/components/shared/StatusMenu/StatusMenu.tsx @@ -3,17 +3,22 @@ import { IconDotsVertical, IconEdit, IconExchange, + IconPalette, IconTrash, } from "@tabler/icons-react"; -import { Box, Group, Menu, Text } from "@mantine/core"; +import { Box, Menu } from "@mantine/core"; +import { modals } from "@mantine/modals"; +import statusColors from "@/app/deals/utils/statusColors"; +import DropdownMenuItem from "@/components/ui/DropdownMenuItem/DropdownMenuItem"; +import ThemeIcon from "@/components/ui/ThemeIcon/ThemeIcon"; import { useDrawersContext } from "@/drawers/DrawersContext"; import useIsMobile from "@/hooks/utils/useIsMobile"; import { BoardSchema, StatusSchema } from "@/lib/client"; -import ThemeIcon from "@/components/ui/ThemeIcon/ThemeIcon"; type Props = { status: StatusSchema; handleEdit: () => void; + onStatusColorChange: (color: string) => void; board: BoardSchema | null; onDeleteStatus: (status: StatusSchema) => void; refetchStatuses?: () => void; @@ -23,6 +28,7 @@ type Props = { const StatusMenu: FC = ({ status, handleEdit, + onStatusColorChange, board, onDeleteStatus, refetchStatuses, @@ -31,6 +37,31 @@ const StatusMenu: FC = ({ const isMobile = useIsMobile(); const { openDrawer } = useDrawersContext(); + const openStatusesMobileEditor = () => { + if (!board) return; + openDrawer({ + key: "statusesMobileEditorDrawer", + props: { + board, + }, + onClose: refetchStatuses, + }); + }; + + const openStatusColorPicker = () => { + if (!board) return; + modals.openContextModal({ + modal: "statusColorPickerModal", + title: "Изменение цвета статуса", + withCloseButton: false, + innerProps: { + color: status.color, + onChange: onStatusColorChange, + switches: statusColors, + }, + }); + }; + return ( @@ -43,44 +74,27 @@ const StatusMenu: FC = ({ - { - e.stopPropagation(); - handleEdit(); - }}> - - - Переименовать - - - { - e.stopPropagation(); - onDeleteStatus(status); - }}> - - - Удалить - - + } + label={"Переименовать"} + /> + } + label={"Изменить цвет"} + /> + onDeleteStatus(status)} + icon={} + label={"Удалить"} + /> {isMobile && withChangeOrderButton && ( - { - e.stopPropagation(); - if (!board) return; - openDrawer({ - key: "statusesMobileEditorDrawer", - props: { - board, - }, - onClose: refetchStatuses, - }); - }}> - - - Изменить порядок - - + } + label={"Изменить порядок"} + /> )} diff --git a/src/app/deals/drawers/ProjectsMobileEditorDrawer/components/ProjectMenu.tsx b/src/app/deals/drawers/ProjectsMobileEditorDrawer/components/ProjectMenu.tsx index bbaa447..488ea5f 100644 --- a/src/app/deals/drawers/ProjectsMobileEditorDrawer/components/ProjectMenu.tsx +++ b/src/app/deals/drawers/ProjectsMobileEditorDrawer/components/ProjectMenu.tsx @@ -1,7 +1,8 @@ import React, { FC } from "react"; import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react"; -import { Box, Group, Menu, Text } from "@mantine/core"; +import { Box, Menu } from "@mantine/core"; import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext"; +import DropdownMenuItem from "@/components/ui/DropdownMenuItem/DropdownMenuItem"; import { ProjectSchema } from "@/lib/client"; import styles from "./../ProjectsEditorDrawer.module.css"; @@ -23,26 +24,16 @@ const ProjectMenu: FC = ({ project, startEditing }) => { - { - e.stopPropagation(); - startEditing(); - }}> - - - Переименовать - - - { - e.stopPropagation(); - projectsCrud.onDelete(project); - }}> - - - Удалить - - + } + label={"Переименовать"} + /> + projectsCrud.onDelete(project)} + icon={} + label={"Удалить"} + /> ); diff --git a/src/app/deals/drawers/StatusesMobileEditorDrawer/components/StatusMobile.tsx b/src/app/deals/drawers/StatusesMobileEditorDrawer/components/StatusMobile.tsx index 354e1d3..5901f89 100644 --- a/src/app/deals/drawers/StatusesMobileEditorDrawer/components/StatusMobile.tsx +++ b/src/app/deals/drawers/StatusesMobileEditorDrawer/components/StatusMobile.tsx @@ -40,6 +40,7 @@ const StatusMobile: FC = ({ status, board }) => { board={board} onDeleteStatus={statusesCrud.onDelete} handleEdit={startEditing} + onStatusColorChange={color => statusesCrud.onUpdate(status.id, { color })} withChangeOrderButton={false} /> diff --git a/src/app/deals/modals/ColorPickerModal/ColorPickerModal.tsx b/src/app/deals/modals/ColorPickerModal/ColorPickerModal.tsx new file mode 100644 index 0000000..27434d3 --- /dev/null +++ b/src/app/deals/modals/ColorPickerModal/ColorPickerModal.tsx @@ -0,0 +1,50 @@ +"use client"; + +import { useState } from "react"; +import { ColorPicker, Flex, Space, Text } from "@mantine/core"; +import { ContextModalProps } from "@mantine/modals"; +import statusColors from "@/app/deals/utils/statusColors"; +import InlineButton from "@/components/ui/InlineButton/InlineButton"; + +type Props = { + color: string; + onChange: (color: string) => void; + switches?: string[]; +}; + +const ColorPickerModal = ({ + id, + context, + innerProps, +}: ContextModalProps) => { + const [color, setColor] = useState(innerProps.color); + + return ( + + + + { + innerProps.onChange(color); + context.closeModal(id); + }}> + Сохранить + + + ); +}; + +export default ColorPickerModal; diff --git a/src/app/deals/utils/statusColors.ts b/src/app/deals/utils/statusColors.ts new file mode 100644 index 0000000..c1cf8a4 --- /dev/null +++ b/src/app/deals/utils/statusColors.ts @@ -0,0 +1,16 @@ +const statusColors = [ + "#228be6", + "#15aabf", + "#12b886", + "#40c057", + "#82c91e", + "#fab005", + "#fd7e14", + "#fa5252", + "#e64980", + "#be4bdb", + "#7950f2", + "#4c6ef5", +]; + +export default statusColors; diff --git a/src/components/ui/BaseTable/components/UpdateDeleteTableActions.tsx b/src/components/ui/BaseTable/components/UpdateDeleteTableActions.tsx index 0e1c194..c53f10c 100644 --- a/src/components/ui/BaseTable/components/UpdateDeleteTableActions.tsx +++ b/src/components/ui/BaseTable/components/UpdateDeleteTableActions.tsx @@ -1,7 +1,8 @@ import React, { CSSProperties, FC } from "react"; import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react"; -import { Box, Flex, Group, Menu, Text } from "@mantine/core"; +import { Box, Flex, Menu } from "@mantine/core"; import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip"; +import DropdownMenuItem from "@/components/ui/DropdownMenuItem/DropdownMenuItem"; import ThemeIcon from "@/components/ui/ThemeIcon/ThemeIcon"; import useIsMobile from "@/hooks/utils/useIsMobile"; @@ -31,26 +32,16 @@ const UpdateDeleteTableActions: FC = ({ - { - e.stopPropagation(); - onChange(); - }}> - - - Редактировать - - - { - e.stopPropagation(); - onDelete(); - }}> - - - Удалить - - + } + label={"Редактировать"} + /> + } + label={"Удалить"} + /> ); diff --git a/src/components/ui/DropdownMenuItem/DropdownMenuItem.tsx b/src/components/ui/DropdownMenuItem/DropdownMenuItem.tsx new file mode 100644 index 0000000..0f33cb6 --- /dev/null +++ b/src/components/ui/DropdownMenuItem/DropdownMenuItem.tsx @@ -0,0 +1,26 @@ +import React, { FC, MouseEventHandler, ReactNode } from "react"; +import { Group, Menu, Text } from "@mantine/core"; + +type Props = { + onClick: MouseEventHandler; + icon: ReactNode; + label: string; +}; + +const DropdownMenuItem: FC = ({ icon, label, onClick }) => { + const onClickWrapper: MouseEventHandler = e => { + e.stopPropagation(); + onClick(e); + }; + + return ( + + + {icon} + {label} + + + ); +}; + +export default DropdownMenuItem; diff --git a/src/hooks/cruds/useStatusesCrud.tsx b/src/hooks/cruds/useStatusesCrud.tsx index 5d7003b..79d42ff 100644 --- a/src/hooks/cruds/useStatusesCrud.tsx +++ b/src/hooks/cruds/useStatusesCrud.tsx @@ -1,4 +1,5 @@ import { LexoRank } from "lexorank"; +import statusColors from "@/app/deals/utils/statusColors"; import { useCrudOperations } from "@/hooks/cruds/baseCrud"; import { CreateStatusSchema, @@ -10,8 +11,8 @@ import { deleteStatusMutation, updateStatusMutation, } from "@/lib/client/@tanstack/react-query.gen"; -import { getMaxByLexorank } from "@/utils/lexorank/max"; import { getNewLexorank } from "@/utils/lexorank/generation"; +import { getMaxByLexorank } from "@/utils/lexorank/max"; type Props = { statuses: StatusSchema[]; @@ -48,10 +49,13 @@ export const useStatusesCrud = ({ const newLexorank = getNewLexorank( lastBoard ? LexoRank.parse(lastBoard.lexorank) : null ); + const nextColorIdx = statuses.length % statusColors.length; + const color = statusColors[nextColorIdx]; return { name: data.name!, boardId, lexorank: newLexorank.toString(), + color, }; }, getUpdateEntity: (old, update) => ({ diff --git a/src/lib/client/types.gen.ts b/src/lib/client/types.gen.ts index 8e9be0c..af06593 100644 --- a/src/lib/client/types.gen.ts +++ b/src/lib/client/types.gen.ts @@ -748,6 +748,10 @@ export type CreateStatusSchema = { * Lexorank */ lexorank: string; + /** + * Color + */ + color: string; }; /** @@ -1522,6 +1526,10 @@ export type StatusSchema = { * Lexorank */ lexorank: string; + /** + * Color + */ + color: string; }; /** @@ -2013,6 +2021,10 @@ export type UpdateStatusSchema = { * Lexorank */ lexorank?: string | null; + /** + * Color + */ + color?: string | null; }; /** diff --git a/src/lib/client/zod.gen.ts b/src/lib/client/zod.gen.ts index 8c11c12..d54ba11 100644 --- a/src/lib/client/zod.gen.ts +++ b/src/lib/client/zod.gen.ts @@ -312,6 +312,7 @@ export const zStatusSchema = z.object({ id: z.int(), name: z.string(), lexorank: z.string(), + color: z.string(), }); /** @@ -556,6 +557,7 @@ export const zCreateStatusSchema = z.object({ name: z.string(), boardId: z.int(), lexorank: z.string(), + color: z.string(), }); /** @@ -1190,6 +1192,7 @@ export const zUpdateServicesKitResponse = z.object({ export const zUpdateStatusSchema = z.object({ name: z.optional(z.union([z.string(), z.null()])), lexorank: z.optional(z.union([z.string(), z.null()])), + color: z.optional(z.union([z.string(), z.null()])), }); /** diff --git a/src/modals/modals.ts b/src/modals/modals.ts index a228ee5..25ce659 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -1,5 +1,6 @@ import BarcodeTemplateEditorModal from "@/app/barcode-templates/modals/BarcodeTemplateFormModal/BarcodeTemplateEditorModal"; import ClientEditorModal from "@/app/clients/modals/ClientFormModal/ClientFormModal"; +import ColorPickerModal from "@/app/deals/modals/ColorPickerModal/ColorPickerModal"; import DealsBoardFiltersModal from "@/app/deals/modals/DealsBoardFiltersModal/DealsBoardFiltersModal"; import DealsScheduleFiltersModal from "@/app/deals/modals/DealsScheduleFiltersModal/DealsScheduleFiltersModal"; import DealsTableFiltersModal from "@/app/deals/modals/DealsTableFiltersModal/DealsTableFiltersModal"; @@ -36,4 +37,5 @@ export const modals = { barcodeTemplateEditorModal: BarcodeTemplateEditorModal, clientEditorModal: ClientEditorModal, printBarcodeModal: PrintBarcodeModal, + statusColorPickerModal: ColorPickerModal, }; diff --git a/src/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductMenu/ProductMenu.tsx b/src/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductMenu/ProductMenu.tsx index 006d091..4433bc9 100644 --- a/src/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductMenu/ProductMenu.tsx +++ b/src/modules/dealModularEditorTabs/FulfillmentBase/mobile/components/ProductMenu/ProductMenu.tsx @@ -1,6 +1,7 @@ import React, { FC } from "react"; import { IconDotsVertical, IconEdit, IconTrash } from "@tabler/icons-react"; -import { Box, Group, Menu, Text } from "@mantine/core"; +import { Box, Menu } from "@mantine/core"; +import DropdownMenuItem from "@/components/ui/DropdownMenuItem/DropdownMenuItem"; import ThemeIcon from "@/components/ui/ThemeIcon/ThemeIcon"; import { DealProductSchema } from "@/lib/client"; @@ -26,26 +27,16 @@ const ProductMenu: FC = ({ value, onChange, onDelete }) => { - { - e.stopPropagation(); - onChange(value); - }}> - - - Редактировать - - - { - e.stopPropagation(); - onDelete(value); - }}> - - - Удалить - - + onChange(value)} + icon={} + label={"Редактировать"} + /> + onDelete(value)} + icon={} + label={"Удалить"} + /> );