feat: modules dependencies

This commit is contained in:
2025-03-15 09:38:12 +04:00
parent 11eebdd3ad
commit 3bbdacdd68
10 changed files with 47 additions and 34 deletions

View File

@@ -8,5 +8,6 @@ export type ModuleSchema = {
label: string; label: string;
iconName?: (string | null); iconName?: (string | null);
isDeleted: boolean; isDeleted: boolean;
dependsOn?: Array<ModuleSchema>;
}; };

View File

@@ -6,8 +6,9 @@ import { IconGripHorizontal } from "@tabler/icons-react";
import { useDebouncedValue } from "@mantine/hooks"; import { useDebouncedValue } from "@mantine/hooks";
import { notifications } from "../../../../shared/lib/notifications.ts"; import { notifications } from "../../../../shared/lib/notifications.ts";
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../modules/utils/isModuleInProject.ts";
import CardTags from "../CardTags/CardTags.tsx"; import CardTags from "../CardTags/CardTags.tsx";
import { ModuleNames } from "../../../../modules/modules.tsx";
type Props = { type Props = {
cards: CardSummary[]; cards: CardSummary[];
@@ -19,7 +20,7 @@ export const CardGroupView: FC<Props> = ({ cards, group }) => {
const [name, setName] = useState<string>(group.name || ""); const [name, setName] = useState<string>(group.name || "");
const [debouncedName] = useDebouncedValue(name, 200); const [debouncedName] = useDebouncedValue(name, 200);
const { selectedProject } = useProjectsContext(); const { selectedProject } = useProjectsContext();
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isServicesAndProductsIncluded = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedProject);
const totalPrice = useMemo(() => cards.reduce((acc, card) => acc + card.totalPrice, 0), [cards]); const totalPrice = useMemo(() => cards.reduce((acc, card) => acc + card.totalPrice, 0), [cards]);
const totalProducts = useMemo(() => cards.reduce((acc, card) => acc + card.totalProducts, 0), [cards]); const totalProducts = useMemo(() => cards.reduce((acc, card) => acc + card.totalProducts, 0), [cards]);

View File

@@ -9,10 +9,11 @@ import { faCheck } from "@fortawesome/free-solid-svg-icons";
import { IconCheck, IconLayoutGridRemove, IconTrash } from "@tabler/icons-react"; import { IconCheck, IconLayoutGridRemove, IconTrash } from "@tabler/icons-react";
import { useContextMenu } from "mantine-contextmenu"; import { useContextMenu } from "mantine-contextmenu";
import useCardSummaryState from "./useCardSummaryState.tsx"; import useCardSummaryState from "./useCardSummaryState.tsx";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../modules/utils/isModuleInProject.ts";
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
import CardTags from "../CardTags/CardTags.tsx"; import CardTags from "../CardTags/CardTags.tsx";
import CardAttributesInSummaryItem from "../CardAttributesInSummaryItem/CardAttributesInSummaryItem.tsx"; import CardAttributesInSummaryItem from "../CardAttributesInSummaryItem/CardAttributesInSummaryItem.tsx";
import { ModuleNames } from "../../../../modules/modules.tsx";
type Props = { type Props = {
cardSummary: CardSummary; cardSummary: CardSummary;
@@ -25,8 +26,8 @@ const CardSummaryItem: FC<Props> = ({ cardSummary, color }) => {
const { setSelectedCard } = useCardPageContext(); const { setSelectedCard } = useCardPageContext();
const { onDelete, onComplete, onDeleteFromGroup } = useCardSummaryState(); const { onDelete, onComplete, onDeleteFromGroup } = useCardSummaryState();
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isServicesAndProductsIncluded = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedProject);
const isClientIncluded = isModuleInProject(Modules.CLIENTS, selectedProject); const isClientIncluded = isModuleInProject(ModuleNames.CLIENTS, selectedProject);
const onCardSummaryClick = () => { const onCardSummaryClick = () => {
CardService.getCardById({ cardId: cardSummary.id }).then(card => { CardService.getCardById({ cardId: cardSummary.id }).then(card => {

View File

@@ -9,8 +9,9 @@ import { groupBy, has, uniq } from "lodash";
import { CardGroupView } from "../CardGroupView/CardGroupView.tsx"; import { CardGroupView } from "../CardGroupView/CardGroupView.tsx";
import CreateDealsFromFileButton from "../CreateCardsFromFileButton/CreateDealsFromFileButton.tsx"; import CreateDealsFromFileButton from "../CreateCardsFromFileButton/CreateDealsFromFileButton.tsx";
import DragState from "../../../../pages/CardsPage/enums/DragState.ts"; import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../modules/utils/isModuleInProject.ts";
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
import { ModuleNames } from "../../../../modules/modules.tsx";
type Props = { type Props = {
status: StatusSchema; status: StatusSchema;
@@ -28,7 +29,7 @@ export const CardsDndColumn: FC<Props> = ({
withCreateButton = false, withCreateButton = false,
}) => { }) => {
const { selectedProject } = useProjectsContext(); const { selectedProject } = useProjectsContext();
const isCreatingDealFromFileEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isCreatingDealFromFileEnabled = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedProject);
const isDropDisabled = dragState !== DragState.DRAG_CARD; const isDropDisabled = dragState !== DragState.DRAG_CARD;
const droppableId = status.id.toString(); const droppableId = status.id.toString();

View File

@@ -7,8 +7,9 @@ import { CardService, StatusSchema } from "../../../../client";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { dateWithoutTimezone } from "../../../../shared/lib/date.ts"; import { dateWithoutTimezone } from "../../../../shared/lib/date.ts";
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx"; import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../modules/utils/isModuleInProject.ts";
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
import { ModuleNames } from "../../../../modules/modules.tsx";
type Props = { type Props = {
status: StatusSchema; status: StatusSchema;
@@ -21,7 +22,7 @@ const CreateCardButton = ({ status }: Props) => {
const { prefillCard, setPrefillCard } = usePrefillCardContext(); const { prefillCard, setPrefillCard } = usePrefillCardContext();
const { selectedProject } = useProjectsContext(); const { selectedProject } = useProjectsContext();
const isPrefillingDealEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isPrefillingDealEnabled = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedProject);
return ( return (
<div <div

View File

@@ -9,8 +9,9 @@ import ShippingWarehouseAutocomplete
from "../../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx"; from "../../../Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
import BaseMarketplaceSelect from "../../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx"; import BaseMarketplaceSelect from "../../../Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx"; import { usePrefillCardContext } from "../../../../pages/CardsPage/contexts/PrefillCardContext.tsx";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../modules/utils/isModuleInProject.ts";
import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx"; import { useProjectsContext } from "../../../../contexts/ProjectsContext.tsx";
import { ModuleNames } from "../../../../modules/modules.tsx";
type Props = { type Props = {
onSubmit: (quickDeal: QuickCard) => void; onSubmit: (quickDeal: QuickCard) => void;
@@ -19,11 +20,11 @@ type Props = {
const CreateCardForm: FC<Props> = ({ onSubmit, onCancel }) => { const CreateCardForm: FC<Props> = ({ onSubmit, onCancel }) => {
const { selectedProject } = useProjectsContext(); const { selectedProject } = useProjectsContext();
const isPrefillingEnabled = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isPrefillingEnabled = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedProject);
const { prefillOnOpen, prefillCard } = usePrefillCardContext(); const { prefillOnOpen, prefillCard } = usePrefillCardContext();
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedProject); const isServicesAndProductsIncluded = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedProject);
const isClientsIncluded = isModuleInProject(Modules.CLIENTS, selectedProject); const isClientsIncluded = isModuleInProject(ModuleNames.CLIENTS, selectedProject);
const form = useForm<QuickCard>({ const form = useForm<QuickCard>({
initialValues: { initialValues: {

View File

@@ -9,9 +9,10 @@ import DragState from "../../../../pages/CardsPage/enums/DragState.ts";
import { useContextMenu } from "mantine-contextmenu"; import { useContextMenu } from "mantine-contextmenu";
import { IconEdit, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconTrash } from "@tabler/icons-react";
import useStatus from "./hooks/useStatus.tsx"; import useStatus from "./hooks/useStatus.tsx";
import isModuleInProject, { Modules } from "../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../modules/utils/isModuleInProject.ts";
import { useEqualHeightsContext } from "./contexts/EqualHeightContext.tsx"; import { useEqualHeightsContext } from "./contexts/EqualHeightContext.tsx";
import { useBoardsContext } from "../../../../contexts/BoardsContext.tsx"; import { useBoardsContext } from "../../../../contexts/BoardsContext.tsx";
import { ModuleNames } from "../../../../modules/modules.tsx";
type Props = { type Props = {
@@ -27,7 +28,7 @@ const Status = ({ summaries, status, dragState, index }: Props) => {
} = useBoardsContext(); } = useBoardsContext();
const isDropDisabled = dragState !== DragState.DRAG_STATUS; const isDropDisabled = dragState !== DragState.DRAG_STATUS;
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, selectedBoard?.project); const isServicesAndProductsIncluded = isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, selectedBoard?.project);
const { const {
divRefs, divRefs,

View File

@@ -1,21 +1,7 @@
import { ProjectSchema } from "../../client"; import { ProjectSchema } from "../../client";
import { ModuleNames } from "../modules.tsx";
export enum Modules { const isModuleInProject = (module: ModuleNames, project?: ProjectSchema | null) => {
SERVICES_AND_PRODUCTS = "servicesAndProducts",
SHIPMENT = "shipment",
EMPLOYEES = "employees",
CLIENTS = "clients",
MANAGERS = "managers",
}
const isModuleInProject = (module: Modules, project?: ProjectSchema | null) => {
// if servicesAndProducts included, then clients also included
if (module === Modules.CLIENTS) {
if (project?.modules.findIndex(m => m.key === Modules.SERVICES_AND_PRODUCTS.toString()) !== -1) {
return true;
}
}
return project?.modules.findIndex(m => m.key === module.toString()) !== -1; return project?.modules.findIndex(m => m.key === module.toString()) !== -1;
}; };

View File

@@ -2,7 +2,8 @@ import { useForm } from "@mantine/form";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { BaseMarketplaceSchema } from "../../../../../client"; import { BaseMarketplaceSchema } from "../../../../../client";
import { useCardSummariesFull } from "../../../hooks/useCardSummaries.tsx"; import { useCardSummariesFull } from "../../../hooks/useCardSummaries.tsx";
import isModuleInProject, { Modules } from "../../../../../modules/utils/isModuleInProject.ts"; import isModuleInProject from "../../../../../modules/utils/isModuleInProject.ts";
import { ModuleNames } from "../../../../../modules/modules.tsx";
type State = { type State = {
idOrName: string | null; idOrName: string | null;
@@ -22,7 +23,7 @@ const usePrefillCard = () => {
const applyFilters = () => { const applyFilters = () => {
let result = objects; let result = objects;
result = result.filter(obj => isModuleInProject(Modules.SERVICES_AND_PRODUCTS, obj.board.project)); result = result.filter(obj => isModuleInProject(ModuleNames.SERVICES_AND_PRODUCTS, obj.board.project));
if (form.values.idOrName) { if (form.values.idOrName) {
if (isNaN(parseInt(form.values.idOrName))) { if (isNaN(parseInt(form.values.idOrName))) {

View File

@@ -1,7 +1,8 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { MRT_ColumnDef } from "mantine-react-table"; import { MRT_ColumnDef } from "mantine-react-table";
import { ModuleSchema } from "../../../../../../../client"; import { ModuleSchema } from "../../../../../../../client";
import { Center, Checkbox } from "@mantine/core"; import { Center, Checkbox, em, Group, Tooltip } from "@mantine/core";
import { IconInfoCircle } from "@tabler/icons-react";
type Props = { type Props = {
@@ -32,7 +33,25 @@ const useModulesTableColumns = ({ selectedModules }: Props) => {
{ {
header: "Название", header: "Название",
accessorKey: "label", accessorKey: "label",
size: 200,
},
{
header: "",
Header: (
<Group gap={"sm"}>
<>Зависит от модулей</>
<Tooltip label={"Зависимости автоматически подключатся при сохранении"}>
<IconInfoCircle size={em(20)} />
</Tooltip>
</Group>
),
accessorKey: "dependsOn",
size: 10000, size: 10000,
Cell: ({ row }) => (
<>
{row.original.dependsOn?.map(module => module.label).join(", ")}
</>
),
}, },
], ],
[selectedModules], [selectedModules],