diff --git a/package.json b/package.json index f2e0feb..e94ae5b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@mantine/modals": "^7.11.2", "@mantine/notifications": "^7.11.2", "@reduxjs/toolkit": "^2.2.6", - "@tabler/icons-react": "^3.11.0", + "@tabler/icons-react": "3.11.0", "@tanstack/react-query": "^5.51.9", "@tanstack/react-router": "^1.45.6", "@tanstack/router-devtools": "^1.45.6", @@ -75,10 +75,10 @@ "postcss": "^8.4.39", "postcss-preset-mantine": "^1.16.0", "postcss-simple-vars": "^7.0.1", - "sass": "^1.77.8", + "sass": "^1.81.1", "typescript": "^5.5.3", "typescript-eslint": "^7.16.1", - "vite": "^5.3.4", + "vite": "^6.0.1", "yarn-upgrade-all": "^0.7.2" }, "packageManager": "yarn@4.1.0" diff --git a/src/client/index.ts b/src/client/index.ts index 5f33b29..9d70c32 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -27,6 +27,7 @@ export type { BarcodeTemplateUpdateRequest } from './models/BarcodeTemplateUpdat export type { BarcodeTemplateUpdateResponse } from './models/BarcodeTemplateUpdateResponse'; export type { BaseAttributeSchema } from './models/BaseAttributeSchema'; export type { BaseBoardSchema } from './models/BaseBoardSchema'; +export type { BaseCardTagSchema } from './models/BaseCardTagSchema'; export type { BaseEnumListSchema } from './models/BaseEnumListSchema'; export type { BaseEnumSchema } from './models/BaseEnumSchema'; export type { BaseMarketplaceSchema } from './models/BaseMarketplaceSchema'; @@ -101,6 +102,7 @@ export type { CardStatusHistorySchema } from './models/CardStatusHistorySchema'; export type { CardSummary } from './models/CardSummary'; export type { CardSummaryReorderRequest } from './models/CardSummaryReorderRequest'; export type { CardSummaryResponse } from './models/CardSummaryResponse'; +export type { CardTagSchema } from './models/CardTagSchema'; export type { CardUpdateGeneralInfoRequest } from './models/CardUpdateGeneralInfoRequest'; export type { CardUpdateGeneralInfoResponse } from './models/CardUpdateGeneralInfoResponse'; export type { CardUpdateProductQuantityRequest } from './models/CardUpdateProductQuantityRequest'; @@ -169,6 +171,8 @@ export type { CreateShippingWarehouseRequest } from './models/CreateShippingWare export type { CreateShippingWarehouseResponse } from './models/CreateShippingWarehouseResponse'; export type { CreateStatusRequest } from './models/CreateStatusRequest'; export type { CreateStatusResponse } from './models/CreateStatusResponse'; +export type { CreateTagRequest } from './models/CreateTagRequest'; +export type { CreateTagResponse } from './models/CreateTagResponse'; export type { CreateTaskResponse } from './models/CreateTaskResponse'; export type { CreateTransactionTagRequest } from './models/CreateTransactionTagRequest'; export type { CreateUserRequest } from './models/CreateUserRequest'; @@ -196,6 +200,7 @@ export type { DeleteShippingProductResponse } from './models/DeleteShippingProdu export type { DeleteShippingWarehouseRequest } from './models/DeleteShippingWarehouseRequest'; export type { DeleteShippingWarehouseResponse } from './models/DeleteShippingWarehouseResponse'; export type { DeleteStatusResponse } from './models/DeleteStatusResponse'; +export type { DeleteTagResponse } from './models/DeleteTagResponse'; export type { DeleteTransactionResponse } from './models/DeleteTransactionResponse'; export type { DeleteTransactionTagResponse } from './models/DeleteTransactionTagResponse'; export type { DeleteUserRequest } from './models/DeleteUserRequest'; @@ -346,6 +351,8 @@ export type { StartPauseByShiftIdResponse } from './models/StartPauseByShiftIdRe export type { StartPauseByUserIdResponse } from './models/StartPauseByUserIdResponse'; export type { StartShiftResponse } from './models/StartShiftResponse'; export type { StatusSchema } from './models/StatusSchema'; +export type { SwitchTagRequest } from './models/SwitchTagRequest'; +export type { SwitchTagResponse } from './models/SwitchTagResponse'; export type { SynchronizeMarketplaceRequest } from './models/SynchronizeMarketplaceRequest'; export type { TaskInfoResponse } from './models/TaskInfoResponse'; export type { TimeTrackingData } from './models/TimeTrackingData'; @@ -393,6 +400,8 @@ export type { UpdateStatusOrderRequest } from './models/UpdateStatusOrderRequest export type { UpdateStatusOrderResponse } from './models/UpdateStatusOrderResponse'; export type { UpdateStatusRequest } from './models/UpdateStatusRequest'; export type { UpdateStatusResponse } from './models/UpdateStatusResponse'; +export type { UpdateTagRequest } from './models/UpdateTagRequest'; +export type { UpdateTagResponse } from './models/UpdateTagResponse'; export type { UpdateTimeTrackingRecordRequest } from './models/UpdateTimeTrackingRecordRequest'; export type { UpdateTimeTrackingRecordResponse } from './models/UpdateTimeTrackingRecordResponse'; export type { UpdateTransactionRequest } from './models/UpdateTransactionRequest'; @@ -421,6 +430,7 @@ export { BillingService } from './services/BillingService'; export { BoardService } from './services/BoardService'; export { CardService } from './services/CardService'; export { CardGroupService } from './services/CardGroupService'; +export { CardTagService } from './services/CardTagService'; export { ClientService } from './services/ClientService'; export { DepartmentService } from './services/DepartmentService'; export { MarketplaceService } from './services/MarketplaceService'; diff --git a/src/client/models/BaseCardTagSchema.ts b/src/client/models/BaseCardTagSchema.ts new file mode 100644 index 0000000..4db2641 --- /dev/null +++ b/src/client/models/BaseCardTagSchema.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type BaseCardTagSchema = { + name: string; + projectId: number; +}; + diff --git a/src/client/models/CardGeneralInfoSchema.ts b/src/client/models/CardGeneralInfoSchema.ts index 485b160..f1cd00c 100644 --- a/src/client/models/CardGeneralInfoSchema.ts +++ b/src/client/models/CardGeneralInfoSchema.ts @@ -12,7 +12,7 @@ export type CardGeneralInfoSchema = { manager?: (UserSchema | null); boardId: number; statusId: number; - isServicesProfitAccounted: boolean; clientId: (number | null); + tags: Array; }; diff --git a/src/client/models/CardSchema.ts b/src/client/models/CardSchema.ts index a6fed84..0759de2 100644 --- a/src/client/models/CardSchema.ts +++ b/src/client/models/CardSchema.ts @@ -11,6 +11,7 @@ import type { CardGroupSchema } from './CardGroupSchema'; import type { CardProductSchema } from './CardProductSchema'; import type { CardServiceSchema } from './CardServiceSchema'; import type { CardStatusHistorySchema } from './CardStatusHistorySchema'; +import type { CardTagSchema } from './CardTagSchema'; import type { ClientSchema } from './ClientSchema'; import type { PalletSchema } from './PalletSchema'; import type { ShippingWarehouseSchema } from './ShippingWarehouseSchema'; @@ -39,6 +40,7 @@ export type CardSchema = { pallets?: Array; boxes?: Array; employees?: Array; + tags?: Array; attributes: Array; }; diff --git a/src/client/models/CardSummary.ts b/src/client/models/CardSummary.ts index 15d4d81..1615c16 100644 --- a/src/client/models/CardSummary.ts +++ b/src/client/models/CardSummary.ts @@ -6,6 +6,7 @@ import type { BaseMarketplaceSchema } from './BaseMarketplaceSchema'; import type { BoardSchema } from './BoardSchema'; import type { CardBillRequestSchema } from './CardBillRequestSchema'; import type { CardGroupSchema } from './CardGroupSchema'; +import type { CardTagSchema } from './CardTagSchema'; import type { StatusSchema } from './StatusSchema'; export type CardSummary = { id: number; @@ -18,6 +19,7 @@ export type CardSummary = { rank: number; baseMarketplace?: (BaseMarketplaceSchema | null); totalProducts: number; + tags: Array; shipmentWarehouseId: (number | null); shipmentWarehouseName: (string | null); billRequest?: (CardBillRequestSchema | null); diff --git a/src/client/models/CardTagSchema.ts b/src/client/models/CardTagSchema.ts new file mode 100644 index 0000000..8008529 --- /dev/null +++ b/src/client/models/CardTagSchema.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type CardTagSchema = { + name: string; + projectId: number; + id: number; +}; + diff --git a/src/client/models/CreateTagRequest.ts b/src/client/models/CreateTagRequest.ts new file mode 100644 index 0000000..9589330 --- /dev/null +++ b/src/client/models/CreateTagRequest.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BaseCardTagSchema } from './BaseCardTagSchema'; +export type CreateTagRequest = { + tag: BaseCardTagSchema; +}; + diff --git a/src/client/models/CreateTagResponse.ts b/src/client/models/CreateTagResponse.ts new file mode 100644 index 0000000..5369692 --- /dev/null +++ b/src/client/models/CreateTagResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type CreateTagResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/DeleteTagResponse.ts b/src/client/models/DeleteTagResponse.ts new file mode 100644 index 0000000..ca5e290 --- /dev/null +++ b/src/client/models/DeleteTagResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type DeleteTagResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/FullProjectSchema.ts b/src/client/models/FullProjectSchema.ts index f55ffdd..d381dd9 100644 --- a/src/client/models/FullProjectSchema.ts +++ b/src/client/models/FullProjectSchema.ts @@ -3,12 +3,14 @@ /* tslint:disable */ /* eslint-disable */ import type { AttributeSchema } from './AttributeSchema'; +import type { CardTagSchema } from './CardTagSchema'; import type { ModuleSchema } from './ModuleSchema'; export type FullProjectSchema = { name: string; id: number; attributes: Array; modules: Array; + tags: Array; boardsCount: number; }; diff --git a/src/client/models/GetProfitChartDataRequest.ts b/src/client/models/GetProfitChartDataRequest.ts index 75b3db3..8b6ee34 100644 --- a/src/client/models/GetProfitChartDataRequest.ts +++ b/src/client/models/GetProfitChartDataRequest.ts @@ -9,6 +9,7 @@ export type GetProfitChartDataRequest = { projectId: number; boardId: number; cardStatusId: number; + cardTagId: number; managerId: number; expenseTagId: number; incomeTagId: number; diff --git a/src/client/models/GetProfitTableDataRequest.ts b/src/client/models/GetProfitTableDataRequest.ts index 356c639..cef06e4 100644 --- a/src/client/models/GetProfitTableDataRequest.ts +++ b/src/client/models/GetProfitTableDataRequest.ts @@ -10,6 +10,7 @@ export type GetProfitTableDataRequest = { projectId: number; boardId: number; cardStatusId: number; + cardTagId: number; managerId: number; expenseTagId: number; incomeTagId: number; diff --git a/src/client/models/ProductsAndServicesGeneralInfoSchema.ts b/src/client/models/ProductsAndServicesGeneralInfoSchema.ts index 377f551..925874f 100644 --- a/src/client/models/ProductsAndServicesGeneralInfoSchema.ts +++ b/src/client/models/ProductsAndServicesGeneralInfoSchema.ts @@ -4,5 +4,6 @@ /* eslint-disable */ export type ProductsAndServicesGeneralInfoSchema = { shippingWarehouse?: (string | null); + isServicesProfitAccounted: boolean; }; diff --git a/src/client/models/ProfitTableGroupBy.ts b/src/client/models/ProfitTableGroupBy.ts index 6abd188..9e3e89e 100644 --- a/src/client/models/ProfitTableGroupBy.ts +++ b/src/client/models/ProfitTableGroupBy.ts @@ -2,4 +2,4 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type ProfitTableGroupBy = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; +export type ProfitTableGroupBy = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; diff --git a/src/client/models/ProjectSchema.ts b/src/client/models/ProjectSchema.ts index f5c7aae..81dae02 100644 --- a/src/client/models/ProjectSchema.ts +++ b/src/client/models/ProjectSchema.ts @@ -3,11 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { AttributeSchema } from './AttributeSchema'; +import type { CardTagSchema } from './CardTagSchema'; import type { ModuleSchema } from './ModuleSchema'; export type ProjectSchema = { name: string; id: number; attributes: Array; modules: Array; + tags: Array; }; diff --git a/src/client/models/SwitchTagRequest.ts b/src/client/models/SwitchTagRequest.ts new file mode 100644 index 0000000..57095fa --- /dev/null +++ b/src/client/models/SwitchTagRequest.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type SwitchTagRequest = { + tagId: number; + cardId?: (number | null); + groupId?: (number | null); +}; + diff --git a/src/client/models/SwitchTagResponse.ts b/src/client/models/SwitchTagResponse.ts new file mode 100644 index 0000000..d3bb911 --- /dev/null +++ b/src/client/models/SwitchTagResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type SwitchTagResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/models/UpdateTagRequest.ts b/src/client/models/UpdateTagRequest.ts new file mode 100644 index 0000000..d96fdc1 --- /dev/null +++ b/src/client/models/UpdateTagRequest.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CardTagSchema } from './CardTagSchema'; +export type UpdateTagRequest = { + tag: CardTagSchema; +}; + diff --git a/src/client/models/UpdateTagResponse.ts b/src/client/models/UpdateTagResponse.ts new file mode 100644 index 0000000..ad2699e --- /dev/null +++ b/src/client/models/UpdateTagResponse.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type UpdateTagResponse = { + ok: boolean; + message: string; +}; + diff --git a/src/client/services/CardTagService.ts b/src/client/services/CardTagService.ts new file mode 100644 index 0000000..c981439 --- /dev/null +++ b/src/client/services/CardTagService.ts @@ -0,0 +1,97 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CreateTagRequest } from '../models/CreateTagRequest'; +import type { CreateTagResponse } from '../models/CreateTagResponse'; +import type { DeleteTagResponse } from '../models/DeleteTagResponse'; +import type { SwitchTagRequest } from '../models/SwitchTagRequest'; +import type { SwitchTagResponse } from '../models/SwitchTagResponse'; +import type { UpdateTagRequest } from '../models/UpdateTagRequest'; +import type { UpdateTagResponse } from '../models/UpdateTagResponse'; +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; +export class CardTagService { + /** + * Create Tag + * @returns CreateTagResponse Successful Response + * @throws ApiError + */ + public static createTag({ + requestBody, + }: { + requestBody: CreateTagRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/card-tag/', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Update Tag + * @returns UpdateTagResponse Successful Response + * @throws ApiError + */ + public static updateTag({ + requestBody, + }: { + requestBody: UpdateTagRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'PATCH', + url: '/card-tag/', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Delete Tag + * @returns DeleteTagResponse Successful Response + * @throws ApiError + */ + public static deleteTag({ + cardTagId, + }: { + cardTagId: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/card-tag/{card_tag_id}', + path: { + 'card_tag_id': cardTagId, + }, + errors: { + 422: `Validation Error`, + }, + }); + } + /** + * Switch Tag + * @returns SwitchTagResponse Successful Response + * @throws ApiError + */ + public static switchTag({ + requestBody, + }: { + requestBody: SwitchTagRequest, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/card-tag/switch', + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } +} diff --git a/src/components/DealStatusSelect/CardStatusSelect.tsx b/src/components/CardStatusSelect/CardStatusSelect.tsx similarity index 100% rename from src/components/DealStatusSelect/CardStatusSelect.tsx rename to src/components/CardStatusSelect/CardStatusSelect.tsx diff --git a/src/components/Dnd/Cards/CardGroupView/CardGroupView.tsx b/src/components/Dnd/Cards/CardGroupView/CardGroupView.tsx index 50b76a1..cf3e540 100644 --- a/src/components/Dnd/Cards/CardGroupView/CardGroupView.tsx +++ b/src/components/Dnd/Cards/CardGroupView/CardGroupView.tsx @@ -7,6 +7,7 @@ import { useDebouncedValue } from "@mantine/hooks"; import { notifications } from "../../../../shared/lib/notifications.ts"; import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; +import CardTags from "../CardTags/CardTags.tsx"; type Props = { cards: CardSummary[]; @@ -75,6 +76,7 @@ export const CardGroupView: FC = ({ cards, group }) => { /> ))} + {isServicesAndProductsIncluded && ( = ({ cardSummary, color }) => { const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isClientIncluded = isModuleInProject(Modules.CLIENTS, selectedProject); - const onDealSummaryClick = () => { + const onCardSummaryClick = () => { CardService.getCardById({ cardId: cardSummary.id }).then(card => { setSelectedCard(card); }); @@ -59,7 +60,7 @@ const CardSummaryItem: FC = ({ cardSummary, color }) => { icon: , }, ])} - onClick={() => onDealSummaryClick()} + onClick={() => onCardSummaryClick()} className={styles["container"]} style={{ backgroundColor: color }} > @@ -95,6 +96,9 @@ const CardSummaryItem: FC = ({ cardSummary, color }) => { )} + {!cardSummary.group?.id && ( + + )} diff --git a/src/components/Dnd/Cards/CardTags/CardTags.module.css b/src/components/Dnd/Cards/CardTags/CardTags.module.css new file mode 100644 index 0000000..10ead59 --- /dev/null +++ b/src/components/Dnd/Cards/CardTags/CardTags.module.css @@ -0,0 +1,21 @@ +.add-tag-button { + background-color: var(--mantine-color-dark-6); + border: 1px gray dashed; + border-radius: 50%; + width: 1.5rem; + height: 1.5rem; + padding: 0; + cursor: pointer; +} + +.add-tag-button:hover { + border-color: white; +} + +.add-tag-button-icon { + color: gray; +} + +.add-tag-button-icon:hover { + color: white; +} diff --git a/src/components/Dnd/Cards/CardTags/CardTags.tsx b/src/components/Dnd/Cards/CardTags/CardTags.tsx new file mode 100644 index 0000000..9c2e951 --- /dev/null +++ b/src/components/Dnd/Cards/CardTags/CardTags.tsx @@ -0,0 +1,98 @@ +import { Center, Checkbox, Group, Menu, Pill, rem, Stack, Text } from "@mantine/core"; +import { CardTagSchema, CardTagService } from "../../../../client"; +import { IconPlus } from "@tabler/icons-react"; +import styles from "./CardTags.module.css"; +import classNames from "classnames"; +import React from "react"; +import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; +import { notifications } from "../../../../shared/lib/notifications.ts"; +import { useCardPageContext } from "../../../../pages/CardsPage/contexts/CardPageContext.tsx"; + +type Props = { + cardId?: number; + groupId?: number; + tags: CardTagSchema[]; +} + +const CardTags = ({ tags, cardId, groupId }: Props) => { + const { selectedProject } = useProjectsContext(); + const { refetchCards } = useCardPageContext(); + + if (selectedProject?.tags.length === 0) return; + + const onTagClick = (tag: CardTagSchema, event: React.MouseEvent) => { + event.stopPropagation(); + + CardTagService.switchTag({ + requestBody: { + cardId: cardId ?? null, + groupId: groupId ?? null, + tagId: tag.id, + }, + }) + .then(({ ok, message }) => { + if (!ok) notifications.error({ message }); + refetchCards(); + }) + .catch(err => console.log(err)); + }; + + const addTagButton = ( + + + + + + e.stopPropagation()} + > + {selectedProject?.tags.map(tag => ( + + cardTag.id === tag.id)} + onClick={(event) => onTagClick(tag, event)} + /> + {tag.name} + + ))} + + + + ); + + const pills = tags.map(tag => { + return ( + + {tag.name} + + ); + }); + + return ( + + {addTagButton} + {pills} + + ); +}; + +export default CardTags; diff --git a/src/components/Dnd/Statuses/Status/Status.tsx b/src/components/Dnd/Statuses/Status/Status.tsx index dd11393..0d48db1 100644 --- a/src/components/Dnd/Statuses/Status/Status.tsx +++ b/src/components/Dnd/Statuses/Status/Status.tsx @@ -1,5 +1,5 @@ import styles from "../../../../pages/CardsPage/ui/CardsPage.module.css"; -import { Divider, Text, Title } from "@mantine/core"; +import { Divider, Stack, Text, Title } from "@mantine/core"; import getColumnColor from "../../Cards/CardsDndColumn/utils/getColumnColor.ts"; import { BoardSchema, CardSummary, StatusSchema } from "../../../../client"; import { getPluralForm } from "../../../../shared/lib/utils.ts"; @@ -10,6 +10,7 @@ import { useContextMenu } from "mantine-contextmenu"; import { IconEdit, IconTrash } from "@tabler/icons-react"; import useStatus from "./hooks/useStatus.tsx"; import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; +import { useEqualHeightsContext } from "./contexts/EqualHeightContext.tsx"; type Props = { @@ -25,6 +26,10 @@ const Status = ({ summaries, status, board, dragState, index, refetch }: Props) const isDropDisabled = dragState === DragState.DRAG_CARD; const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, board?.project); + const { + divRefs, + } = useEqualHeightsContext(); + const { onEditStatusClick, onDeleteStatusClick, @@ -60,50 +65,63 @@ const Status = ({ summaries, status, board, dragState, index, refetch }: Props) }, ]); - return ( - - {provided => ( -
- - {(provided) => ( -
+ + + {status.name} + + + {getDealsText()} + + + + + ); - className={styles["header-statuses"]} - onContextMenu={contextMenu()} - > - - {status.name} - - - {getDealsText()} - - -
- )} -
- {provided.placeholder} -
- )} -
+ return ( +
(divRefs.current[index] = el)} + > + + {provided => ( +
+ + {(provided) => ( +
+ {header} +
+ )} +
+ {provided.placeholder} +
+ )} +
+
); }; diff --git a/src/components/Dnd/Statuses/Status/contexts/EqualHeightContext.tsx b/src/components/Dnd/Statuses/Status/contexts/EqualHeightContext.tsx new file mode 100644 index 0000000..4b29e82 --- /dev/null +++ b/src/components/Dnd/Statuses/Status/contexts/EqualHeightContext.tsx @@ -0,0 +1,83 @@ +import React, { createContext, FC, useContext, useEffect, useRef, useState } from "react"; +import { StatusSchema } from "../../../../../client"; + +type EqualHeightsContextState = { + divRefs: React.MutableRefObject<(HTMLDivElement | null)[]>; +}; + +const EqualHeightsContext = createContext( + undefined, +); + +type EqualHeightsContextProps = { + statuses: StatusSchema[]; +} + +const useEqualHeightsContextState = ({ statuses }: EqualHeightsContextProps) => { + const divRefs = useRef<(HTMLDivElement | null)[]>([]); + + const updateHeights = () => { + divRefs.current.forEach(div => { + if (div) { + div.style.minHeight = "auto"; + div.style.height = "auto"; + } + }); + + const heights = divRefs.current.map(div => div?.offsetHeight || 0); + const newMaxHeight = Math.max(...heights); + + divRefs.current.forEach(div => { + if (div) { + div.style.minHeight = newMaxHeight + "px"; + div.style.height = newMaxHeight + "px"; + } + }); + }; + + const [windowWidth, setWindowWidth] = useState(window.innerWidth); + + useEffect(() => { + const handleResize = () => { + setWindowWidth(window.innerWidth); + }; + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + useEffect(() => { + updateHeights(); + }, [windowWidth, divRefs.current.length, statuses]); + + return { + divRefs, + }; +}; + +type EqualHeightsContextProviderProps = { + children: React.ReactNode; +} & EqualHeightsContextProps; + +export const EqualHeightsContextProvider: FC = ({ children, statuses }) => { + const state = useEqualHeightsContextState({ statuses }); + + return ( + + {children} + + ); +}; + +export const useEqualHeightsContext = () => { + const context = useContext(EqualHeightsContext); + if (!context) { + throw new Error( + "useEqualHeightsContext must be used within a EqualHeightsContextProvider", + ); + } + return context; +}; diff --git a/src/components/Dnd/Statuses/Statuses/Statuses.tsx b/src/components/Dnd/Statuses/Statuses/Statuses.tsx index 9702793..3b0a0bb 100644 --- a/src/components/Dnd/Statuses/Statuses/Statuses.tsx +++ b/src/components/Dnd/Statuses/Statuses/Statuses.tsx @@ -6,6 +6,7 @@ import { Flex, rem, Stack } from "@mantine/core"; import { DragDropContext } from "@hello-pangea/dnd"; import useDnd from "../../../../pages/CardsPage/hooks/useDnd.tsx"; import Status from "../Status/Status.tsx"; +import { EqualHeightsContextProvider } from "../Status/contexts/EqualHeightContext.tsx"; type Props = { @@ -67,19 +68,21 @@ const Statuses = ({ onDragStart={onDragStart} onDragEnd={onDragEnd} > - - - {selectedBoard && - statuses.map(((status: StatusSchema, index: number) => { - return statusDndColumn(status, index); - })) - } + + + + {selectedBoard && + statuses.map(((status: StatusSchema, index: number) => { + return statusDndColumn(status, index); + })) + } + + - - + ); }; diff --git a/src/components/Selects/CardTagSelect/CardTagSelect.tsx b/src/components/Selects/CardTagSelect/CardTagSelect.tsx new file mode 100644 index 0000000..79711ef --- /dev/null +++ b/src/components/Selects/CardTagSelect/CardTagSelect.tsx @@ -0,0 +1,26 @@ +import { FC } from "react"; +import { CardTagSchema, ProjectSchema } from "../../../client"; +import ObjectSelect, { ObjectSelectProps } from "../../ObjectSelect/ObjectSelect.tsx"; + +type OtherProps = { + project: ProjectSchema | null; +} + +type SelectProps = Omit, "data" | "getLabelFn" | "getValueFn">; + +type Props = OtherProps & SelectProps; + +const CardTagSelect: FC = ({ project, ...props }) => { + const onClear = () => props.onChange(null); + + return ( + + ); +}; +export default CardTagSelect; diff --git a/src/modals/modals.ts b/src/modals/modals.ts index 8130404..cfd5cfd 100644 --- a/src/modals/modals.ts +++ b/src/modals/modals.ts @@ -38,6 +38,7 @@ import StatusModal from "../pages/CardsPage/modals/StatusModal/StatusModal.tsx"; import AttributeModal from "../pages/AdminPage/tabs/Attributes/modals/AttributeModal.tsx"; import CreateProjectModal from "../pages/CardsPage/drawers/ProjectEditDrawer/tabs/General/modals/CreateProjectModal.tsx"; +import CardTagModal from "../pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/modals/CardTagModal.tsx"; export const modals = { enterDeadline: EnterDeadlineModal, @@ -77,4 +78,5 @@ export const modals = { statusModal: StatusModal, attributeModal: AttributeModal, createProjectModal: CreateProjectModal, + cardTagModal: CardTagModal, }; diff --git a/src/pages/CardsPage/components/CardTagsInput/CardTagsInput.tsx b/src/pages/CardsPage/components/CardTagsInput/CardTagsInput.tsx new file mode 100644 index 0000000..4d804fb --- /dev/null +++ b/src/pages/CardsPage/components/CardTagsInput/CardTagsInput.tsx @@ -0,0 +1,18 @@ +import { TagsInput, TagsInputProps } from "@mantine/core"; +import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; + +type Props = Omit + +const CardTagsInput = (props: Props) => { + const { selectedProject } = useProjectsContext(); + + return ( + tag.name)} + label={"Теги"} + /> + ); +}; + +export default CardTagsInput; diff --git a/src/pages/CardsPage/drawers/CardEditDrawer/CardEditDrawer.tsx b/src/pages/CardsPage/drawers/CardEditDrawer/CardEditDrawer.tsx index f51b46b..e38661d 100644 --- a/src/pages/CardsPage/drawers/CardEditDrawer/CardEditDrawer.tsx +++ b/src/pages/CardsPage/drawers/CardEditDrawer/CardEditDrawer.tsx @@ -42,7 +42,7 @@ const CardEditDrawer: FC = () => { const getTabPanel = (value: string, component: ReactNode) => { return ( - + { const getTabs = () => { const moduleTabs = modules.map(module => ( diff --git a/src/pages/CardsPage/drawers/CardEditDrawer/tabs/GeneralTab/GeneralTab.tsx b/src/pages/CardsPage/drawers/CardEditDrawer/tabs/GeneralTab/GeneralTab.tsx index f2d490c..4989dd4 100644 --- a/src/pages/CardsPage/drawers/CardEditDrawer/tabs/GeneralTab/GeneralTab.tsx +++ b/src/pages/CardsPage/drawers/CardEditDrawer/tabs/GeneralTab/GeneralTab.tsx @@ -22,9 +22,10 @@ import { ButtonCopyControlled } from "../../../../../../components/ButtonCopyCon import { useClipboard } from "@mantine/hooks"; import ProjectSelect from "../../../../../../components/ProjectSelect/ProjectSelect.tsx"; import BoardSelect from "../../../../../../components/BoardSelect/BoardSelect.tsx"; -import CardStatusSelect from "../../../../../../components/DealStatusSelect/CardStatusSelect.tsx"; +import CardStatusSelect from "../../../../../../components/CardStatusSelect/CardStatusSelect.tsx"; import CardAttributeFields from "../../../../../../components/CardAttributeFields/CardAttributeFields.tsx"; import getAttributesFromCard from "../../../../../../components/CardAttributeFields/utils/getAttributesFromCard.ts"; +import CardTagsInput from "../../../../components/CardTagsInput/CardTagsInput.tsx"; type Props = { card: CardSchema; @@ -41,6 +42,7 @@ const Content: FC = ({ card }) => { const clipboard = useClipboard(); const queryClient = useQueryClient(); const [project, setProject] = useState(card.board.project); + const [cardTags, setCardTags] = useState(card.tags?.map(tag => tag.name) ?? []); const getInitialValues = (card: CardSchema): CardGeneralFormType => { return { @@ -82,6 +84,7 @@ const Content: FC = ({ card }) => { boardId: values.board.id, clientId: values.client?.id ?? null, attributes, + tags: cardTags, }, }, }).then(({ ok, message }) => { @@ -125,6 +128,20 @@ const Content: FC = ({ card }) => { }); }; + const cancelChanges = () => { + form.reset(); + setCardTags(card.tags?.map(tag => tag.name) ?? []); + }; + + const isEqualValues = () => { + const initialCardTagsSet = new Set(card.tags?.map(tag => tag.name) ?? []); + + const tagsEqual = initialCardTagsSet.size === cardTags.length && + cardTags.every(element => initialCardTagsSet.has(element)); + + return isEqual(initialValues, form.values) && tagsEqual; + }; + return (
handleSubmit(values))}> = ({ card }) => { placeholder={"Введите коментарий"} {...form.getInputProps("comment")} /> + {project && project?.tags.length > 0 && ( + + )} {project && ( = ({ card }) => { diff --git a/src/pages/CardsPage/drawers/ProjectEditDrawer/ProjectEditDrawer.tsx b/src/pages/CardsPage/drawers/ProjectEditDrawer/ProjectEditDrawer.tsx index 4c258e8..ff691b4 100644 --- a/src/pages/CardsPage/drawers/ProjectEditDrawer/ProjectEditDrawer.tsx +++ b/src/pages/CardsPage/drawers/ProjectEditDrawer/ProjectEditDrawer.tsx @@ -1,11 +1,12 @@ import { Box, Drawer, rem, Tabs } from "@mantine/core"; -import { IconHexagons, IconSettings, IconSubtask } from "@tabler/icons-react"; +import { IconHexagons, IconSettings, IconSubtask, IconTags } from "@tabler/icons-react"; import { ReactNode } from "react"; import { motion } from "framer-motion"; import { useProjectsEditorContext } from "../../contexts/ProjectsEditorContext.tsx"; import General from "./tabs/General/General.tsx"; import Attributes from "./tabs/Attributes/Attributes.tsx"; import Modules from "./tabs/Modules/Modules.tsx"; +import Tags from "./tabs/Tags/Tags.tsx"; const ProjectEditDrawer = () => { @@ -67,11 +68,17 @@ const ProjectEditDrawer = () => { leftSection={}> Атрибуты + }> + Теги + - {getTabPanel("general", )} - {getTabPanel("attributes", )} + {getTabPanel("general", )} + {getTabPanel("attributes", )} {getTabPanel("modules", )} + {getTabPanel("tags", )} ); diff --git a/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/Tags.tsx b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/Tags.tsx new file mode 100644 index 0000000..09270f6 --- /dev/null +++ b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/Tags.tsx @@ -0,0 +1,66 @@ +import { ActionIcon, Flex, Group, rem, Stack, Tooltip } from "@mantine/core"; +import { BaseTable } from "../../../../../../components/BaseTable/BaseTable.tsx"; +import tagsTableColumns from "./hooks/tagsTableColumns.tsx"; +import { IconEdit, IconPlus, IconTrash } from "@tabler/icons-react"; +import { CardTagSchema } from "../../../../../../client"; +import { MRT_TableOptions } from "mantine-react-table"; +import useTags from "./hooks/useTags.tsx"; +import InlineButton from "../../../../../../components/InlineButton/InlineButton.tsx"; + + +const Tags = () => { + const columns = tagsTableColumns(); + + const { + project, + onDeleteClick, + onChangeClick, + onCreateClick, + } = useTags(); + + return ( + + + + + Создать + + + { + return ( + + + onDeleteClick(row.original)} + variant={"default"}> + + + + + onChangeClick(row.original)} + variant={"default"}> + + + + + ); + }, + } as MRT_TableOptions + } + /> + + ); +}; + +export default Tags; diff --git a/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/hooks/tagsTableColumns.tsx b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/hooks/tagsTableColumns.tsx new file mode 100644 index 0000000..4b60d5d --- /dev/null +++ b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/hooks/tagsTableColumns.tsx @@ -0,0 +1,18 @@ +import { useMemo } from "react"; +import { MRT_ColumnDef } from "mantine-react-table"; +import { CardTagSchema } from "../../../../../../../client"; + +const useTagsTableColumns = () => { + return useMemo[]>( + () => [ + { + header: "Название", + accessorKey: "name", + size: 1000, + }, + ], + [], + ); +}; + +export default useTagsTableColumns; diff --git a/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/hooks/useTags.tsx b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/hooks/useTags.tsx new file mode 100644 index 0000000..bea4475 --- /dev/null +++ b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/hooks/useTags.tsx @@ -0,0 +1,109 @@ +import { + CancelablePromise, + CardTagSchema, + CardTagService, + CreateTagResponse, + DeleteTagResponse, + UpdateTagResponse, +} from "../../../../../../../client"; +import { notifications } from "../../../../../../../shared/lib/notifications.ts"; +import { modals } from "@mantine/modals"; +import { Text } from "@mantine/core"; +import { useProjectsContext } from "../../../../../../../contexts/ProjectsContext.tsx"; +import { useCardPageContext } from "../../../../../contexts/CardPageContext.tsx"; + + +const useTags = () => { + const { selectedProject: project, refetchProjects } = useProjectsContext(); + const { refetchCards } = useCardPageContext(); + + const processResponse = ( + response: CancelablePromise, + ) => { + response + .then(({ ok, message }) => { + if (!ok) { + notifications.error({ message }); + return; + } + refetchProjects(); + refetchCards(); + }) + .catch(err => console.log(err)); + }; + + const onDelete = (tag: CardTagSchema) => { + const response = CardTagService.deleteTag({ + cardTagId: tag.id, + }); + + processResponse(response); + }; + + const onDeleteClick = (tag: CardTagSchema) => { + modals.openConfirmModal({ + title: "Удаление тега", + children: ( + + Вы уверены что хотите удалить тег {tag.name} + + ), + labels: { confirm: "Да", cancel: "Нет" }, + confirmProps: { color: "red" }, + onConfirm: () => onDelete(tag), + }); + }; + + const onChange = (tag: CardTagSchema) => { + const response = CardTagService.updateTag({ + requestBody: { tag }, + }); + + processResponse(response); + }; + + const onChangeClick = (tag: CardTagSchema) => { + modals.openContextModal({ + modal: "cardTagModal", + innerProps: { + element: tag, + onChange, + }, + withCloseButton: false, + }); + }; + + const onCreate = (tag: CardTagSchema) => { + if (!project) return; + + const response = CardTagService.createTag({ + requestBody: { + tag: { + name: tag.name, + projectId: project.id, + } + }, + }); + + processResponse(response); + }; + + const onCreateClick = () => { + modals.openContextModal({ + modal: "cardTagModal", + innerProps: { + onCreate, + }, + withCloseButton: false, + }); + }; + + return { + project, + onDeleteClick, + onChangeClick, + onCreateClick, + }; +}; + +export default useTags; diff --git a/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/modals/CardTagModal.tsx b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/modals/CardTagModal.tsx new file mode 100644 index 0000000..95f3227 --- /dev/null +++ b/src/pages/CardsPage/drawers/ProjectEditDrawer/tabs/Tags/modals/CardTagModal.tsx @@ -0,0 +1,47 @@ +import { ContextModalProps } from "@mantine/modals"; +import BaseFormModal, { + CreateEditFormProps, +} from "../../../../../../ClientsPage/modals/BaseFormModal/BaseFormModal.tsx"; +import { CardTagSchema } from "../../../../../../../client"; +import { useForm } from "@mantine/form"; +import { TextInput } from "@mantine/core"; + +type Props = CreateEditFormProps; + +const CardTagModal = ({ + context, + id, + innerProps, + }: ContextModalProps) => { + const isEditing = "element" in innerProps; + const initialValue: Partial = isEditing + ? innerProps.element + : { + name: "", + }; + + const form = useForm>({ + initialValues: initialValue, + validate: { + name: name => !name && "Необходимо указать название тега", + }, + }); + + return ( + context.closeContextModal(id)} + {...innerProps}> + + + + + ); +}; + +export default CardTagModal; diff --git a/src/pages/CardsPage/modals/CardsTableFiltersModal.tsx b/src/pages/CardsPage/modals/CardsTableFiltersModal.tsx index ab2e734..1ffb859 100644 --- a/src/pages/CardsPage/modals/CardsTableFiltersModal.tsx +++ b/src/pages/CardsPage/modals/CardsTableFiltersModal.tsx @@ -3,7 +3,7 @@ import { Flex, Modal, NumberInput, rem } from "@mantine/core"; import { UseFormReturnType } from "@mantine/form"; import { CardsPageState } from "../hooks/useCardsPageState.tsx"; import ObjectSelect from "../../../components/ObjectSelect/ObjectSelect.tsx"; -import CardStatusSelect from "../../../components/DealStatusSelect/CardStatusSelect.tsx"; +import CardStatusSelect from "../../../components/CardStatusSelect/CardStatusSelect.tsx"; import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx"; import { useDisclosure } from "@mantine/hooks"; diff --git a/src/pages/CardsPage/ui/CardsPage.module.css b/src/pages/CardsPage/ui/CardsPage.module.css index f18f2c3..9b1cfbc 100644 --- a/src/pages/CardsPage/ui/CardsPage.module.css +++ b/src/pages/CardsPage/ui/CardsPage.module.css @@ -21,6 +21,7 @@ align-items: stretch; text-align: center; flex-direction: column; + height: 100%; } .delete { diff --git a/src/pages/StatisticsPage/components/ExpenseTagSelect/TransactionTagSelect.tsx b/src/pages/StatisticsPage/components/ExpenseTagSelect/TransactionTagSelect.tsx index 7dc57e6..0bc7551 100644 --- a/src/pages/StatisticsPage/components/ExpenseTagSelect/TransactionTagSelect.tsx +++ b/src/pages/StatisticsPage/components/ExpenseTagSelect/TransactionTagSelect.tsx @@ -6,13 +6,15 @@ import useAllTransactionTagsList from "../../../AdminPage/hooks/useAllTransactio type IsIncome = { isIncome: boolean; } + type Props = Omit< ObjectSelectProps, "data" | "getValueFn" | "getLabelFn" > & IsIncome; -const TransactionTagSelect: FC = props => { + +const TransactionTagSelect: FC = ({ isIncome, ...props }) => { let { objects: tags } = useAllTransactionTagsList(); - tags = tags.filter(tag => tag.isIncome === props.isIncome); + tags = tags.filter(tag => tag.isIncome === isIncome); return ( = Omit< @@ -41,6 +43,8 @@ type FiltersProps = { statusSelectProps?: Omit, "data">; + cardTagSelectProps?: Omit, "data">; + managerSelectProps?: SelectProps; onManagerClear?: () => void; @@ -78,6 +82,21 @@ export const Filters = (props: FiltersProps) => { ); }; + useEffect(() => { + if (props.boardSelectProps?.onClear) { + props.boardSelectProps.onClear(); + } + if (props.cardTagSelectProps && !props.projectSelectProps?.value) { + props.cardTagSelectProps.onChange(null); + } + }, [props.projectSelectProps?.value]); + + useEffect(() => { + if (props.statusSelectProps?.onClear) { + props.statusSelectProps.onClear(); + } + }, [props.boardSelectProps?.value]); + return ( @@ -104,6 +123,7 @@ export const Filters = (props: FiltersProps) => { project={props.projectSelectProps?.value ?? null} {...props.boardSelectProps} clearable + disabled={!props.projectSelectProps?.value} /> } {props.statusSelectProps && @@ -111,6 +131,16 @@ export const Filters = (props: FiltersProps) => { board={props.boardSelectProps?.value ?? null} {...props.statusSelectProps} clearable + disabled={!props.boardSelectProps?.value} + /> + } + {props.cardTagSelectProps && + } {props.clientSelectProps && diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx index a4c9a5b..56b1b97 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTable/hooks/columns.tsx @@ -19,6 +19,7 @@ export const useProfitTableColumns = ({ groupTableBy, statuses }: Props) => { [GroupStatisticsTable.BY_MARKETPLACES]: "Маркетплейс", [GroupStatisticsTable.BY_WAREHOUSES]: "Склад отгрузки", [GroupStatisticsTable.BY_MANAGERS]: "Менеджер", + [GroupStatisticsTable.BY_TAGS]: "Тег", }; const getConditionalColumns = (): MRT_ColumnDef[] => { diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx index 1f4a49a..ab44aa6 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx @@ -11,6 +11,7 @@ export enum GroupStatisticsTable { BY_WAREHOUSES, BY_MARKETPLACES, BY_MANAGERS, + BY_TAGS, } type ControlProps = Omit; @@ -22,7 +23,7 @@ type OtherProps = { type Props = ControlProps & OtherProps; -export const ProfitTableSegmentedControl: FC = props => { +export const ProfitTableSegmentedControl: FC = ({ selectedBoard, selectedProject, ...props}) => { const data: (string | SegmentedControlItem)[] = [ { label: "По датам", @@ -39,12 +40,17 @@ export const ProfitTableSegmentedControl: FC = props => { { label: "По доскам", value: GroupStatisticsTable.BY_BOARDS.toString(), - disabled: !props.selectedProject, + disabled: !selectedProject, }, { label: "По статусам", value: GroupStatisticsTable.BY_STATUSES.toString(), - disabled: !props.selectedBoard, + disabled: !selectedBoard, + }, + { + label: "По тегам", + value: GroupStatisticsTable.BY_TAGS.toString(), + disabled: !selectedProject, }, { label: "По складам отгрузки", @@ -67,15 +73,15 @@ export const ProfitTableSegmentedControl: FC = props => { useEffect(() => { if (props.value === GroupStatisticsTable.BY_STATUSES.toString()) { - if (!props.selectedProject) { + if (!selectedProject) { setGrouping(GroupStatisticsTable.BY_PROJECTS); - } else if (!props.selectedBoard) { + } else if (!selectedBoard) { setGrouping(GroupStatisticsTable.BY_BOARDS); } - } else if (props.value === GroupStatisticsTable.BY_BOARDS.toString() && !props.selectedProject) { + } else if (props.value === GroupStatisticsTable.BY_BOARDS.toString() && !selectedProject) { setGrouping(GroupStatisticsTable.BY_PROJECTS); } - }, [props.selectedBoard, props.selectedProject]); + }, [selectedBoard, selectedProject]); return ( { project: null, board: null, status: null, + cardTag: null, manager: null, expenseTag: null, incomeTag: null, - isCompletedOnly: true, + isCompletedOnly: false, }, }); const [isChartLoading, setIsChartLoading] = useState(false); @@ -53,6 +54,7 @@ const useProfitTabContextState = () => { projectId: form.values.project?.id ?? -1, boardId: form.values.board?.id ?? -1, cardStatusId: form.values.status?.id ?? -1, + cardTagId: form.values.cardTag?.id ?? -1, managerId: form.values.manager?.id ?? -1, expenseTagId: form.values.expenseTag?.id ?? -1, incomeTagId: form.values.incomeTag?.id ?? -1, diff --git a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx index 130123f..3a11812 100644 --- a/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx +++ b/src/pages/StatisticsPage/tabs/ProfitTab/modals/ProfitFiltersModal.tsx @@ -43,6 +43,7 @@ export const ProfitFiltersModal = ({ form }: Props) => { projectSelectProps={form.getInputProps("project")} boardSelectProps={form.getInputProps("board")} statusSelectProps={form.getInputProps("status")} + cardTagSelectProps={form.getInputProps("cardTag")} managerSelectProps={form.getInputProps("manager")} onManagerClear={() => form.setFieldValue("manager", null)} isCompletedOnlyCheckboxProps={form.getInputProps("isCompletedOnly", { type: "checkbox" })} diff --git a/src/pages/StatisticsPage/types/FormFilters.ts b/src/pages/StatisticsPage/types/FormFilters.ts index e35c666..9f1688a 100644 --- a/src/pages/StatisticsPage/types/FormFilters.ts +++ b/src/pages/StatisticsPage/types/FormFilters.ts @@ -2,7 +2,7 @@ import { GroupStatisticsTable, } from "../tabs/ProfitTab/components/ProfitTableSegmentedControl/ProfitTableSegmentedControl.tsx"; import { - BaseMarketplaceSchema, BoardSchema, + BaseMarketplaceSchema, BoardSchema, CardTagSchema, ClientSchema, ProjectSchema, StatusSchema, @@ -18,6 +18,7 @@ export interface FormFilters { project: ProjectSchema | null; board: BoardSchema | null; status: StatusSchema | null; + cardTag: CardTagSchema | null; manager: UserSchema | null; isCompletedOnly: boolean; expenseTag: TransactionTagSchema | null;