From 98f596bba81fb5ea43e07824e6750388a6e6cbfc Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 10 Oct 2024 10:26:36 +0200 Subject: [PATCH 01/23] Display LifeCyclePolicies in UI (read-only) Adds a new tab to recordings called "LifeCycle Policies". Much like other tabs this tab displays information on its subject in a table format. Unlike i.e. the events tab, the LifeCycle Policies cannot be changed in any way, just be viewed. Editing is supposed to be added at a later date. Depends on PR https://github.com/opencast/opencast/pull/6139 being merged to make any sense. Similarly, if you would like to test this, your admin interface should point to an Opencast with the PR installed. --- src/App.tsx | 3 + src/components/events/Events.tsx | 19 +++ src/components/events/LifeCyclePolicies.tsx | 151 ++++++++++++++++++ src/components/events/Series.tsx | 21 ++- .../partials/LifeCyclePolicyActionCell.tsx | 62 +++++++ .../partials/LifeCyclePolicyIsActiveCell.tsx | 22 +++ .../LifeCyclePolicyAccessTab.tsx | 50 ++++++ .../LifeCyclePolicyGeneralTab.tsx | 120 ++++++++++++++ .../modals/LifeCyclePolicyDetails.tsx | 54 +++++++ src/components/shared/MainNav.tsx | 25 ++- src/components/shared/modals/DetailsModal.tsx | 48 ++++++ .../lifeCyclePoliciesTableConfig.ts | 30 ++++ .../tableConfigs/lifeCyclePoliciesTableMap.ts | 11 ++ .../adminui/languages/lang-en_US.json | 42 +++++ src/selectors/lifeCycleDetailsSelectors.ts | 7 + src/selectors/lifeCycleSelectors.ts | 7 + src/slices/lifeCycleDetailsSlice.ts | 136 ++++++++++++++++ src/slices/lifeCycleSlice.ts | 112 +++++++++++++ src/slices/tableSlice.ts | 3 +- src/store.ts | 5 + src/thunks/tableThunks.ts | 51 ++++++ 21 files changed, 975 insertions(+), 4 deletions(-) create mode 100644 src/components/events/LifeCyclePolicies.tsx create mode 100644 src/components/events/partials/LifeCyclePolicyActionCell.tsx create mode 100644 src/components/events/partials/LifeCyclePolicyIsActiveCell.tsx create mode 100644 src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx create mode 100644 src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx create mode 100644 src/components/events/partials/modals/LifeCyclePolicyDetails.tsx create mode 100644 src/components/shared/modals/DetailsModal.tsx create mode 100644 src/configs/tableConfigs/lifeCyclePoliciesTableConfig.ts create mode 100644 src/configs/tableConfigs/lifeCyclePoliciesTableMap.ts create mode 100644 src/selectors/lifeCycleDetailsSelectors.ts create mode 100644 src/selectors/lifeCycleSelectors.ts create mode 100644 src/slices/lifeCycleDetailsSlice.ts create mode 100644 src/slices/lifeCycleSlice.ts diff --git a/src/App.tsx b/src/App.tsx index 26742260bf..bf012cc542 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import Acls from "./components/users/Acls"; import About from "./components/About"; import { useAppDispatch } from "./store"; import { fetchOcVersion, fetchUserInfo } from "./slices/userInfoSlice"; +import LifeCyclePolicies from "./components/events/LifeCyclePolicies"; function App() { const dispatch = useAppDispatch(); @@ -41,6 +42,8 @@ function App() { } /> + } /> + } /> } /> diff --git a/src/components/events/Events.tsx b/src/components/events/Events.tsx index 0e1337a4f6..f3e6c73fc8 100644 --- a/src/components/events/Events.tsx +++ b/src/components/events/Events.tsx @@ -15,6 +15,7 @@ import EditMetadataEventsModal from "./partials/modals/EditMetadataEventsModal"; import { eventsTemplateMap } from "../../configs/tableConfigs/eventsTableMap"; import { loadEventsIntoTable, + loadLifeCyclePoliciesIntoTable, loadSeriesIntoTable, } from "../../thunks/tableThunks"; import { fetchFilters, fetchStats, editTextFilter } from "../../slices/tableFilterSlice"; @@ -43,6 +44,7 @@ import { import { fetchSeries } from "../../slices/seriesSlice"; import EventDetailsModal from "./partials/modals/EventDetailsModal"; import { showModal } from "../../selectors/eventDetailsSelectors"; +import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -99,6 +101,14 @@ const Events = () => { dispatch(loadSeriesIntoTable()); }; + const loadLifeCyclePolicies = async () => { + // Fetching policies from server + await dispatch(fetchLifeCyclePolicies()); + + // Load policies into table + dispatch(loadLifeCyclePoliciesIntoTable()); + }; + useEffect(() => { if ("events" !== currentFilterType) { dispatch(fetchFilters("events")) @@ -230,6 +240,15 @@ const Events = () => { {t("EVENTS.EVENTS.NAVIGATION.SERIES")} )} + {hasAccess("ROLE_UI_LIFECYCLEPOLICIES_VIEW", user) && ( + loadLifeCyclePolicies()} + > + {t("LIFECYCLE.NAVIGATION.POLICIES")} + + )} {/* Include status bar component*/} diff --git a/src/components/events/LifeCyclePolicies.tsx b/src/components/events/LifeCyclePolicies.tsx new file mode 100644 index 0000000000..19f5c46755 --- /dev/null +++ b/src/components/events/LifeCyclePolicies.tsx @@ -0,0 +1,151 @@ +import React, { useEffect, useState } from "react"; +import MainNav from "../shared/MainNav"; +import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; +import cn from "classnames"; +import TableFilters from "../shared/TableFilters"; +import Table from "../shared/Table"; +import Notifications from "../shared/Notifications"; +import { loadEventsIntoTable, loadLifeCyclePoliciesIntoTable, loadSeriesIntoTable } from "../../thunks/tableThunks"; +import { fetchFilters, editTextFilter, fetchStats } from "../../slices/tableFilterSlice"; +import Header from "../Header"; +import NavBar from "../NavBar"; +import MainView from "../MainView"; +import Footer from "../Footer"; +import { getUserInformation } from "../../selectors/userInfoSelectors"; +import { hasAccess } from "../../utils/utils"; +import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; +import { useAppDispatch, useAppSelector } from "../../store"; +import { AsyncThunk } from "@reduxjs/toolkit"; +import { AsyncThunkConfig } from "@reduxjs/toolkit/dist/createAsyncThunk"; +import { getTotalLifeCyclePolicies } from "../../selectors/lifeCycleSelectors"; +import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice"; +import { lifeCyclePoliciesTemplateMap } from "../../configs/tableConfigs/lifeCyclePoliciesTableMap"; +import { fetchEvents } from "../../slices/eventSlice"; +import { setOffset } from "../../slices/tableSlice"; +import { fetchSeries } from "../../slices/seriesSlice"; + +/** + * This component renders the table view of policies + */ +const LifeCyclePolicies = () => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const [displayNavigation, setNavigation] = useState(false); + + const user = useAppSelector(state => getUserInformation(state)); + const policiesTotal = useAppSelector(state => getTotalLifeCyclePolicies(state)); + const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); + + const loadEvents = async () => { + // Fetching stats from server + dispatch(fetchStats()); + + // Fetching events from server + await dispatch(fetchEvents()); + + // Load events into table + dispatch(loadEventsIntoTable()); + }; + + const loadSeries = () => { + // Reset the current page to first page + dispatch(setOffset(0)); + + //fetching series from server + dispatch(fetchSeries()); + + //load series into table + dispatch(loadSeriesIntoTable()); + }; + + const loadLifeCyclePolicies = async () => { + // Fetching policies from server + await dispatch(fetchLifeCyclePolicies()); + + // Load policies into table + dispatch(loadLifeCyclePoliciesIntoTable()); + }; + + useEffect(() => { + if ("lifeCyclePolicies" !== currentFilterType) { + dispatch(fetchFilters("lifeCyclePolicies")); + } + + // Reset text filter + dispatch(editTextFilter("")); + + // Load policies on mount + loadLifeCyclePolicies().then((r) => console.info(r)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const toggleNavigation = () => { + setNavigation(!displayNavigation); + }; + + return ( + <> +
+ + {/* Include Burger-button menu*/} + + + + + + + {/* Include notifications component */} + + +
+ {/* Include filters component */} + {/* LifeCycle policies are not indexed, can't search or filter them */} + {/* But if we don't include this component, the policies won't load on page load, because the first + fetch request we send to the backend contains invalid params >.> */} + } + loadResourceIntoTable={loadLifeCyclePoliciesIntoTable} + resource={"lifeCyclePolicies"} + /> + +

{t("LIFECYCLE.POLICIES.TABLE.CAPTION")}

+

{t("TABLE_SUMMARY", { numberOfRows: policiesTotal })}

+
+ {/* Include table component */} + + +
+ + ); +}; + +export default LifeCyclePolicies; diff --git a/src/components/events/Series.tsx b/src/components/events/Series.tsx index 4dcce7f070..537ba99094 100644 --- a/src/components/events/Series.tsx +++ b/src/components/events/Series.tsx @@ -11,6 +11,7 @@ import DeleteSeriesModal from "./partials/modals/DeleteSeriesModal"; import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableMap"; import { loadEventsIntoTable, + loadLifeCyclePoliciesIntoTable, loadSeriesIntoTable, } from "../../thunks/tableThunks"; import { fetchFilters, fetchStats, editTextFilter } from "../../slices/tableFilterSlice"; @@ -34,6 +35,7 @@ import { showActionsSeries, } from "../../slices/seriesSlice"; import { fetchSeriesDetailsTobiraNew } from "../../slices/seriesSlice"; +import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -79,6 +81,14 @@ const Series = () => { dispatch(loadSeriesIntoTable()); }; + const loadLifeCyclePolicies = async () => { + // Fetching policies from server + await dispatch(fetchLifeCyclePolicies()); + + // Load policies into table + dispatch(loadLifeCyclePoliciesIntoTable()); + }; + useEffect(() => { if ("series" !== currentFilterType) { dispatch(fetchFilters("series")) @@ -186,8 +196,17 @@ const Series = () => { {t("EVENTS.EVENTS.NAVIGATION.SERIES")} )} + {hasAccess("ROLE_UI_LIFECYCLEPOLICIES_VIEW", user) && ( + loadLifeCyclePolicies()} + > + {t("LIFECYCLE.NAVIGATION.POLICIES")} + + )} - +
{hasAccess("ROLE_UI_SERIES_CREATE", user) && (
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TITLE")}{policy.title}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ISACTIVE")}{ + + }
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ISCREATEDFROMCONFIG")}{ + + }
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETTYPE")}{t(policy.targetType)}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS")} + + + {Object.entries(policy.targetFilters).map(([key, value], index) => { + return( + + + + + ) + })} + +
{key}{value.value + ", " + value.type + ", " + value.must}
+
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TIMING")}{t(policy.timing)}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONDATE")}{t("dateFormats.dateTime.full", { dateTime: renderValidDate(policy.actionDate) })}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.CRONTRIGGER")}{policy.cronTrigger}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTION")}{t(policy.action)}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONPARAMETERS")} + + + {Object.entries(policy.actionParameters).map(([key, value], index) => { + return( + + + + + ) + })} + +
{key}{value}
+
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ID")}{policy.id}
+ + + + + + ); +}; + +export default LifeCyclePolicyGeneralTab; diff --git a/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx b/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx new file mode 100644 index 0000000000..e818dd57c2 --- /dev/null +++ b/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx @@ -0,0 +1,54 @@ +import React, { useState } from "react"; +import ModalNavigation from "../../../shared/modals/ModalNavigation"; +import { getLifeCyclePolicyDetails } from "../../../../selectors/lifeCycleDetailsSelectors"; +import LifeCyclePolicyGeneralTab from "../ModalTabsAndPages/LifeCyclePolicyGeneralTab"; +import LifeCyclePolicyDetailsAccessTab from "../ModalTabsAndPages/LifeCyclePolicyAccessTab"; +import { useAppSelector } from "../../../../store"; + +/** + * This component manages the tabs of the series details modal + */ +const LifeCyclePolicyDetails = () => { + const [page, setPage] = useState(0); + + const policy = useAppSelector(state => getLifeCyclePolicyDetails(state)); + + // information about tabs + const tabs = [ + { + tabTranslation: "LIFECYCLE.POLICIES.DETAILS.TAB.GENERAL", + accessRole: "ROLE_UI_LIFECYCLEPOLICIES_DETAILS_GENERAL_VIEW", + name: "general", + }, + { + tabTranslation: "LIFECYCLE.POLICIES.DETAILS.TAB.ACCESSPOLICIES", + accessRole: "ROLE_UI_LIFECYCLEPOLICIES_DETAILS_ACCESSPOLICIES_VIEW", + name: "WARNING: None of the changes you make here can be saved!", + }, + ]; + + const openTab = (tabNr: number) => { + setPage(tabNr); + }; + + return ( + <> + {/* Navigation */} + + +
+ {page === 0 && } + {page === 1 && + false} + /> + } +
+ + ); +}; + +export default LifeCyclePolicyDetails; diff --git a/src/components/shared/MainNav.tsx b/src/components/shared/MainNav.tsx index e0d40ab02a..5def5026a3 100644 --- a/src/components/shared/MainNav.tsx +++ b/src/components/shared/MainNav.tsx @@ -6,6 +6,7 @@ import { loadEventsIntoTable, loadGroupsIntoTable, loadJobsIntoTable, + loadLifeCyclePoliciesIntoTable, loadRecordingsIntoTable, loadSeriesIntoTable, loadServersIntoTable, @@ -34,6 +35,7 @@ import { fetchSeries } from "../../slices/seriesSlice"; import { fetchJobs } from "../../slices/jobSlice"; import { fetchEvents } from "../../slices/eventSlice"; import { Tooltip } from "./Tooltip"; +import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice"; /** * This component renders the main navigation that opens when the burger button is clicked @@ -84,6 +86,19 @@ const MainNav = ({ dispatch(loadSeriesIntoTable()); }; + const loadLifeCyclePolicies = () => { + dispatch(fetchFilters("lifeCylePolicies")); + + // Reset the current page to first page + dispatch(setOffset(0)); + + // Fetching lifeCycle policies from server + dispatch(fetchLifeCyclePolicies()); + + // Load lifeCycle policies into table + dispatch(loadLifeCyclePoliciesIntoTable()); + }; + const loadRecordings = () => { dispatch(fetchFilters("recordings")); @@ -228,13 +243,19 @@ const MainNav = ({ - ) : ( - hasAccess("ROLE_UI_SERIES_VIEW", user) && ( + ) : hasAccess("ROLE_UI_SERIES_VIEW", user) ? ( loadSeries()}> + ) : ( + hasAccess("ROLE_UI_LIFECYCLEPOLICIES_VIEW", user) && ( + loadLifeCyclePolicies()}> + + + + ) ))} {hasAccess("ROLE_UI_NAV_CAPTURE_VIEW", user) && diff --git a/src/components/shared/modals/DetailsModal.tsx b/src/components/shared/modals/DetailsModal.tsx new file mode 100644 index 0000000000..5ebd58ec5a --- /dev/null +++ b/src/components/shared/modals/DetailsModal.tsx @@ -0,0 +1,48 @@ +import React, { PropsWithChildren } from "react"; +import { useTranslation } from "react-i18next"; +import { useHotkeys } from "react-hotkeys-hook"; +import { availableHotkeys } from "../../../configs/hotkeysConfig"; + +/** + * This component renders the modal for displaying series details + */ +const DetailsModal = ({ + handleClose, + prefix, + title, + children +}: PropsWithChildren<{ + handleClose: () => void + prefix: string + title: string +}>) => { + const { t } = useTranslation(); + + const close = () => { + handleClose(); + }; + + useHotkeys( + availableHotkeys.general.CLOSE_MODAL.sequence, + () => close(), + { description: t(availableHotkeys.general.CLOSE_MODAL.description) ?? undefined }, + [close], + ); + + return ( + <> +
+
+
+
+ {children} +
+ + ); +}; + +export default DetailsModal; diff --git a/src/configs/tableConfigs/lifeCyclePoliciesTableConfig.ts b/src/configs/tableConfigs/lifeCyclePoliciesTableConfig.ts new file mode 100644 index 0000000000..71a47c8a8e --- /dev/null +++ b/src/configs/tableConfigs/lifeCyclePoliciesTableConfig.ts @@ -0,0 +1,30 @@ +import { TableConfig } from "./aclsTableConfig"; + +export const lifeCyclePolicyTableConfig: TableConfig = { + columns: [ + { + name: "title", + label: "LIFECYCLE.POLICIES.TABLE.TITLE", + sortable: true, + }, + { + template: "LifeCyclePolicyIsActiveCell", + name: "isActive", + label: "LIFECYCLE.POLICIES.TABLE.ISACTIVE", + }, + { + name: "timing", + label: "LIFECYCLE.POLICIES.TABLE.TIMING", + sortable: true, + }, + { + template: "LifeCyclePolicyActionCell", + name: "actions", + label: "LIFECYCLE.POLICIES.TABLE.ACTION", + }, + ], + caption: "TABLE.CAPTION", + resource: "lifeCyclePolicies", + category: "events", + multiSelect: false, +}; diff --git a/src/configs/tableConfigs/lifeCyclePoliciesTableMap.ts b/src/configs/tableConfigs/lifeCyclePoliciesTableMap.ts new file mode 100644 index 0000000000..7a460a6363 --- /dev/null +++ b/src/configs/tableConfigs/lifeCyclePoliciesTableMap.ts @@ -0,0 +1,11 @@ +import LifeCyclePolicyActionCell from "../../components/events/partials/LifeCyclePolicyActionCell"; +import LifeCyclePolicyIsActiveCell from "../../components/events/partials/LifeCyclePolicyIsActiveCell"; + +/** + * This map contains the mapping between the template strings above and the corresponding react component. + * This helps to render different templates of cells more dynamically + */ +export const lifeCyclePoliciesTemplateMap = { + LifeCyclePolicyIsActiveCell: LifeCyclePolicyIsActiveCell, + LifeCyclePolicyActionCell: LifeCyclePolicyActionCell, +}; diff --git a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json index 9ca41a06e7..cd16368d29 100644 --- a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json +++ b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json @@ -1249,6 +1249,48 @@ } } }, + "LIFECYCLE": { + "NAVIGATION": { + "POLICIES": "LifeCycle Policies" + }, + "POLICIES": { + "TABLE": { + "ACTION": "Actions", + "CAPTION": "LifeCycle Policies", + "ISACTIVE": "Active", + "TIMING": "Timing", + "TITLE": "Title", + "TOOLTIP": { + "DETAILS": "LifeCycle Policy Details" + } + }, + "DETAILS": { + "HEADER": "LifeCycle Policy Details", + "GENERAL": { + "ACTION": "Action", + "ACTIONDATE": "Action Date", + "ACTIONPARAMETERS": "Action Parameters", + "CAPTION": "LifeCycle Policy Details", + "CRONTRIGGER": "Cron Trigger", + "ID": "Identifier", + "ISACTIVE": "Active", + "ISCREATEDFROMCONFIG": "Created from Config", + "TARGETTYPE": "Target Type", + "TARGETFILTERS": "Target Filters", + "TIMING": "Timing", + "TITLE": "Title" + }, + "ACCESS": { + "LABEL": "Select a template", + "DESCRIPTION": "At least one role with Read and Write permissions is required." + }, + "TAB": { + "GENERAL": "General", + "ACCESSPOLICIES": "Access Policies" + } + } + } + }, "RECORDINGS": { "NAVIGATION": { "LOCATIONS": "Locations" diff --git a/src/selectors/lifeCycleDetailsSelectors.ts b/src/selectors/lifeCycleDetailsSelectors.ts new file mode 100644 index 0000000000..364368819a --- /dev/null +++ b/src/selectors/lifeCycleDetailsSelectors.ts @@ -0,0 +1,7 @@ +import { RootState } from "../store"; + +/** + * This file contains selectors regarding details of a certain lifeCyclePolicy/capture agent + */ +export const getLifeCyclePolicyDetails = (state: RootState) => state.lifeCyclePolicyDetails; +export const getLifeCyclePolicyDetailsAcl = (state: RootState) => state.lifeCyclePolicyDetails.accessControlEntries; diff --git a/src/selectors/lifeCycleSelectors.ts b/src/selectors/lifeCycleSelectors.ts new file mode 100644 index 0000000000..fadb641e3d --- /dev/null +++ b/src/selectors/lifeCycleSelectors.ts @@ -0,0 +1,7 @@ +import { RootState } from "../store"; + +/** + * This file contains selectors regarding acls + */ +export const getLifeCyclePolicies = (state: RootState) => state.lifeCycle.results; +export const getTotalLifeCyclePolicies = (state: RootState) => state.lifeCycle.total; diff --git a/src/slices/lifeCycleDetailsSlice.ts b/src/slices/lifeCycleDetailsSlice.ts new file mode 100644 index 0000000000..2bba3cda56 --- /dev/null +++ b/src/slices/lifeCycleDetailsSlice.ts @@ -0,0 +1,136 @@ +import { PayloadAction, SerializedError, createSlice } from '@reduxjs/toolkit' +import axios from 'axios'; +import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; +import { LifeCyclePolicy } from './lifeCycleSlice'; +import { TransformedAcl } from './aclDetailsSlice'; +import { createPolicy } from '../utils/resourceUtils'; +import { Ace } from './aclSlice'; + +/** + * This file contains redux reducer for actions affecting the state of a lifeCyclePolicy/capture agent + */ +interface LifeCyclePolicyDetailsState extends LifeCyclePolicy { + statusLifeCyclePolicyDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorLifeCyclePolicyDetails: SerializedError | null, +} + +// Initial state of lifeCyclePolicy details in redux store +const initialState: LifeCyclePolicyDetailsState = { + statusLifeCyclePolicyDetails: 'uninitialized', + errorLifeCyclePolicyDetails: null, + actionParameters: {}, + timing: "SPECIFIC_DATE", + action: "START_WORKFLOW", + targetType: "EVENT", + id: "", + title: "", + isActive: false, + isCreatedFromConfig: false, + actionDate: "", + cronTrigger: "", + targetFilters: {}, + accessControlEntries: [] +}; + +// fetch details of certain lifeCyclePolicy from server +export const fetchLifeCyclePolicyDetails = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyDetails', async (id: string) => { + const res = await axios.get(`/api/lifecyclemanagement/policies/${id}`); + const data = res.data; + + data.actionParameters = JSON.parse(data.actionParameters) + data.targetFilters = JSON.parse(data.targetFilters) + + let accessPolicies : { + id: number, + allow: boolean, + role: string, + action: string, + }[] = data.accessControlEntries; + let acls: TransformedAcl[] = []; + + const json = accessPolicies; + let newPolicies: { [key: string]: TransformedAcl } = {}; + let policyRoles: string[] = []; + for (let i = 0; i < json.length; i++) { + const policy: Ace = json[i]; + if (!newPolicies[policy.role]) { + newPolicies[policy.role] = createPolicy(policy.role); + policyRoles.push(policy.role); + } + if (policy.action === "read" || policy.action === "write") { + newPolicies[policy.role][policy.action] = policy.allow; + } else if (policy.allow === true) { //|| policy.allow === "true") { + newPolicies[policy.role].actions.push(policy.action); + } + } + acls = policyRoles.map((role) => newPolicies[role]); + + data.accessControlEntries = acls; + + return data; +}); + +// Dummy function for compatability +export const fetchLifeCyclePolicyDetailsAcls = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyDetailsAcls', async (id: string, {getState}) => { + const state = getState(); + return state.lifeCyclePolicyDetails.accessControlEntries; +}); + +// Dummy function for compatability +export const updateLifeCyclePolicyAccess = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyDetailsAcls', async (params: { + id: string, + policies: { acl: { ace: Ace[] } } +}, {dispatch}) => { + return false; +}); + +const lifeCyclePolicyDetailsSlice = createSlice({ + name: 'lifeCyclePolicyDetails', + initialState, + reducers: {}, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchLifeCyclePolicyDetails.pending, (state) => { + state.statusLifeCyclePolicyDetails = 'loading'; + }) + .addCase(fetchLifeCyclePolicyDetails.fulfilled, (state, action: PayloadAction<{ + actionParameters: LifeCyclePolicyDetailsState["actionParameters"], + timing: LifeCyclePolicyDetailsState["timing"], + action: LifeCyclePolicyDetailsState["action"], + targetType: LifeCyclePolicyDetailsState["targetType"], + id: LifeCyclePolicyDetailsState["id"], + title: LifeCyclePolicyDetailsState["title"], + isActive: LifeCyclePolicyDetailsState["isActive"], + isCreatedFromConfig: LifeCyclePolicyDetailsState["isCreatedFromConfig"], + actionDate: LifeCyclePolicyDetailsState["actionDate"], + cronTrigger: LifeCyclePolicyDetailsState["cronTrigger"], + targetFilters: LifeCyclePolicyDetailsState["targetFilters"], + accessControlEntries: LifeCyclePolicyDetailsState["accessControlEntries"], + }>) => { + state.statusLifeCyclePolicyDetails = 'succeeded'; + const lifeCyclePolicyDetails = action.payload; + state.actionParameters = lifeCyclePolicyDetails.actionParameters; + state.timing = lifeCyclePolicyDetails.timing; + state.action = lifeCyclePolicyDetails.action; + state.targetType = lifeCyclePolicyDetails.targetType; + state.id = lifeCyclePolicyDetails.id; + state.title = lifeCyclePolicyDetails.title; + state.isActive = lifeCyclePolicyDetails.isActive; + state.isCreatedFromConfig = lifeCyclePolicyDetails.isCreatedFromConfig; + state.actionDate = lifeCyclePolicyDetails.actionDate; + state.cronTrigger = lifeCyclePolicyDetails.cronTrigger; + state.targetFilters = lifeCyclePolicyDetails.targetFilters; + state.accessControlEntries = lifeCyclePolicyDetails.accessControlEntries; + }) + .addCase(fetchLifeCyclePolicyDetails.rejected, (state, action) => { + state.statusLifeCyclePolicyDetails = 'failed'; + state.errorLifeCyclePolicyDetails = action.error; + }); + } +}); + +// export const {} = lifeCyclePolicyDetailsSlice.actions; + +// Export the slice reducer as the default export +export default lifeCyclePolicyDetailsSlice.reducer; diff --git a/src/slices/lifeCycleSlice.ts b/src/slices/lifeCycleSlice.ts new file mode 100644 index 0000000000..cfa4e78192 --- /dev/null +++ b/src/slices/lifeCycleSlice.ts @@ -0,0 +1,112 @@ +import { PayloadAction, SerializedError, createSlice } from '@reduxjs/toolkit' +import { TableConfig } from "../configs/tableConfigs/aclsTableConfig"; +import { lifeCyclePolicyTableConfig } from "../configs/tableConfigs/lifeCyclePoliciesTableConfig"; +import axios from 'axios'; +import { getURLParams } from '../utils/resourceUtils'; +import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; +import { TransformedAcl } from './aclDetailsSlice'; + +type LifeCyclePolicyTiming = "SPECIFIC_DATE" | "REPEATING" | "ALWAYS"; +type LifeCyclePolicyAction = "START_WORKFLOW" +type LifeCyclePolicyTargetType = "EVENT" + +export type LifeCyclePolicy = { + actionParameters: { [key: string]: unknown }, // JSON. Variable, depends on action + timing: LifeCyclePolicyTiming, + action: LifeCyclePolicyAction, + targetType: LifeCyclePolicyTargetType, + id: string, + title: string, + isActive: boolean, + isCreatedFromConfig: boolean, + actionDate: string, // Date + cronTrigger: string, + targetFilters: { [key: string]: { + value: string, + type: "SEARCH" | "WILDCARD" | "GREATER_THAN" | "LESS_THAN", + must: boolean + }}, + accessControlEntries: TransformedAcl[] +} + +type LifeCycleState = { + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: LifeCyclePolicy[], + columns: TableConfig["columns"], + total: number, + count: number, + offset: number, + limit: number, +}; + +// Fill columns initially with columns defined in aclsTableConfig +const initialColumns = lifeCyclePolicyTableConfig.columns.map((column) => ({ + ...column, + deactivated: false, +})); + +// Initial state of acls in redux store +const initialState: LifeCycleState = { + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, +}; + +export const fetchLifeCyclePolicies = createAppAsyncThunk('lifeCycle/fetchLifeCyclePolicies', async (_, { getState }) => { + const state = getState(); + let params = getURLParams(state); + const res = await axios.get("/api/lifecyclemanagement/policies", { params: params }); + return res.data; +}); + +const lifeCycleSlice = createSlice({ + name: 'lifeCycle', + initialState, + reducers: { + setLifeCycleColumns(state, action: PayloadAction< + LifeCycleState["columns"] + >) { + state.columns = action.payload; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchLifeCyclePolicies.pending, (state) => { + state.status = 'loading'; + }) + // Pass the generated action creators to `.addCase()` + .addCase(fetchLifeCyclePolicies.fulfilled, (state, action: PayloadAction<{ + total: LifeCycleState["total"], + count: LifeCycleState["count"], + limit: LifeCycleState["limit"], + offset: LifeCycleState["offset"], + results: LifeCycleState["results"], + }>) => { + // Same "mutating" update syntax thanks to Immer + state.status = 'succeeded'; + const policies = action.payload; + state.total = policies.total; + state.count = policies.count; + state.limit = policies.limit; + state.offset = policies.offset; + state.results = policies.results; + }) + .addCase(fetchLifeCyclePolicies.rejected, (state, action) => { + state.status = 'failed'; + state.results = []; + state.error = action.error; + }); + } +}); + +export const { setLifeCycleColumns } = lifeCycleSlice.actions; + +// Export the slice reducer as the default export +export default lifeCycleSlice.reducer; diff --git a/src/slices/tableSlice.ts b/src/slices/tableSlice.ts index ae6455574e..001f95062d 100644 --- a/src/slices/tableSlice.ts +++ b/src/slices/tableSlice.ts @@ -10,6 +10,7 @@ import { AclResult } from './aclSlice'; import { ThemeDetailsType } from './themeSlice'; import { Series } from './seriesSlice'; import { Event } from './eventSlice'; +import { LifeCyclePolicy } from './lifeCycleSlice'; /* Overview of the structure of the data in arrays in state @@ -68,7 +69,7 @@ export function isSeries(row: Row | Event | Series | Recording | Server | Job | } // TODO: Improve row typing. While this somewhat correctly reflects the current state of our code, it is rather annoying to work with. -export type Row = { selected: boolean } & ( Event | Series | Recording | Server | Job | Service | UserResult | Group | AclResult | ThemeDetailsType ) +export type Row = { selected: boolean } & ( Event | Series | Recording | Server | Job | Service | UserResult | Group | AclResult | ThemeDetailsType | LifeCyclePolicy) type TableState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', diff --git a/src/store.ts b/src/store.ts index d6973a7233..1a6175c879 100644 --- a/src/store.ts +++ b/src/store.ts @@ -6,6 +6,7 @@ import tableFilterProfiles from "./slices/tableFilterProfilesSlice"; import events from "./slices/eventSlice"; import table from "./slices/tableSlice"; import series from "./slices/seriesSlice"; +import lifeCycle from "./slices/lifeCycleSlice"; import recordings from "./slices/recordingSlice"; import jobs from "./slices/jobSlice"; import servers from "./slices/serverSlice"; @@ -19,6 +20,7 @@ import notifications from "./slices/notificationSlice"; import workflows from "./slices/workflowSlice"; import eventDetails from "./slices/eventDetailsSlice"; import seriesDetails from "./slices/seriesDetailsSlice"; +import lifeCyclePolicyDetails from "./slices/lifeCycleDetailsSlice"; import userDetails from "./slices/userDetailsSlice"; import recordingDetails from "./slices/recordingDetailsSlice"; import groupDetails from "./slices/groupDetailsSlice"; @@ -38,6 +40,7 @@ import autoMergeLevel2 from "redux-persist/lib/stateReconciler/autoMergeLevel2"; const tableFilterProfilesPersistConfig = { key: "tableFilterProfiles", storage, whitelist: ["profiles"] } const eventsPersistConfig = { key: "events", storage, whitelist: ["columns"] } const seriesPersistConfig = { key: "series", storage, whitelist: ["columns"] } +const lifeCyclePersistConfig = { key: "lifeCycle", storage, whitelist: ["columns"] } const tablePersistConfig = { key: "table", storage, whitelist: ["pagination"] } const recordingsPersistConfig = { key: "recordings", storage, whitelist: ["columns"] } const jobsPersistConfig = { key: "jobs", storage, whitelist: ["columns"] } @@ -54,6 +57,7 @@ const reducers = combineReducers({ tableFilterProfiles: persistReducer(tableFilterProfilesPersistConfig, tableFilterProfiles), events: persistReducer(eventsPersistConfig, events), series: persistReducer(seriesPersistConfig, series), + lifeCycle: persistReducer(lifeCyclePersistConfig, lifeCycle), table: persistReducer(tablePersistConfig, table), recordings: persistReducer(recordingsPersistConfig, recordings), jobs: persistReducer(jobsPersistConfig, jobs), @@ -69,6 +73,7 @@ const reducers = combineReducers({ eventDetails, themeDetails, seriesDetails, + lifeCyclePolicyDetails, recordingDetails, userDetails, groupDetails, diff --git a/src/thunks/tableThunks.ts b/src/thunks/tableThunks.ts index 45b92c27b0..95b7b13417 100644 --- a/src/thunks/tableThunks.ts +++ b/src/thunks/tableThunks.ts @@ -44,6 +44,8 @@ import { fetchRecordings, setRecordingsColumns } from "../slices/recordingSlice" import { setGroupColumns } from "../slices/groupSlice"; import { fetchAcls, setAclColumns } from "../slices/aclSlice"; import { AppDispatch, AppThunk, RootState } from "../store"; +import { lifeCyclePolicyTableConfig } from "../configs/tableConfigs/lifeCyclePoliciesTableConfig"; +import { fetchLifeCyclePolicies, setLifeCycleColumns } from "../slices/lifeCycleSlice"; /** * This file contains methods/thunks used to manage the table in the main view and its state changes @@ -147,6 +149,40 @@ export const loadSeriesIntoTable = (): AppThunk => (dispatch, getState) => { dispatch(loadResourceIntoTable(tableData)); }; +export const loadLifeCyclePoliciesIntoTable = (): AppThunk => (dispatch, getState) => { + const { lifeCycle, table } = getState() as RootState; + const pagination = table.pagination; + const resource = lifeCycle.results; + const total = lifeCycle.total; + + const pages = calculatePages(total / pagination.limit, pagination.offset); + + let tableData = { + resource: "lifeCyclePolicies", + rows: resource.map((obj) => { + return { ...obj, selected: false } + }), + columns: lifeCycle.columns, + multiSelect: table.multiSelect, + pages: pages, + sortBy: table.sortBy, + reverse: table.reverse, + totalItems: total, + }; + + if (table.resource !== "lifeCyclePolicies") { + const multiSelect = lifeCyclePolicyTableConfig.multiSelect; + + tableData = { + ...tableData, + sortBy: "title", + reverse: "ASC", + multiSelect: multiSelect, + }; + } + dispatch(loadResourceIntoTable(tableData)); +} + export const loadRecordingsIntoTable = (): AppThunk => (dispatch, getState) => { const { recordings, table } = getState() as RootState; const pagination = table.pagination; @@ -444,6 +480,11 @@ export const goToPage = (pageNumber: number) => async (dispatch: AppDispatch, ge dispatch(loadSeriesIntoTable()); break; } + case "lifeCyclePolicies": { + await dispatch(fetchLifeCyclePolicies()); + dispatch(loadLifeCyclePoliciesIntoTable()); + break; + } case "recordings": { await dispatch(fetchRecordings()); dispatch(loadRecordingsIntoTable()); @@ -513,6 +554,11 @@ export const updatePages = () => async (dispatch: AppDispatch, getState: () => R dispatch(loadSeriesIntoTable()); break; } + case "lifeCyclePolicies": { + await dispatch(fetchLifeCyclePolicies()); + dispatch(loadLifeCyclePoliciesIntoTable()); + break; + } case "recordings": { await dispatch(fetchRecordings()); dispatch(loadRecordingsIntoTable()); @@ -625,6 +671,11 @@ export const changeColumnSelection = (updatedColumns: TableConfig["columns"]) => dispatch(loadSeriesIntoTable()); break; } + case "lifeCyclePolicies": { + await dispatch(setLifeCycleColumns(updatedColumns)); + dispatch(loadLifeCyclePoliciesIntoTable()); + break; + } case "recordings": { await dispatch(setRecordingsColumns(updatedColumns)); dispatch(loadRecordingsIntoTable()); From 5e5a850e06a021781b2e2c50931cd9d424aae148 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 14 Oct 2024 09:37:07 +0200 Subject: [PATCH 02/23] Fix typescript complaint Type LifeCyclePolicy was missing on a union type. --- src/slices/tableSlice.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slices/tableSlice.ts b/src/slices/tableSlice.ts index 001f95062d..4efd01de3e 100644 --- a/src/slices/tableSlice.ts +++ b/src/slices/tableSlice.ts @@ -60,11 +60,11 @@ export function isRowSelectable(row: Row) { return false; } -export function isEvent(row: Event | Series | Recording | Server | Job | Service | UserResult | Group | AclResult | ThemeDetailsType): row is Event { +export function isEvent(row: Event | Series | Recording | Server | Job | Service | UserResult | Group | AclResult | ThemeDetailsType | LifeCyclePolicy): row is Event { return (row as Event).event_status !== undefined; } -export function isSeries(row: Row | Event | Series | Recording | Server | Job | Service | UserResult | Group | AclResult | ThemeDetailsType): row is Series { +export function isSeries(row: Row | Event | Series | Recording | Server | Job | Service | UserResult | Group | AclResult | ThemeDetailsType | LifeCyclePolicy): row is Series { return (row as Series).organizers !== undefined; } From c2d1c40f1d9006cadbdc4db448645626acb56f31 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 4 Nov 2024 11:40:05 +0100 Subject: [PATCH 03/23] Add Create, Edit, Delete for LifeCycle Policies Instead of only being able to view lifecycle policies, this commit lets you edit them, create new ones and even delete them. Depends on changes to the backend. --- package-lock.json | 2448 ++++++++++++----- package.json | 1 + src/components/events/LifeCyclePolicies.tsx | 39 + .../partials/LifeCyclePolicyActionCell.tsx | 37 +- .../LifeCyclePolicyAccessTab.tsx | 2 + .../LifeCyclePolicyGeneralTab.tsx | 265 +- .../NewLifeCyclePolicyGeneralPage.tsx | 40 + .../modals/LifeCyclePolicyDetails.tsx | 24 +- .../wizards/LifeCyclePolicyGeneralFields.tsx | 475 ++++ .../wizards/NewLifeCyclePolicySummary.tsx | 114 + .../wizards/NewLifeCyclePolicyWizard.tsx | 177 ++ .../partials/wizards/RenderWorkflowConfig.tsx | 24 +- src/components/shared/ConfirmModal.tsx | 2 +- src/components/shared/DropDown.tsx | 2 +- src/components/shared/NewResourceModal.tsx | 10 +- src/components/shared/wizard/RenderField.tsx | 68 +- .../shared/wizard/RenderMultiField.tsx | 2 +- src/configs/modalConfig.ts | 31 + .../adminui/languages/lang-en_US.json | 38 +- src/selectors/lifeCycleDetailsSelectors.ts | 3 + src/slices/lifeCycleDetailsSlice.ts | 96 +- src/slices/lifeCycleSlice.ts | 95 +- src/slices/workflowSlice.ts | 2 +- src/styles/main.scss | 5 + src/utils/componentStyles.ts | 2 + src/utils/dropDownUtils.ts | 13 +- src/utils/lifeCycleUtils.ts | 16 + src/utils/utils.ts | 10 + src/utils/validate.ts | 27 + 29 files changed, 3240 insertions(+), 828 deletions(-) create mode 100644 src/components/events/partials/ModalTabsAndPages/NewLifeCyclePolicyGeneralPage.tsx create mode 100644 src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx create mode 100644 src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx create mode 100644 src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx create mode 100644 src/utils/lifeCycleUtils.ts diff --git a/package-lock.json b/package-lock.json index be31794a2a..e1ac46744a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "react-hotkeys-hook": "^4.5.1", "react-i18next": "^15.1.0", "react-icons": "^5.3.0", + "react-js-cron": "^5.0.1", "react-redux": "^9.1.2", "react-router-dom": "^6.27.0", "react-select": "^5.8.0", @@ -83,6 +84,115 @@ "node": ">=6.0.0" } }, + "node_modules/@ant-design/colors": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.1.0.tgz", + "integrity": "sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg==", + "peer": true, + "dependencies": { + "@ctrl/tinycolor": "^3.6.1" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.22.0.tgz", + "integrity": "sha512-W9XSFeRPR0mAN3OuxfuS/xhENCYKf+8s+QyNNER0FSWoK9OpISTag6CCweg6lq0hASQ/2Vcza0Z8/kGivCP0Ng==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz", + "integrity": "sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ==", + "peer": true, + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/cssinjs/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "peer": true + }, + "node_modules/@ant-design/cssinjs/node_modules/stylis": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz", + "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==", + "peer": true + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.5.1.tgz", + "integrity": "sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w==", + "peer": true, + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "peer": true + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -2252,6 +2362,15 @@ "node": ">=6.9.0" } }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -2270,6 +2389,11 @@ "stylis": "4.2.0" } }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2290,13 +2414,13 @@ } }, "node_modules/@emotion/cache": { - "version": "11.13.1", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", - "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.5.tgz", + "integrity": "sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.0", + "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } @@ -2312,9 +2436,10 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "peer": true }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", @@ -2353,17 +2478,22 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", - "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.1", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, + "node_modules/@emotion/serialize/node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", @@ -2417,9 +2547,9 @@ } }, "node_modules/@emotion/utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", - "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" }, "node_modules/@emotion/weak-memoize": { "version": "0.3.1", @@ -2545,9 +2675,9 @@ } }, "node_modules/@floating-ui/react": { - "version": "0.26.27", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.27.tgz", - "integrity": "sha512-jLP72x0Kr2CgY6eTYi/ra3VA9LOkTo4C+DUTrbFgFOExKy3omYVmwMjNKqxAHdsnyLS96BIDLcO2SlnsNf8KUQ==", + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", @@ -2693,24 +2823,24 @@ "peer": true }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.6.tgz", - "integrity": "sha512-nz1SlR9TdBYYPz4qKoNasMPRiGb4PaIHFkzLzhju0YVYS5QSuFF2+n7CsiHMIDcHv3piPu/xDWI53ruhOqvZwQ==", + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.10.tgz", + "integrity": "sha512-LY5wdiLCBDY7u+Od8UmFINZFGN/5ZU90fhAslf/ZtfP+5RhuY45f679pqYIxe0y54l6Gkv9PFOc8Cs10LDTBYg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/material": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.6.tgz", - "integrity": "sha512-1yvejiQ/601l5AK3uIdUlAVElyCxoqKnl7QA+2oFB/2qYPWfRwDgavW/MoywS5Y2gZEslcJKhe0s2F3IthgFgw==", + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.10.tgz", + "integrity": "sha512-txnwYObY4N9ugv5T2n5h1KcbISegZ6l65w1/7tpSU5OB6MQCU94YkP8n/3slDw2KcEfRk4+4D8EUGfhSPMODEQ==", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.1.6", - "@mui/system": "^6.1.6", + "@mui/core-downloads-tracker": "^6.1.10", + "@mui/system": "^6.1.10", "@mui/types": "^7.2.19", - "@mui/utils": "^6.1.6", + "@mui/utils": "^6.1.10", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", @@ -2729,7 +2859,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.1.6", + "@mui/material-pigment-css": "^6.1.10", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -2749,42 +2879,13 @@ } } }, - "node_modules/@mui/material/node_modules/@mui/utils": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.6.tgz", - "integrity": "sha512-sBS6D9mJECtELASLM+18WUcXF6RH3zNxBRFeyCRg8wad6NbyNrdxLuwK+Ikvc38sTZwBzAz691HmSofLqHd9sQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.19", - "@types/prop-types": "^15.7.13", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/private-theming": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.6.tgz", - "integrity": "sha512-ioAiFckaD/fJSnTrUMWgjl9HYBWt7ixCh7zZw7gDZ+Tae7NuprNV6QJK95EidDT7K0GetR2rU3kAeIR61Myttw==", + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.10.tgz", + "integrity": "sha512-DqgsH0XFEweeG3rQfVkqTkeXcj/E76PGYWag8flbPdV8IYdMo+DfVdFlZK8JEjsaIVD2Eu1kJg972XnH5pfnBQ==", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.1.6", + "@mui/utils": "^6.1.10", "prop-types": "^15.8.1" }, "engines": { @@ -2804,43 +2905,14 @@ } } }, - "node_modules/@mui/private-theming/node_modules/@mui/utils": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.6.tgz", - "integrity": "sha512-sBS6D9mJECtELASLM+18WUcXF6RH3zNxBRFeyCRg8wad6NbyNrdxLuwK+Ikvc38sTZwBzAz691HmSofLqHd9sQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.19", - "@types/prop-types": "^15.7.13", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/styled-engine": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.6.tgz", - "integrity": "sha512-I+yS1cSuSvHnZDBO7e7VHxTWpj+R7XlSZvTC4lS/OIbUNJOMMSd3UDP6V2sfwzAdmdDNBi7NGCRv2SZ6O9hGDA==", + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.10.tgz", + "integrity": "sha512-+NV9adKZYhslJ270iPjf2yzdVJwav7CIaXcMlPSi1Xy1S/zRe5xFgZ6BEoMdmGRpr34lIahE8H1acXP2myrvRw==", "dependencies": { "@babel/runtime": "^7.26.0", - "@emotion/cache": "^11.13.1", - "@emotion/serialize": "^1.3.2", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -2867,15 +2939,15 @@ } }, "node_modules/@mui/system": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.6.tgz", - "integrity": "sha512-qOf1VUE9wK8syiB0BBCp82oNBAVPYdj4Trh+G1s+L+ImYiKlubWhhqlnvWt3xqMevR+D2h1CXzA1vhX2FvA+VQ==", + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.10.tgz", + "integrity": "sha512-5YNIqxETR23SIkyP7MY2fFnXmplX/M4wNi2R+10AVRd3Ub+NLctWY/Vs5vq1oAMF0eSDLhRTGUjaUe+IGSfWqg==", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.1.6", - "@mui/styled-engine": "^6.1.6", + "@mui/private-theming": "^6.1.10", + "@mui/styled-engine": "^6.1.10", "@mui/types": "^7.2.19", - "@mui/utils": "^6.1.6", + "@mui/utils": "^6.1.10", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -2905,35 +2977,6 @@ } } }, - "node_modules/@mui/system/node_modules/@mui/utils": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.6.tgz", - "integrity": "sha512-sBS6D9mJECtELASLM+18WUcXF6RH3zNxBRFeyCRg8wad6NbyNrdxLuwK+Ikvc38sTZwBzAz691HmSofLqHd9sQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.19", - "@types/prop-types": "^15.7.13", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/types": { "version": "7.2.19", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", @@ -2948,27 +2991,27 @@ } }, "node_modules/@mui/utils": { - "version": "5.16.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", - "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.10.tgz", + "integrity": "sha512-1ETuwswGjUiAf2dP9TkBy8p49qrw2wXa+RuAjNTRE5+91vtXJ1HKrs7H9s8CZd1zDlQVzUcUAPm9lpQwF5ogTw==", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/types": "^7.2.15", - "@types/prop-types": "^15.7.12", + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.19", + "@types/prop-types": "^15.7.13", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^18.3.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2977,13 +3020,13 @@ } }, "node_modules/@mui/x-date-pickers": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.18.0.tgz", - "integrity": "sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.23.0.tgz", + "integrity": "sha512-Db9ElibVYHluXLVsRLfFwlYkL6/3NNE5AosSZiTx+Gw7uix/Z3pdjyHeA3ab65fU1tCk08XHY0PU6LQFifYB2g==", "dependencies": { - "@babel/runtime": "^7.25.6", - "@mui/utils": "^5.16.6", - "@mui/x-internals": "7.18.0", + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0", + "@mui/x-internals": "7.23.0", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -3006,10 +3049,10 @@ "dayjs": "^1.10.7", "luxon": "^3.0.2", "moment": "^2.29.4", - "moment-hijri": "^2.1.2", + "moment-hijri": "^2.1.2 || ^3.0.0", "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -3042,12 +3085,12 @@ } }, "node_modules/@mui/x-internals": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.18.0.tgz", - "integrity": "sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.23.0.tgz", + "integrity": "sha512-bPclKpqUiJYIHqmTxSzMVZi6MH51cQsn5U+8jskaTlo3J4QiMeCYJn/gn7YbeR9GOZFp8hetyHjoQoVHKRXCig==", "dependencies": { - "@babel/runtime": "^7.25.6", - "@mui/utils": "^5.16.6" + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0" }, "engines": { "node": ">=14.0.0" @@ -3057,7 +3100,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { @@ -3132,143 +3175,126 @@ "node": ">= 8" } }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@redux-devtools/extension": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.3.0.tgz", - "integrity": "sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.23.2", - "immutable": "^4.3.4" - }, - "peerDependencies": { - "redux": "^3.1.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/@reduxjs/toolkit": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", - "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", - "dependencies": { - "immer": "^10.0.3", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@remix-run/router": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", - "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "node_modules/@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" }, "engines": { - "node": ">=14.0.0" + "node": ">= 10.0.0" }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", "cpu": [ - "arm" + "arm64" ], "dev": true, "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", "cpu": [ "arm64" ], "dev": true, "optional": true, "os": [ - "android" - ] + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", "cpu": [ - "arm64" + "x64" ], "dev": true, "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "darwin" - ] + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", "cpu": [ "arm" ], @@ -3276,12 +3302,19 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", "cpu": [ "arm" ], @@ -3289,12 +3322,19 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", "cpu": [ "arm64" ], @@ -3302,12 +3342,19 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", "cpu": [ "arm64" ], @@ -3315,51 +3362,39 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", - "cpu": [ - "ppc64" ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", "cpu": [ - "riscv64" + "x64" ], "dev": true, "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", - "cpu": [ - "s390x" ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", "cpu": [ "x64" ], @@ -3367,58 +3402,316 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", "cpu": [ - "x64" + "arm64" ], "dev": true, "optional": true, "os": [ - "linux" - ] + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", "cpu": [ - "arm64" + "ia32" ], "dev": true, "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", "cpu": [ - "ia32" + "x64" ], "dev": true, "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "peer": true, + "dependencies": { + "@ant-design/fast-color": "^2.0.6", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz", + "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.5.tgz", + "integrity": "sha512-F1EJ4KjFpGAHAjuKvOyZB/6IZDkVx0bHl0M4fQM5wXcmm7lgTgVSSnR3bXwdmS6jOJGHOqfDxIJW3WUvwMIXhQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@redux-devtools/extension": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.3.0.tgz", + "integrity": "sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.2", + "immutable": "^4.3.4" + }, + "peerDependencies": { + "redux": "^3.1.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", + "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", + "dependencies": { + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@remix-run/router": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", + "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "win32" + "linux" ] }, "node_modules/@rushstack/eslint-patch": { @@ -3668,14 +3961,14 @@ } }, "node_modules/@swc/core": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.42.tgz", - "integrity": "sha512-iQrRk3SKndQZ4ptJv1rzeQSiCYQIhMjiO97QXOlCcCoaazOLKPnLnXzU4Kv0FuBFyYfG2FE94BoR0XI2BN02qw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.0.tgz", + "integrity": "sha512-+CuuTCmQFfzaNGg1JmcZvdUVITQXJk9sMnl1C2TiDLzOSVOJRwVD4dNo5dljX/qxpMAN+2BIYlwjlSkoGi6grg==", "dev": true, "hasInstallScript": true, "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.13" + "@swc/types": "^0.1.17" }, "engines": { "node": ">=10" @@ -3685,16 +3978,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.42", - "@swc/core-darwin-x64": "1.7.42", - "@swc/core-linux-arm-gnueabihf": "1.7.42", - "@swc/core-linux-arm64-gnu": "1.7.42", - "@swc/core-linux-arm64-musl": "1.7.42", - "@swc/core-linux-x64-gnu": "1.7.42", - "@swc/core-linux-x64-musl": "1.7.42", - "@swc/core-win32-arm64-msvc": "1.7.42", - "@swc/core-win32-ia32-msvc": "1.7.42", - "@swc/core-win32-x64-msvc": "1.7.42" + "@swc/core-darwin-arm64": "1.10.0", + "@swc/core-darwin-x64": "1.10.0", + "@swc/core-linux-arm-gnueabihf": "1.10.0", + "@swc/core-linux-arm64-gnu": "1.10.0", + "@swc/core-linux-arm64-musl": "1.10.0", + "@swc/core-linux-x64-gnu": "1.10.0", + "@swc/core-linux-x64-musl": "1.10.0", + "@swc/core-win32-arm64-msvc": "1.10.0", + "@swc/core-win32-ia32-msvc": "1.10.0", + "@swc/core-win32-x64-msvc": "1.10.0" }, "peerDependencies": { "@swc/helpers": "*" @@ -3706,9 +3999,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.42.tgz", - "integrity": "sha512-fWhaCs2+8GDRIcjExVDEIfbptVrxDqG8oHkESnXgymmvqTWzWei5SOnPNMS8Q+MYsn/b++Y2bDxkcwmq35Bvxg==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.0.tgz", + "integrity": "sha512-wCeUpanqZyzvgqWRtXIyhcFK3CqukAlYyP+fJpY2gWc/+ekdrenNIfZMwY7tyTFDkXDYEKzvn3BN/zDYNJFowQ==", "cpu": [ "arm64" ], @@ -3722,9 +4015,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.42.tgz", - "integrity": "sha512-ZaVHD2bijrlkCyD7NDzLmSK849Jgcx+6DdL4x1dScoz1slJ8GTvLtEu0JOUaaScQwA+cVlhmrmlmi9ssjbRLGQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.0.tgz", + "integrity": "sha512-0CZPzqTynUBO+SHEl/qKsFSahp2Jv/P2ZRjFG0gwZY5qIcr1+B/v+o74/GyNMBGz9rft+F2WpU31gz2sJwyF4A==", "cpu": [ "x64" ], @@ -3738,9 +4031,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.42.tgz", - "integrity": "sha512-iF0BJj7hVTbY/vmbvyzVTh/0W80+Q4fbOYschdUM3Bsud39TA+lSaPOefOHywkNH58EQ1z3EAxYcJOWNES7GFQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.0.tgz", + "integrity": "sha512-oq+DdMu5uJOFPtRkeiITc4kxmd+QSmK+v+OBzlhdGkSgoH3yRWZP+H2ao0cBXo93ZgCr2LfjiER0CqSKhjGuNA==", "cpu": [ "arm" ], @@ -3754,9 +4047,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.42.tgz", - "integrity": "sha512-xGu8j+DOLYTLkVmsfZPJbNPW1EkiWgSucT0nOlz77bLxImukt/0+HVm2hOwHSKuArQ8C3cjahAMY3b/s4VH2ww==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.0.tgz", + "integrity": "sha512-Y6+PC8knchEViRxiCUj3j8wsGXaIhuvU+WqrFqV834eiItEMEI9+Vh3FovqJMBE3L7d4E4ZQtgImHCXjrHfxbw==", "cpu": [ "arm64" ], @@ -3770,9 +4063,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.42.tgz", - "integrity": "sha512-qtW3JNO7i1yHEko59xxz+jY38+tYmB96JGzj6XzygMbYJYZDYbrOpXQvKbMGNG3YeTDan7Fp2jD0dlKf7NgDPA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.0.tgz", + "integrity": "sha512-EbrX9A5U4cECCQQfky7945AW9GYnTXtCUXElWTkTYmmyQK87yCyFfY8hmZ9qMFIwxPOH6I3I2JwMhzdi8Qoz7g==", "cpu": [ "arm64" ], @@ -3786,9 +4079,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.42.tgz", - "integrity": "sha512-F9WY1TN+hhhtiEzZjRQziNLt36M5YprMeOBHjsLVNqwgflzleSI7ulgnlQECS8c8zESaXj3ksGduAoJYtPC1cA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.0.tgz", + "integrity": "sha512-TaxpO6snTjjfLXFYh5EjZ78se69j2gDcqEM8yB9gguPYwkCHi2Ylfmh7iVaNADnDJFtjoAQp0L41bTV/Pfq9Cg==", "cpu": [ "x64" ], @@ -3802,9 +4095,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.42.tgz", - "integrity": "sha512-7YMdOaYKLMQ8JGfnmRDwidpLFs/6ka+80zekeM0iCVO48yLrJR36G0QGXzMjKsXI0BPhq+mboZRRENK4JfQnEA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.0.tgz", + "integrity": "sha512-IEGvDd6aEEKEyZFZ8oCKuik05G5BS7qwG5hO5PEMzdGeh8JyFZXxsfFXbfeAqjue4UaUUrhnoX+Ze3M2jBVMHw==", "cpu": [ "x64" ], @@ -3818,9 +4111,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.42.tgz", - "integrity": "sha512-C5CYWaIZEyqPl5W/EwcJ/mLBJFHVoUEa/IwWi0b4q2fCXcSCktQGwKXOQ+d67GneiZoiq0HasgcdMmMpGS9YRQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.0.tgz", + "integrity": "sha512-UkQ952GSpY+Z6XONj9GSW8xGSkF53jrCsuLj0nrcuw7Dvr1a816U/9WYZmmcYS8tnG2vHylhpm6csQkyS8lpCw==", "cpu": [ "arm64" ], @@ -3834,9 +4127,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.42.tgz", - "integrity": "sha512-3j47seZ5pO62mbrqvPe1iwhe2BXnM5q7iB+n2xgA38PCGYt0mnaJafqmpCXm/uYZOCMqSNynaoOWCMMZm4sqtA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.0.tgz", + "integrity": "sha512-a2QpIZmTiT885u/mUInpeN2W9ClCnqrV2LnMqJR1/Fgx1Afw/hAtiDZPtQ0SqS8yDJ2VR5gfNZo3gpxWMrqdVA==", "cpu": [ "ia32" ], @@ -3850,9 +4143,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.42.tgz", - "integrity": "sha512-FXl9MdeUogZLGDcLr6QIRdDVkpG0dkN4MLM4dwQ5kcAk+XfKPrQibX6M2kcfhsCx+jtBqtK7hRFReRXPWJZGbA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.0.tgz", + "integrity": "sha512-tZcCmMwf483nwsEBfUk5w9e046kMa1iSik4bP9Kwi2FGtOfHuDfIcwW4jek3hdcgF5SaBW1ktnK/lgQLDi5AtA==", "cpu": [ "x64" ], @@ -3872,9 +4165,9 @@ "dev": true }, "node_modules/@swc/types": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz", - "integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", "dev": true, "dependencies": { "@swc/counter": "^0.1.3" @@ -3955,9 +4248,9 @@ "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", - "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4012,9 +4305,9 @@ "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" }, "node_modules/@types/trusted-types": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", - "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", @@ -4749,25 +5042,25 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react-swc": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.1.tgz", - "integrity": "sha512-vgWOY0i1EROUK0Ctg1hwhtC3SdcDjZcdit4Ups4aPkDcB1jYhmo+RMYWY87cmXMhvtD5uf8lV89j2w16vkdSVg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.7.2.tgz", + "integrity": "sha512-y0byko2b2tSVVf5Gpng1eEhX1OvPC7x8yns1Fx8jDzlJp4LS6CMkCPfLw47cjyoMrshQDoQw4qcgjsU9VvlCew==", "dev": true, "dependencies": { "@swc/core": "^1.7.26" }, "peerDependencies": { - "vite": "^4 || ^5" + "vite": "^4 || ^5 || ^6" } }, "node_modules/@vitest/expect": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", - "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, "dependencies": { - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -4776,12 +5069,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", - "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", "dev": true, "dependencies": { - "@vitest/spy": "2.1.4", + "@vitest/spy": "2.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, @@ -4802,9 +5095,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", - "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, "dependencies": { "tinyrainbow": "^1.2.0" @@ -4814,12 +5107,12 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", - "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, "dependencies": { - "@vitest/utils": "2.1.4", + "@vitest/utils": "2.1.8", "pathe": "^1.1.2" }, "funding": { @@ -4827,12 +5120,12 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", - "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.8", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -4841,9 +5134,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", - "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, "dependencies": { "tinyspy": "^3.0.2" @@ -4853,12 +5146,12 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", - "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.4", + "@vitest/pretty-format": "2.1.8", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -4928,6 +5221,71 @@ "node": ">=4" } }, + "node_modules/antd": { + "version": "5.22.2", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.22.2.tgz", + "integrity": "sha512-vihhiJbm9VG3d6boUeD1q2MXMax+qBrXhgqCEC+45v8iGUF6m4Ct+lFiCW4oWaN3EABOsbVA6Svy3Rj/QkQFKw==", + "peer": true, + "dependencies": { + "@ant-design/colors": "^7.1.0", + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/cssinjs-utils": "^1.1.1", + "@ant-design/icons": "^5.5.1", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.25.7", + "@ctrl/tinycolor": "^3.6.1", + "@rc-component/color-picker": "~2.0.1", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", + "@rc-component/tour": "~1.15.1", + "@rc-component/trigger": "^2.2.5", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.30.0", + "rc-checkbox": "~3.3.0", + "rc-collapse": "~3.9.0", + "rc-dialog": "~9.6.0", + "rc-drawer": "~7.2.0", + "rc-dropdown": "~4.2.0", + "rc-field-form": "~2.5.1", + "rc-image": "~7.11.0", + "rc-input": "~1.6.3", + "rc-input-number": "~9.3.0", + "rc-mentions": "~2.17.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.9.3", + "rc-notification": "~5.6.2", + "rc-pagination": "~4.3.0", + "rc-picker": "~4.8.1", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.0", + "rc-resize-observer": "^1.4.0", + "rc-segmented": "~2.5.0", + "rc-select": "~14.16.3", + "rc-slider": "~11.1.7", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.48.1", + "rc-tabs": "~15.4.0", + "rc-textarea": "~1.8.2", + "rc-tooltip": "~6.2.1", + "rc-tree": "~5.10.1", + "rc-tree-select": "~5.24.4", + "rc-upload": "~4.8.1", + "rc-util": "^5.43.0", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5550,6 +5908,12 @@ "node": ">= 0.8" } }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", + "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==", + "peer": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5569,6 +5933,15 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "peer": true, + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js-compat": { "version": "3.37.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", @@ -5608,9 +5981,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -5722,6 +6095,12 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "peer": true + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -5816,6 +6195,19 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5852,9 +6244,12 @@ } }, "node_modules/dompurify": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", - "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.2.tgz", + "integrity": "sha512-YMM+erhdZ2nkZ4fTNRTSI94mb7VG7uVF5vj5Zde7tImgnhZE3R6YW/IACGIHb2ux+QkEXMhe591N+5jWOmL4Zw==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/dot-case": { "version": "3.0.4", @@ -6010,6 +6405,12 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -7381,20 +7782,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7722,9 +8109,9 @@ } }, "node_modules/i18next": { - "version": "23.16.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.4.tgz", - "integrity": "sha512-9NIYBVy9cs4wIqzurf7nLXPyf3R78xYbxExVqHLK9od3038rjpyOEzW+XB130kZ1N4PZ9inTtJ471CRJ4Ituyg==", + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", "funding": [ { "type": "individual", @@ -8310,6 +8697,15 @@ "dev": true, "license": "MIT" }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "peer": true, + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -8463,9 +8859,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.14", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz", + "integrity": "sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -8605,6 +9001,13 @@ "tslib": "^2.0.3" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -8828,215 +9231,820 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dependencies": { - "callsites": "^3.0.0" + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, + "node_modules/rc-cascader": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.30.0.tgz", + "integrity": "sha512-rrzSbk1Bdqbu+pDwiLCLHu72+lwX9BZ28+JKzoi0DWZ4N29QYFeip8Gctl33QVd2Xg3Rf14D3yAOG76ElJw16w==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "^2.3.1", + "rc-select": "~14.16.2", + "rc-tree": "~5.10.1", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-checkbox": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.3.0.tgz", + "integrity": "sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-collapse": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz", + "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dialog": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz", + "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-drawer": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.2.0.tgz", + "integrity": "sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.0.tgz", + "integrity": "sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/rc-field-form": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.5.1.tgz", + "integrity": "sha512-33hunXwynQJyeae7LS3hMGTXNeRBjiPyPYgB0824EbmLHiXC1EBGyUwRh6xjLRy9c+en5WARYN0gJz5+JAqwig==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-image": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.11.0.tgz", + "integrity": "sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.6.0", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-input": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.6.3.tgz", + "integrity": "sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-input-number": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.3.0.tgz", + "integrity": "sha512-JQ363ywqRyxwgVxpg2z2kja3CehTpYdqR7emJ/6yJjRdbvo+RvfE83fcpBCIJRq3zLp8SakmEXq60qzWyZ7Usw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.6.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.17.0.tgz", + "integrity": "sha512-sfHy+qLvc+p8jx8GUsujZWXDOIlIimp6YQz7N5ONQ6bHsa2kyG+BLa5k2wuxgebBbH97is33wxiyq5UkiXRpHA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.6.0", + "rc-menu": "~9.16.0", + "rc-textarea": "~1.8.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.0.tgz", + "integrity": "sha512-vAL0yqPkmXWk3+YKRkmIR8TYj3RVdEt3ptG2jCJXWNAvQbT0VJJdRyHZ7kG/l1JsZlB+VJq/VcYOo69VR4oD+w==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.3.tgz", + "integrity": "sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.2.tgz", + "integrity": "sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.3.2.tgz", + "integrity": "sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-4.3.0.tgz", + "integrity": "sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.8.2.tgz", + "integrity": "sha512-I6Nn4ngkRskSD//rsXDvjlEQ8CzX9kPQrUIb7+qTY49erJaa3/oKJWmi6JIxo/A7gy59phNmPTdhKosAa/NrQQ==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.0.tgz", + "integrity": "sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz", + "integrity": "sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.38.0", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.5.0.tgz", + "integrity": "sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.3", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.3.tgz", + "integrity": "sha512-51+j6s3fJJJXB7E+B6W1hM4Tjzv1B/Decooz9ilgegDBt3ZAth1b/xMwYCTrT5BbG2e53XACQsyDib2+3Ro1fg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" }, "engines": { - "node": ">=6" + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/rc-slider": { + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.7.tgz", + "integrity": "sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A==", + "peer": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" }, "engines": { - "node": ">=8" + "node": ">=8.x" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, "engines": { - "node": ">=8" + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "node_modules/rc-table": { + "version": "7.48.1", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.48.1.tgz", + "integrity": "sha512-Z4mDKjWg+xz/Ezdw6ivWcbqRpaJ0QfCORRoRrlrw65KSGZLK8OcTdacH22/fyGb8L4It/0/9qcMm8VrVAk/WBw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.41.0", + "rc-virtual-list": "^3.14.2" + }, "engines": { - "node": ">=8" + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/rc-tabs": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.4.0.tgz", + "integrity": "sha512-llKuyiAVqmXm2z7OrmhX5cNb2ueZaL8ZyA2P4R+6/72NYYcbEgOXibwHiQCFY2RiN3swXl53SIABi2CumUS02g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, "engines": { - "node": ">=8" + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "engines": { - "node": ">= 14.16" + "node_modules/rc-textarea": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.8.2.tgz", + "integrity": "sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.6.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" + "node_modules/rc-tooltip": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.2.1.tgz", + "integrity": "sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "license": "MIT", + "node_modules/rc-tree": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.10.1.tgz", + "integrity": "sha512-FPXb3tT/u39mgjr6JNlHaUTYfHkVGW56XaGDahDpEFLGsnPxGcVLNTjcqoQb/GNbSCycl7tD7EvIymwOTP0+Yw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, "engines": { - "node": ">= 0.4" + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" } }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/rc-tree-select": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.24.5.tgz", + "integrity": "sha512-PnyR8LZJWaiEFw0SHRqo4MNQWyyZsyMs8eNmo68uXZWjxc7QqeWcjPPoONN0rc90c3HZqGF9z+Roz+GLzY5GXA==", + "peer": true, "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "@babel/runtime": "^7.25.7", + "classnames": "2.x", + "rc-select": "~14.16.2", + "rc-tree": "~5.10.1", + "rc-util": "^5.43.0" }, - "engines": { - "node": "^10 || ^12 || >=14" + "peerDependencies": { + "react": "*", + "react-dom": "*" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" + "node_modules/rc-upload": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.8.1.tgz", + "integrity": "sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "node_modules/rc-util": { + "version": "5.43.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz", + "integrity": "sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==", + "peer": true, "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", + "node_modules/rc-virtual-list": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.15.0.tgz", + "integrity": "sha512-dF2YQztqrU3ijAeWOqscTshCEr7vpimzSqAVjO1AyAmaqcHulaXpnGR0ptK5PXfxTUy48VkJOiglMIxlkYGs0w==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, "engines": { - "node": ">=6" + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/raf-schd": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", - "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" - }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -9090,18 +10098,18 @@ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, "node_modules/react-hotkeys-hook": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.1.tgz", - "integrity": "sha512-scAEJOh3Irm0g95NIn6+tQVf/OICCjsQsC9NBHfQws/Vxw4sfq1tDQut5fhTEvPraXhu/sHxRd9lOtxzyYuNAg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.6.1.tgz", + "integrity": "sha512-XlZpbKUj9tkfgPgT9gA+1p7Ey6vFIZHttUjPqpTdyT5nqQ8mHL7elxvSbaC+dpSiHUSmr21Ya1mDxBZG3aje4Q==", "peerDependencies": { "react": ">=16.8.1", "react-dom": ">=16.8.1" } }, "node_modules/react-i18next": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.0.tgz", - "integrity": "sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==", + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.3.tgz", + "integrity": "sha512-J11oA30FbM3NZegUZjn8ySK903z6PLBz/ZuBYyT1JMR0QPrW6PFXvl1WoUhortdGi9dM0m48/zJQlPskVZXgVw==", "dependencies": { "@babel/runtime": "^7.25.0", "html-parse-stringify": "^3.0.1" @@ -9120,9 +10128,9 @@ } }, "node_modules/react-icons": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", - "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz", + "integrity": "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==", "peerDependencies": { "react": "*" } @@ -9132,6 +10140,16 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-js-cron": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-js-cron/-/react-js-cron-5.0.1.tgz", + "integrity": "sha512-qHUb/qWeMvXklGW6/hLtH9CjboRU7pu2hHxGGErUDTjHDOLn/b6CZHvVsx/e3ak+UObwf4Y0BHSQJzpn1JpvvA==", + "peerDependencies": { + "antd": ">=5.8.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/react-redux": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", @@ -9155,11 +10173,11 @@ } }, "node_modules/react-router": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", - "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", + "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", "dependencies": { - "@remix-run/router": "1.20.0" + "@remix-run/router": "1.21.0" }, "engines": { "node": ">=14.0.0" @@ -9169,12 +10187,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", - "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", + "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", "dependencies": { - "@remix-run/router": "1.20.0", - "react-router": "6.27.0" + "@remix-run/router": "1.21.0", + "react-router": "6.28.0" }, "engines": { "node": ">=14.0.0" @@ -9220,9 +10238,9 @@ } }, "node_modules/readdirp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", - "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, "engines": { "node": ">= 14.16.0" @@ -9374,6 +10392,12 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "peer": true + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -9420,9 +10444,9 @@ } }, "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", + "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -9435,22 +10459,24 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@rollup/rollup-android-arm-eabi": "4.27.4", + "@rollup/rollup-android-arm64": "4.27.4", + "@rollup/rollup-darwin-arm64": "4.27.4", + "@rollup/rollup-darwin-x64": "4.27.4", + "@rollup/rollup-freebsd-arm64": "4.27.4", + "@rollup/rollup-freebsd-x64": "4.27.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", + "@rollup/rollup-linux-arm-musleabihf": "4.27.4", + "@rollup/rollup-linux-arm64-gnu": "4.27.4", + "@rollup/rollup-linux-arm64-musl": "4.27.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", + "@rollup/rollup-linux-riscv64-gnu": "4.27.4", + "@rollup/rollup-linux-s390x-gnu": "4.27.4", + "@rollup/rollup-linux-x64-gnu": "4.27.4", + "@rollup/rollup-linux-x64-musl": "4.27.4", + "@rollup/rollup-win32-arm64-msvc": "4.27.4", + "@rollup/rollup-win32-ia32-msvc": "4.27.4", + "@rollup/rollup-win32-x64-msvc": "4.27.4", "fsevents": "~2.3.2" } }, @@ -9529,13 +10555,13 @@ } }, "node_modules/sass": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", - "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.82.0.tgz", + "integrity": "sha512-j4GMCTa8elGyN9A7x7bEglx0VgSpNUG4W4wNedQ33wSMdnkqQCT8HTwOaVSV4e6yQovcu/3Oc4coJP/l0xhL2Q==", "dev": true, "dependencies": { "chokidar": "^4.0.0", - "immutable": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -9543,8 +10569,17 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, + "node_modules/sass/node_modules/immutable": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", + "dev": true + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -9553,6 +10588,15 @@ "loose-envify": "^1.1.0" } }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "peer": true, + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -9705,11 +10749,17 @@ "dev": true }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", "dev": true }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "peer": true + }, "node_modules/string-natural-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", @@ -9938,6 +10988,15 @@ "dev": true, "license": "MIT" }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "peer": true, + "engines": { + "node": ">=12.22" + } + }, "node_modules/tiny-case": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", @@ -9966,9 +11025,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", - "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -10012,6 +11071,12 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "peer": true + }, "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", @@ -10194,9 +11259,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10342,9 +11407,9 @@ } }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -10355,9 +11420,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, "dependencies": { "esbuild": "^0.21.3", @@ -10414,13 +11479,14 @@ } }, "node_modules/vite-node": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", - "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", "dev": true, "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, @@ -10488,9 +11554,9 @@ } }, "node_modules/vite/node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -10508,7 +11574,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -10516,30 +11582,30 @@ } }, "node_modules/vitest": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", - "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", - "dev": true, - "dependencies": { - "@vitest/expect": "2.1.4", - "@vitest/mocker": "2.1.4", - "@vitest/pretty-format": "^2.1.4", - "@vitest/runner": "2.1.4", - "@vitest/snapshot": "2.1.4", - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "dev": true, + "dependencies": { + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", - "std-env": "^3.7.0", + "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.4", + "vite-node": "2.1.8", "why-is-node-running": "^2.3.0" }, "bin": { @@ -10554,8 +11620,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.4", - "@vitest/ui": "2.1.4", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 6208ec3049..19d6ffcd83 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-hotkeys-hook": "^4.5.1", "react-i18next": "^15.1.0", "react-icons": "^5.3.0", + "react-js-cron": "^5.0.1", "react-redux": "^9.1.2", "react-router-dom": "^6.27.0", "react-select": "^5.8.0", diff --git a/src/components/events/LifeCyclePolicies.tsx b/src/components/events/LifeCyclePolicies.tsx index 19f5c46755..157afeb9ca 100644 --- a/src/components/events/LifeCyclePolicies.tsx +++ b/src/components/events/LifeCyclePolicies.tsx @@ -24,6 +24,8 @@ import { lifeCyclePoliciesTemplateMap } from "../../configs/tableConfigs/lifeCyc import { fetchEvents } from "../../slices/eventSlice"; import { setOffset } from "../../slices/tableSlice"; import { fetchSeries } from "../../slices/seriesSlice"; +import NewResourceModal from "../shared/NewResourceModal"; +import { fetchLifeCyclePolicyActions, fetchLifeCyclePolicyTargetTypes, fetchLifeCyclePolicyTimings } from "../../slices/lifeCycleDetailsSlice"; /** * This component renders the table view of policies @@ -32,6 +34,7 @@ const LifeCyclePolicies = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); + const [displayNewPolicyModal, setNewPolicyModal] = useState(false); const user = useAppSelector(state => getUserInformation(state)); const policiesTotal = useAppSelector(state => getTotalLifeCyclePolicies(state)); @@ -77,6 +80,11 @@ const LifeCyclePolicies = () => { // Load policies on mount loadLifeCyclePolicies().then((r) => console.info(r)); + + // Fetch policies repeatedly + let fetchInterval = setInterval(loadLifeCyclePolicies, 5000); + + return () => clearInterval(fetchInterval); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -84,10 +92,32 @@ const LifeCyclePolicies = () => { setNavigation(!displayNavigation); }; + const showNewPolicyModal = async () => { + await dispatch(fetchLifeCyclePolicyActions()); + await dispatch(fetchLifeCyclePolicyTargetTypes()); + await dispatch(fetchLifeCyclePolicyTimings()); + + setNewPolicyModal(true); + }; + + const hideNewPolicyModal = () => { + setNewPolicyModal(false); + }; + return ( <>
+ { + /* Display modal for new event if add event button is clicked */ + displayNewPolicyModal && ( + + ) + } + {/* Include Burger-button menu*/} @@ -120,6 +150,15 @@ const LifeCyclePolicies = () => { )} + +
+ {hasAccess("ROLE_UI_EVENTS_CREATE", user) && ( + + )} +
diff --git a/src/components/events/partials/LifeCyclePolicyActionCell.tsx b/src/components/events/partials/LifeCyclePolicyActionCell.tsx index ce5df1e317..78de162c74 100644 --- a/src/components/events/partials/LifeCyclePolicyActionCell.tsx +++ b/src/components/events/partials/LifeCyclePolicyActionCell.tsx @@ -2,12 +2,13 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { useAppDispatch, useAppSelector } from "../../../store"; import { Tooltip } from "../../shared/Tooltip"; -import { LifeCyclePolicy } from "../../../slices/lifeCycleSlice"; +import { deleteLifeCyclePolicy, LifeCyclePolicy } from "../../../slices/lifeCycleSlice"; import DetailsModal from "../../shared/modals/DetailsModal"; import LifeCyclePolicyDetails from "./modals/LifeCyclePolicyDetails"; import { hasAccess } from "../../../utils/utils"; import { getUserInformation } from "../../../selectors/userInfoSelectors"; import { fetchLifeCyclePolicyDetails } from "../../../slices/lifeCycleDetailsSlice"; +import ConfirmModal from "../../shared/ConfirmModal"; /** * This component renders the title cells of series in the table view @@ -21,6 +22,7 @@ const LifeCyclePolicyActionCell = ({ const dispatch = useAppDispatch(); const [displayLifeCyclePolicyDetails, setLifeCyclePolicyDetails] = useState(false); + const [displayDeleteConfirmation, setDeleteConfirmation] = useState(false); const user = useAppSelector(state => getUserInformation(state)); @@ -34,6 +36,18 @@ const LifeCyclePolicyActionCell = ({ setLifeCyclePolicyDetails(false); }; + const hideDeleteConfirmation = () => { + setDeleteConfirmation(false); + }; + + const showDeleteConfirmation = async () => { + setDeleteConfirmation(true); + }; + + const deletingPolicy = (id: string) => { + dispatch(deleteLifeCyclePolicy(id)); + }; + return ( <> {/* view details location/recording */} @@ -55,6 +69,27 @@ const LifeCyclePolicyActionCell = ({ )} + + {/* delete policy */} + {hasAccess("ROLE_UI_LIFECYCLEPOLICY_DELETE", user) && ( + + + + + +
+ + )} + {/*
*/} +
- - - + + )} + ); }; diff --git a/src/components/events/partials/ModalTabsAndPages/NewLifeCyclePolicyGeneralPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewLifeCyclePolicyGeneralPage.tsx new file mode 100644 index 0000000000..532abdf59b --- /dev/null +++ b/src/components/events/partials/ModalTabsAndPages/NewLifeCyclePolicyGeneralPage.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import { FormikProps } from "formik"; +import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; +import LifeCyclePolicyGeneralFields from "../wizards/LifeCyclePolicyGeneralFields"; +import { LifeCyclePolicy, TargetFilter } from "../../../../slices/lifeCycleSlice"; + +/** + * This component renders the metadata page for new events and series in the wizards. + */ +const NewLifeCyclePolicyGeneralPage = ({ + formik, + nextPage, + header +}: { + formik: FormikProps, + nextPage: (values: T) => void, + header: string +}) => { + + return ( + <> +
+
+
+ {/* Table view containing input fields for metadata */} + +
+
+
+ + {/* Button for navigation to next page */} + + + ); +}; + +export default NewLifeCyclePolicyGeneralPage; diff --git a/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx b/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx index e818dd57c2..4cffc334ff 100644 --- a/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx +++ b/src/components/events/partials/modals/LifeCyclePolicyDetails.tsx @@ -1,18 +1,32 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import ModalNavigation from "../../../shared/modals/ModalNavigation"; import { getLifeCyclePolicyDetails } from "../../../../selectors/lifeCycleDetailsSelectors"; import LifeCyclePolicyGeneralTab from "../ModalTabsAndPages/LifeCyclePolicyGeneralTab"; import LifeCyclePolicyDetailsAccessTab from "../ModalTabsAndPages/LifeCyclePolicyAccessTab"; -import { useAppSelector } from "../../../../store"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { removeNotificationWizardForm } from "../../../../slices/notificationSlice"; +import { fetchLifeCyclePolicyActions, fetchLifeCyclePolicyTargetTypes, fetchLifeCyclePolicyTimings } from "../../../../slices/lifeCycleDetailsSlice"; /** * This component manages the tabs of the series details modal */ const LifeCyclePolicyDetails = () => { const [page, setPage] = useState(0); + const dispatch = useAppDispatch(); + + useEffect(() => { + dispatch(removeNotificationWizardForm()); + dispatch(fetchLifeCyclePolicyActions()); + dispatch(fetchLifeCyclePolicyTargetTypes()); + dispatch(fetchLifeCyclePolicyTimings()); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const policy = useAppSelector(state => getLifeCyclePolicyDetails(state)); + // tracks, whether the policies are different to the initial value + const [policyChanged, setPolicyChanged] = useState(false); + // information about tabs const tabs = [ { @@ -23,7 +37,7 @@ const LifeCyclePolicyDetails = () => { { tabTranslation: "LIFECYCLE.POLICIES.DETAILS.TAB.ACCESSPOLICIES", accessRole: "ROLE_UI_LIFECYCLEPOLICIES_DETAILS_ACCESSPOLICIES_VIEW", - name: "WARNING: None of the changes you make here can be saved!", + name: "Access Policies" }, ]; @@ -42,8 +56,8 @@ const LifeCyclePolicyDetails = () => { false} + policyChanged={policyChanged} + setPolicyChanged={setPolicyChanged} /> } diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx new file mode 100644 index 0000000000..042f2046f0 --- /dev/null +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -0,0 +1,475 @@ +import React, { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { FieldArray, FieldProps, FormikProps } from "formik"; +import { Field } from "../../../shared/Field"; +import RenderField from "../../../shared/wizard/RenderField"; +import { ALL_TARGET_FILTER_TYPES, LifeCyclePolicy, TargetFilter } from "../../../../slices/lifeCycleSlice"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { getLifeCyclePolicyActions, getLifeCyclePolicyTargetTypes, getLifeCyclePolicyTimings } from "../../../../selectors/lifeCycleDetailsSelectors"; +import DropDown from "../../../shared/DropDown"; +import { getEventMetadata } from "../../../../selectors/eventSelectors"; +import { fetchEventMetadata } from "../../../../slices/eventSlice"; + +/** + * This component renders the metadata page for new events and series in the wizards. + */ +// interface RequiredFormProps { +// sourceMode: string, +// processingWorkflow: string, +// } + +const LifeCyclePolicyGeneralFields = ({ + formik, + isNew, +}: { + formik: FormikProps, + isNew: boolean +}) => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + + const actions = useAppSelector(state => getLifeCyclePolicyActions(state)); + const targetTypes = useAppSelector(state => getLifeCyclePolicyTargetTypes(state)); + const timings = useAppSelector(state => getLifeCyclePolicyTimings(state)); + const metadataFields = useAppSelector(state => getEventMetadata(state)); + + useEffect(() => { + dispatch(fetchEventMetadata()) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const ADDITIONAL_TARGET_FILTER_KEYS_EVENTS = [ + { + id: 'series_name', + type: 'text', + collection: undefined + }, + { + id: 'presenter', + type: 'text', + collection: undefined + }, + { + id: 'start_date', + type: 'date', + collection: undefined + }, + { + id: 'end_date', + type: 'date', + collection: undefined + }, + { + id: 'created', + type: 'date', + collection: undefined + }, + { + id: 'source', + type: 'text', + collection: undefined + }, + { + id: 'rights', + type: 'text', + collection: undefined + }, + { + id: 'location', + type: 'text', + collection: undefined + }, + ] + + const eventFilterOptions: { id: string, type: string, collection?: unknown }[] = [] + for (const field of metadataFields.fields) { + eventFilterOptions.push(field) + } + for (const field of ADDITIONAL_TARGET_FILTER_KEYS_EVENTS) { + eventFilterOptions.push(field) + } + + const createTargetFilter = (): TargetFilter => { + return { + value: "", + type: "SEARCH", + must: true + } + } + + const filterOptions = (targetType: string) => { + switch (targetType) { + case "EVENT": + return eventFilterOptions + default: + return [] + } + } + + return ( + <> +
+
{t("LIFECYCLE.POLICIES.NEW.GENERAL.CAPTION")}
+ + + + + + + + + + + + {!isNew && + + + + + } + + + + + + + + + + + + + + + + + + + + + {!isNew && + + + + + } + + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TITLE")}* + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ISACTIVE")}* + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ISCREATEDFROMCONFIG")} + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETTYPE")}* + ({ value: element, name: element }) ), + id: "language", + }} + component={RenderField} + /> +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TIMING")}* + ({ value: element, name: element }) ), + id: "language", + }} + component={RenderField} + /> +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTION")}* + ({ value: element, name: element }) ), + id: "language" + }} + component={RenderField} + /> +
+ {t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONDATE")} + {formik.values.timing === "SPECIFIC_DATE" && *} + + +
+ {t("LIFECYCLE.POLICIES.DETAILS.GENERAL.CRONTRIGGER")} + {formik.values.timing === "REPEATING" && *} + + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ID")} + {formik.values.id} +
+
+ +
+ + + {/* Target Filters like the ACLs + Can we make "key" a dropdown? + Type of "Value" should depend on key, e.g. for key "start_date" show a date picker + */} +
+
+ { t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.CAPTION") } +
+ + + {/* column headers */} + + + + + + + + + + + + + {({ replace, remove, push }) => ( + <> + {Object.entries(formik.values.targetFiltersArray).map(([key, filter], index) => { + return( + + + + + + + + ) + })} + + + + + )} + + +
+ { t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.FILTER") } + + { t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.VALUE") } + + { t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.TYPE") } + + { t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.MUST") } + + { t("EVENTS.EVENTS.DETAILS.ACCESS.ACCESS_POLICY.ACTION") } +
+ e.id)} + creatable={true} + clearFieldName={`targetFiltersArray.${key}.value`} + component={DropdownField} + /> + + + + + + + +
+ +
+
+ + {formik.values.action === "START_WORKFLOW" && +
+
+ { t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONPARAMETERS.CAPTION") } +
+ + + + + + + + + + + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONPARAMETERS.WORKFLOW_ID")}* + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONPARAMETERS.WORKFLOW_PARAMETERS")} + + {/* Using our "WorkflowConfig" component would be nice, but it does not allow us to add or remove config options */} + {/* */} +
+
+ } + + ); +}; + + +export default LifeCyclePolicyGeneralFields; + +const DropdownField = ({ + field, + form: { setFieldValue }, + value, + values, + clearFieldName, + creatable = false +}: { + field: FieldProps["field"] + form: FieldProps["form"] + value: string, + values: string[] + clearFieldName: string + creatable: boolean +}) => { + const { t } = useTranslation(); + + return ( + { + setFieldValue(clearFieldName, undefined) + element && setFieldValue(field.name, element.value) + }} + placeholder={`-- ${t("SELECT_NO_OPTION_SELECTED")} --`} + creatable={creatable} + /> + ) +}; + +const getTargetFilterRenderType = (filterName: string, targetFilterOptions: { id: string, type: string, collection?: unknown }[] ) => { + const option = targetFilterOptions.find(e => e.id === filterName); + if (option === undefined) { + return "text"; + } + // Simplify types like "long_text" or "mixed_text" + if (option.type.includes("text")) { + return "text"; + } + return option.type; +} + +const getTargetFilterRenderCollection = (filterName: string, targetFilterOptions: { id: string, type: string, collection?: unknown }[] ) => { + const option = targetFilterOptions.find(e => e.id === filterName); + return option !== undefined ? option.collection : undefined +} diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx new file mode 100644 index 0000000000..ddf79b8a47 --- /dev/null +++ b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx @@ -0,0 +1,114 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import AccessSummaryTable from "./summaryTables/AccessSummaryTable"; +import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; +import { FormikProps } from "formik"; +import { renderValidDate } from "../../../../utils/dateUtils"; +import { initialFormValuesNewLifeCyclePolicy } from "../../../../configs/modalConfig"; + +/** + * This component renders the summary page for new series in the new series wizard. + */ +// interface RequiredFormProps { + +// } + +const NewLifeCyclePolicySummary = ({ + formik, + previousPage, +}: { + formik: FormikProps, + previousPage: (values: T, twoPagesBack?: boolean) => void, +}) => { + const { t } = useTranslation(); + + return ( + <> +
+
+
+ +
+
{t("LIFECYCLE.POLICIES.NEW.CAPTION")}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ))} + + + + + + {/* @ts-ignore */} + + {/* @ts-ignore */} + + + } + + + +
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TITLE")}{formik.values.title}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ISACTIVE")}{formik.values.isActive}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETTYPE")}{formik.values.targetType}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TIMING")}{formik.values.timing}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTION")}{formik.values.action}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONDATE")}{t("dateFormats.dateTime.medium", { dateTime: renderValidDate(formik.values.actionDate) } )}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.CRONTRIGGER")}{formik.values.cronTrigger}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.CAPTION")} + {formik.values.targetFiltersArray.map((filter) => ( +
{filter.filter}{filter.value}{filter.type}{filter.must.toString()}
{t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONPARAMETERS.CAPTION")} + {formik.values.action === "START_WORKFLOW" && +
{formik.values.actionParameters.workflowId}{formik.values.actionParameters.workflowParameters}
+
+
+ {/*Summary access configuration*/} + + +
+
+
+ + {/* Button for navigation to next page and previous page */} + + + ); +}; + +export default NewLifeCyclePolicySummary; diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx new file mode 100644 index 0000000000..73b59719bf --- /dev/null +++ b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useState } from "react"; +import { Formik } from "formik"; +import NewAccessPage from "../ModalTabsAndPages/NewAccessPage"; +import WizardStepperEvent from "../../../shared/wizard/WizardStepperEvent"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { getUserInformation } from "../../../../selectors/userInfoSelectors"; +import { UserInfoState } from "../../../../slices/userInfoSlice"; +import { postNewLifeCyclePolicy } from "../../../../slices/lifeCycleSlice"; +import NewLifeCyclePolicyGeneralPage from "../ModalTabsAndPages/NewLifeCyclePolicyGeneralPage"; +import NewLifeCyclePolicySummary from "./NewLifeCyclePolicySummary"; +import { LifeCyclePolicySchema } from "../../../../utils/validate"; +import { initialFormValuesNewLifeCyclePolicy } from "../../../../configs/modalConfig"; +import { parseTargetFiltersForSubmit } from "../../../../utils/lifeCycleUtils"; + +/** + * This component manages the pages of the new event wizard and the submission of values + */ +const NewLifeCyclePolicyWizard = ({ + close, +}: { + close: () => void +}) => { + const dispatch = useAppDispatch(); + + const user = useAppSelector(state => getUserInformation(state)); + + const initialValues = getInitialValues(user); + + const [page, setPage] = useState(0); + const [snapshot, setSnapshot] = useState(initialValues); + const [pageCompleted, setPageCompleted] = useState<{ [key: number]: boolean }>({}); + + // Caption of steps used by Stepper + const steps = [ + { + translation: "LIFECYCLE.POLICIES.NEW.GENERAL.CAPTION", + name: "general", + hidden: false, + }, + { + translation: "EVENTS.EVENTS.NEW.ACCESS.CAPTION", + name: "access", + hidden: false, + }, + { + translation: "EVENTS.EVENTS.NEW.SUMMARY.CAPTION", + name: "summary", + hidden: false, + }, + ]; + + const nextPage = (values: typeof initialValues) => { + setSnapshot(values); + + // set page as completely filled out + let updatedPageCompleted = pageCompleted; + updatedPageCompleted[page] = true; + setPageCompleted(updatedPageCompleted); + + let newPage = page; + do { + newPage = newPage + 1; + } while(steps[newPage] && steps[newPage].hidden); + if (steps[newPage]) { + setPage(newPage) + } + }; + + const previousPage = (values: typeof initialValues) => { + setSnapshot(values); + + let newPage = page; + do { + newPage = newPage - 1; + } while(steps[newPage] && steps[newPage].hidden); + if (steps[newPage]) { + setPage(newPage) + } + }; + + const handleSubmit = (values: typeof initialValues) => { + const fixedValues = { + ...values, + targetFilters: parseTargetFiltersForSubmit(values.targetFiltersArray), + accessControlEntries: values.acls + } + if (fixedValues.action === "START_WORKFLOW") { + fixedValues.actionParameters["workflowParameters"] = JSON.parse(values.actionParameters["workflowParameters"] as string) + } + // values["accessControlEntries"] = values.acls; + const response = dispatch(postNewLifeCyclePolicy(fixedValues)); + console.info(response); + close(); + }; + + return ( + <> + handleSubmit(values)} + > + {/* Render wizard pages depending on current value of page variable */} + {(formik) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + formik.validateForm(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [page]); + + return ( + <> + {/* Stepper that shows each step of wizard as header */} + +
+ {page === 0 && ( + + )} + {page === 1 && ( + + )} + {page === 2 && ( + + )} +
+ + ); + }} +
+ + ); +} + +// Transform all initial values needed from information provided by backend +const getInitialValues = ( + user: UserInfoState +) => { + let initialValues = initialFormValuesNewLifeCyclePolicy; + + initialValues["acls"] = [ + { + role: user.userRole, + read: true, + write: true, + actions: [], + }, + ]; + + return initialValues; +}; + +export default NewLifeCyclePolicyWizard; diff --git a/src/components/events/partials/wizards/RenderWorkflowConfig.tsx b/src/components/events/partials/wizards/RenderWorkflowConfig.tsx index 6a9c067fa7..c564699deb 100644 --- a/src/components/events/partials/wizards/RenderWorkflowConfig.tsx +++ b/src/components/events/partials/wizards/RenderWorkflowConfig.tsx @@ -6,7 +6,7 @@ import { getWorkflowDefById, } from "../../../../selectors/workflowSelectors"; import { useAppSelector } from "../../../../store"; -import { FieldSetField } from "../../../../slices/workflowSlice"; +import { ConfigurationPanelField, FieldSetField } from "../../../../slices/workflowSlice"; /** * This component renders the configuration panel for the selected workflow in the processing step of the new event @@ -32,10 +32,28 @@ const RenderWorkflowConfig = ({ const configPanel = !!workflowDef && workflowDef.configuration_panel_json ? workflowDef.configuration_panel_json : []; - const description = !!workflowDef && workflowDef.description + const description = !!workflowDef && workflowDef.description && !displayDescription ? workflowDef.description : ""; + return ( + + ); +}; + +export const WorkflowConfig = ({ + formik, + configPanel, + description +}: { + formik: FormikProps + configPanel: string | ConfigurationPanelField[] + description: string +}) => { const descriptionBoxStyle = { margin: "15px 0 0 0", position: "relative" as const, @@ -55,7 +73,7 @@ const RenderWorkflowConfig = ({ return ( <> - {displayDescription && description.length > 0 && ( + {description.length > 0 && (
{description.trim()}
diff --git a/src/components/shared/ConfirmModal.tsx b/src/components/shared/ConfirmModal.tsx index 91395fab88..4a4262dc23 100644 --- a/src/components/shared/ConfirmModal.tsx +++ b/src/components/shared/ConfirmModal.tsx @@ -15,7 +15,7 @@ const ConfirmModal = ({ deleteWithCautionMessage = "", }: { close: () => void, - resourceType: "EVENT" | "SERIES" | "LOCATION" | "USER" | "GROUP" | "ACL" | "THEME", + resourceType: "EVENT" | "SERIES" | "LOCATION" | "USER" | "GROUP" | "ACL" | "THEME" | "LIFECYCLE_POLICY", resourceName: string, resourceId: T, deleteMethod: (id: T) => void, diff --git a/src/components/shared/DropDown.tsx b/src/components/shared/DropDown.tsx index 2c746a6307..dfa3d0c922 100644 --- a/src/components/shared/DropDown.tsx +++ b/src/components/shared/DropDown.tsx @@ -18,7 +18,7 @@ import CreatableSelect from "react-select/creatable"; * - Creating typescript types for each "type" is moot atm, as a lot of them (i.e. capture agents) are not properly typed yet * I would suggest waiting with typing options until all of its inputs are properly typed */ -export type DropDownType = "language" | "isPartOf" | "license" | "captureAgent" | "aclRole" | "workflow" | "aclTemplate" | "newTheme" | "comment" | "theme" | "time" | "filter"; +export type DropDownType = "language" | "isPartOf" | "license" | "captureAgent" | "aclRole" | "workflow" | "aclTemplate" | "newTheme" | "comment" | "theme" | "time" | "filter" | "policyAction"; /** * This component provides a bar chart for visualising (statistics) data diff --git a/src/components/shared/NewResourceModal.tsx b/src/components/shared/NewResourceModal.tsx index 848e48df43..d47f022c7c 100644 --- a/src/components/shared/NewResourceModal.tsx +++ b/src/components/shared/NewResourceModal.tsx @@ -8,6 +8,7 @@ import NewGroupWizard from "../users/partials/wizard/NewGroupWizard"; import NewUserWizard from "../users/partials/wizard/NewUserWizard"; import { useHotkeys } from "react-hotkeys-hook"; import { availableHotkeys } from "../../configs/hotkeysConfig"; +import NewLifeCyclePolicyWizard from "../events/partials/wizards/NewLifeCyclePolicyWizard"; /** * This component renders the modal for adding new resources @@ -17,7 +18,7 @@ const NewResourceModal = ({ resource }: { handleClose: () => void, - resource: "events" | "series" | "user" | "group" | "acl" | "themes" + resource: "events" | "series" | "user" | "group" | "acl" | "themes" | "lifecyclepolicy" }) => { const { t } = useTranslation(); @@ -56,6 +57,9 @@ const NewResourceModal = ({ {resource === "user" && (

{t("USERS.USERS.DETAILS.NEWCAPTION")}

)} + {resource === "lifecyclepolicy" && ( +

{t("LIFECYCLE.POLICIES.NEW.CAPTION")}

+ )}
{resource === "events" && ( //New Event Wizard @@ -81,6 +85,10 @@ const NewResourceModal = ({ // New User Wizard )} + {resource === "lifecyclepolicy" && ( + // New LifeCyclePolicy Wizard + + )} ); diff --git a/src/components/shared/wizard/RenderField.tsx b/src/components/shared/wizard/RenderField.tsx index 1a72718c97..6e8a979847 100644 --- a/src/components/shared/wizard/RenderField.tsx +++ b/src/components/shared/wizard/RenderField.tsx @@ -10,6 +10,8 @@ import { parseISO } from "date-fns"; import { FieldProps } from "formik"; import { MetadataField } from "../../../slices/eventSlice"; import { renderValidDate } from "../../../utils/dateUtils"; +import Cron from "react-js-cron"; +import 'react-js-cron/dist/styles.css' const childRef = React.createRef(); /** @@ -23,7 +25,7 @@ const RenderField = ({ isFirstField = false, }: { field: FieldProps["field"] - metadataField: MetadataField + metadataField: { type: string, collection: { [key: string]: unknown }[], required: boolean, id: string }, //MetadataField form: FieldProps["form"] showCheck?: boolean, isFirstField?: boolean, @@ -129,6 +131,17 @@ const RenderField = ({ handleKeyDown={handleKeyDown} /> )} + {metadataField.type === "cron" && ( + + )} ); }; @@ -229,7 +242,7 @@ const EditableSingleSelect = ({ showCheck, }: { field: FieldProps["field"] - metadataField: MetadataField + metadataField: { type: string, collection: { [key: string]: unknown }[], required: boolean, id: string }, //MetadataField text: string editMode: boolean | undefined setEditMode: (e: boolean) => void @@ -427,4 +440,55 @@ const EditableSingleValueTime = ({ ); }; +const EditableCronValue = ({ + field, + form: { initialValues, setFieldValue }, + text, + editMode, + setEditMode, + showCheck, + handleKeyDown, +} : { + field: FieldProps["field"] + form: FieldProps["form"] + text: string + editMode: boolean | undefined + setEditMode: (e: boolean) => void + showCheck?: boolean, + handleKeyDown: (event: React.KeyboardEvent, type: string) => void +}) => { + + return editMode ? ( + // TODO: Figure out a way to set EditMode to false again + // As of now, selecting a value in one of the dropdowns will cause EditMode + // to be set to false, without the selected value actually being set. +
setEditMode(false)} + onKeyDown={(e) => handleKeyDown(e, "cron")} + // ref={childRef} + > + setFieldValue(field.name, value)} + /> +
+ ) : ( +
setEditMode(true)} className="show-edit"> + {text || ""} +
+ + {showCheck && ( + + )} +
+
+ ) +}; + + export default RenderField; diff --git a/src/components/shared/wizard/RenderMultiField.tsx b/src/components/shared/wizard/RenderMultiField.tsx index 303d5f7598..4357f40ce1 100644 --- a/src/components/shared/wizard/RenderMultiField.tsx +++ b/src/components/shared/wizard/RenderMultiField.tsx @@ -28,7 +28,7 @@ const RenderMultiField = ({ // Temporary storage for value user currently types in const [inputValue, setInputValue] = useState(""); - let fieldValue = [...field.value]; + let fieldValue = field.value ? [...field.value] : []; // Handle change of value user currently types in const handleChange = (e: React.ChangeEvent) => { diff --git a/src/configs/modalConfig.ts b/src/configs/modalConfig.ts index a9c4ba1901..5922dae1db 100644 --- a/src/configs/modalConfig.ts +++ b/src/configs/modalConfig.ts @@ -5,6 +5,7 @@ import { TobiraPage } from "../slices/seriesSlice"; import { initArray } from "../utils/utils"; import { EditedEvents, Event, UploadAssetsTrack } from "../slices/eventSlice"; import { Role } from "../slices/aclSlice"; +import { TargetFilter } from "../slices/lifeCycleSlice"; // Context for notifications shown in modals export const NOTIFICATION_CONTEXT = "modal-form"; @@ -210,3 +211,33 @@ export const initialFormValuesEditScheduledEvents: { editedEvents: [], changedEvents: [], }; + +export const initialFormValuesNewLifeCyclePolicy: { + title: string, + isActive: boolean, + isCreatedFromConfig: boolean, + targetType: string, + timing: string, + action: string, + actionDate: string, + cronTrigger: string, + actionParameters: { [key: string]: unknown } + acls: TransformedAcl[] + targetFiltersArray: (TargetFilter & { filter: string })[], +} = { + title: "", + isActive: true, + isCreatedFromConfig: false, + targetType: "EVENT", + timing: "SPECIFIC_DATE", + action: "START_WORKFLOW", + actionDate: "", + cronTrigger: "", + actionParameters: { + workflowId: "noop", + workflowParameters: `{"straightToPublishing": true}` + }, + + acls: [], + targetFiltersArray: [], +}; diff --git a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json index cd16368d29..8bfeeff89a 100644 --- a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json +++ b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json @@ -119,7 +119,8 @@ "GROUP": "The following group will be deleted", "USER": "The following user will be deleted", "THEME": "The following theme will be deleted", - "LOCATION": "The following location will be deleted" + "LOCATION": "The following location will be deleted", + "LIFECYCLE_POLICY": "The following lifeCycle policy will be deleted" }, "NAME": "Name" }, @@ -190,6 +191,10 @@ "EVENTS_NOT_DELETED_NOT_AUTHORIZED": "The event(s) could not be deleted, because you don't have the permission to do so.", "SERIES_DELETED": "The series has been deleted", "SERIES_NOT_DELETED": "The series could not be deleted", + "LIFECYCLE_POLICY_ADDED": "The lifeCycle policy has been created", + "LIFECYCLE_POLICY_NOT_SAVED": "The lifeCycle policy could not be saved", + "LIFECYCLE_POLICY_DELETED": "The lifeCycle policy has been deleted", + "LIFECYCLE_POLICY_NOT_DELETED": "The lifeCycle policy could not be deleted", "LOCATION_DELETED": "The location has been deleted", "LOCATION_NOT_DELETED": "The location could not be deleted", "LOCATION_NOT_DELETED_NOT_AUTHORIZED": "The location could not be deleted, because you don't have the permission to do so.", @@ -1261,7 +1266,15 @@ "TIMING": "Timing", "TITLE": "Title", "TOOLTIP": { - "DETAILS": "LifeCycle Policy Details" + "DETAILS": "LifeCycle Policy Details", + "DELETE": "Delete LifeCycle Policy" + }, + "ADD_POLICY": "Add LifeCycle Policy" + }, + "NEW": { + "CAPTION": "Create LifeCycle Policy", + "GENERAL": { + "CAPTION": "General" } }, "DETAILS": { @@ -1269,16 +1282,31 @@ "GENERAL": { "ACTION": "Action", "ACTIONDATE": "Action Date", - "ACTIONPARAMETERS": "Action Parameters", + "ACTIONPARAMETERS": { + "CAPTION": "Action Parameters", + "WORKFLOW_ID": "Workflow ID", + "WORKFLOW_PARAMETERS": "Workflow Parameters" + }, "CAPTION": "LifeCycle Policy Details", "CRONTRIGGER": "Cron Trigger", "ID": "Identifier", "ISACTIVE": "Active", "ISCREATEDFROMCONFIG": "Created from Config", "TARGETTYPE": "Target Type", - "TARGETFILTERS": "Target Filters", + "TARGETFILTERS": { + "CAPTION": "Target Filters", + "FILTER": "Filter", + "VALUE": "Value", + "TYPE": "Type", + "MUST": "Must", + "NEW": "New Filter" + }, "TIMING": "Timing", - "TITLE": "Title" + "TITLE": "Title", + "NOTE": { + "TITLE": "A note on editing LifeCycle Policies", + "MESSAGE": "The same policy cannot affect the same target multiple times. Depending on your goals, creating a new policy may be required." + } }, "ACCESS": { "LABEL": "Select a template", diff --git a/src/selectors/lifeCycleDetailsSelectors.ts b/src/selectors/lifeCycleDetailsSelectors.ts index 364368819a..8b279abb40 100644 --- a/src/selectors/lifeCycleDetailsSelectors.ts +++ b/src/selectors/lifeCycleDetailsSelectors.ts @@ -5,3 +5,6 @@ import { RootState } from "../store"; */ export const getLifeCyclePolicyDetails = (state: RootState) => state.lifeCyclePolicyDetails; export const getLifeCyclePolicyDetailsAcl = (state: RootState) => state.lifeCyclePolicyDetails.accessControlEntries; +export const getLifeCyclePolicyActions = (state: RootState) => state.lifeCyclePolicyDetails.actionsEnum; +export const getLifeCyclePolicyTargetTypes = (state: RootState) => state.lifeCyclePolicyDetails.targetTypesEnum; +export const getLifeCyclePolicyTimings = (state: RootState) => state.lifeCyclePolicyDetails.timingsEnum; diff --git a/src/slices/lifeCycleDetailsSlice.ts b/src/slices/lifeCycleDetailsSlice.ts index 2bba3cda56..447254d3ef 100644 --- a/src/slices/lifeCycleDetailsSlice.ts +++ b/src/slices/lifeCycleDetailsSlice.ts @@ -5,6 +5,8 @@ import { LifeCyclePolicy } from './lifeCycleSlice'; import { TransformedAcl } from './aclDetailsSlice'; import { createPolicy } from '../utils/resourceUtils'; import { Ace } from './aclSlice'; +import { addNotification } from './notificationSlice'; + /** * This file contains redux reducer for actions affecting the state of a lifeCyclePolicy/capture agent @@ -12,6 +14,10 @@ import { Ace } from './aclSlice'; interface LifeCyclePolicyDetailsState extends LifeCyclePolicy { statusLifeCyclePolicyDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed', errorLifeCyclePolicyDetails: SerializedError | null, + + actionsEnum: string[], + targetTypesEnum: string[], + timingsEnum: string[], } // Initial state of lifeCyclePolicy details in redux store @@ -29,7 +35,11 @@ const initialState: LifeCyclePolicyDetailsState = { actionDate: "", cronTrigger: "", targetFilters: {}, - accessControlEntries: [] + accessControlEntries: [], + + actionsEnum: [], + targetTypesEnum: [], + timingsEnum: [], }; // fetch details of certain lifeCyclePolicy from server @@ -70,6 +80,27 @@ export const fetchLifeCyclePolicyDetails = createAppAsyncThunk('lifeCyclePolicyD return data; }); +export const fetchLifeCyclePolicyActions = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyActions', async () => { + const res = await axios.get(`/api/lifecyclemanagement/policies/actions`); + const data = res.data; + + return data; +}); + +export const fetchLifeCyclePolicyTargetTypes = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyTargetTypes', async () => { + const res = await axios.get(`/api/lifecyclemanagement/policies/targettypes`); + const data = res.data; + + return data; +}); + +export const fetchLifeCyclePolicyTimings = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyTimings', async () => { + const res = await axios.get(`/api/lifecyclemanagement/policies/timings`); + const data = res.data; + + return data; +}); + // Dummy function for compatability export const fetchLifeCyclePolicyDetailsAcls = createAppAsyncThunk('lifeCyclePolicyDetails/fetchLifeCyclePolicyDetailsAcls', async (id: string, {getState}) => { const state = getState(); @@ -81,7 +112,48 @@ export const updateLifeCyclePolicyAccess = createAppAsyncThunk('lifeCyclePolicyD id: string, policies: { acl: { ace: Ace[] } } }, {dispatch}) => { - return false; + const { id, policies } = params; + + let data = new URLSearchParams(); + data.append("accessControlEntries", JSON.stringify(policies.acl.ace)); + + axios.put(`/api/lifecyclemanagement/policies/${id}`, data) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "LIFECYCLEPOLICY_ADDED"})); + return true; + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "LIFECYCLEPOLICY_NOT_SAVED"})); + return false; + }); +}); + +export const updateLifeCyclePolicy = createAppAsyncThunk('lifeCyclePolicyDetails/updateLifeCyclePolicy', async (policy: LifeCyclePolicy, {dispatch}) => { + let data = new URLSearchParams(); + + Object.entries(policy).forEach(([key, value]) => { + let stringified = value + if (stringified instanceof Date) { + stringified = stringified.toJSON() + } + else if (stringified === Object(stringified)) { + stringified = JSON.stringify(stringified) + } + // @ts-ignore + data.append(key, stringified); + }) + + axios.put(`/api/lifecyclemanagement/policies/${policy.id}`, data) + .then((response) => { + console.info(response); + dispatch(addNotification({type: "success", key: "LIFECYCLEPOLICY_ADDED"})); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification({type: "error", key: "LIFECYCLEPOLICY_NOT_SAVED"})); + }); }); const lifeCyclePolicyDetailsSlice = createSlice({ @@ -126,7 +198,25 @@ const lifeCyclePolicyDetailsSlice = createSlice({ .addCase(fetchLifeCyclePolicyDetails.rejected, (state, action) => { state.statusLifeCyclePolicyDetails = 'failed'; state.errorLifeCyclePolicyDetails = action.error; - }); + }) + .addCase(fetchLifeCyclePolicyActions.fulfilled, (state, action: PayloadAction< + LifeCyclePolicyDetailsState["actionsEnum"] + >) => { + const actionsEnum = action.payload; + state.actionsEnum = actionsEnum; + }) + .addCase(fetchLifeCyclePolicyTargetTypes.fulfilled, (state, action: PayloadAction< + LifeCyclePolicyDetailsState["actionsEnum"] + >) => { + const targetTypesEnum = action.payload; + state.targetTypesEnum = targetTypesEnum; + }) + .addCase(fetchLifeCyclePolicyTimings.fulfilled, (state, action: PayloadAction< + LifeCyclePolicyDetailsState["actionsEnum"] + >) => { + const timingsEnum = action.payload; + state.timingsEnum = timingsEnum; + }) } }); diff --git a/src/slices/lifeCycleSlice.ts b/src/slices/lifeCycleSlice.ts index cfa4e78192..6be65c607d 100644 --- a/src/slices/lifeCycleSlice.ts +++ b/src/slices/lifeCycleSlice.ts @@ -2,30 +2,35 @@ import { PayloadAction, SerializedError, createSlice } from '@reduxjs/toolkit' import { TableConfig } from "../configs/tableConfigs/aclsTableConfig"; import { lifeCyclePolicyTableConfig } from "../configs/tableConfigs/lifeCyclePoliciesTableConfig"; import axios from 'axios'; -import { getURLParams } from '../utils/resourceUtils'; +import { getURLParams, prepareAccessPolicyRulesForPost } from '../utils/resourceUtils'; import { createAppAsyncThunk } from '../createAsyncThunkWithTypes'; import { TransformedAcl } from './aclDetailsSlice'; +import { addNotification } from './notificationSlice'; -type LifeCyclePolicyTiming = "SPECIFIC_DATE" | "REPEATING" | "ALWAYS"; -type LifeCyclePolicyAction = "START_WORKFLOW" -type LifeCyclePolicyTargetType = "EVENT" +// type LifeCyclePolicyTiming = "SPECIFIC_DATE" | "REPEATING" | "ALWAYS"; +// type LifeCyclePolicyAction = "START_WORKFLOW" +// type LifeCyclePolicyTargetType = "EVENT" +export type TargetFilter = { + value: string | string[], + type: TargetFiltersType, + must: boolean +} +export const ALL_TARGET_FILTER_TYPES = ['SEARCH', 'WILDCARD', 'GREATER_THAN', 'LESS_THAN'] as const; +type TargetFilterTypesTuple = typeof ALL_TARGET_FILTER_TYPES; +type TargetFiltersType = TargetFilterTypesTuple[number]; export type LifeCyclePolicy = { actionParameters: { [key: string]: unknown }, // JSON. Variable, depends on action - timing: LifeCyclePolicyTiming, - action: LifeCyclePolicyAction, - targetType: LifeCyclePolicyTargetType, + timing: string, + action: string, + targetType: string, id: string, title: string, isActive: boolean, isCreatedFromConfig: boolean, actionDate: string, // Date cronTrigger: string, - targetFilters: { [key: string]: { - value: string, - type: "SEARCH" | "WILDCARD" | "GREATER_THAN" | "LESS_THAN", - must: boolean - }}, + targetFilters: { [key: string]: TargetFilter }, accessControlEntries: TransformedAcl[] } @@ -65,6 +70,72 @@ export const fetchLifeCyclePolicies = createAppAsyncThunk('lifeCycle/fetchLifeCy return res.data; }); +export const postNewLifeCyclePolicy = createAppAsyncThunk('lifeCycle/postNewLifeCyclePolicy', async ( + policy: { + actionParameters: { [key: string]: unknown }, + timing: string, + action: string, + targetType: string, + title: string, + isActive: boolean, + actionDate: string, + cronTrigger: string, + targetFilters: { [key: string]: TargetFilter }, + accessControlEntries: TransformedAcl[] + }, + { dispatch } +) => { + let data = new URLSearchParams(); + + // Format filter collections + // for (const filterName in policy.targetFilters) { + // // policy.targetFilters[filterName] + // if (hasOwnProperty(TARGET_FILTER_KEYS_EVENTS, filterName) + // && TARGET_FILTER_KEYS_EVENTS[filterName].collection) { + // policy.targetFilters[filterName].value = policy.targetFilters[filterName].value.toString() + // } + // } + + // Stringify + Object.entries(policy).forEach(([key, value]) => { + let stringified = value + if (stringified instanceof Date) { + stringified = stringified.toJSON() + } + else if (stringified === Object(stringified)) { + stringified = JSON.stringify(stringified) + } + // @ts-ignore + data.append(key, stringified); + }) + + data.delete("accessControlEntries"); + data.append("accessControlEntries", JSON.stringify(prepareAccessPolicyRulesForPost(policy.accessControlEntries).acl.ace)) + + axios.post(`/api/lifecyclemanagement/policies`, data) + .then((res) => { + console.info(res); + dispatch(addNotification({type: "success", key: "LIFECYCLE_POLICY_ADDED"})); + }) + .catch((res) => { + console.error(res); + dispatch(addNotification({type: "error", key: "LIFECYCLE_POLICY_NOT_SAVED"})); + }); +}); + +export const deleteLifeCyclePolicy = createAppAsyncThunk('lifeCycle/fetchLifeCyclePolicies', async (id: string, { dispatch }) => { + axios + .delete(`/api/lifecyclemanagement/policies/${id}`) + .then((res) => { + console.info(res); + dispatch(addNotification({type: "success", key: "LIFECYCLE_POLICY_DELETED"})); + }) + .catch((res) => { + console.error(res); + dispatch(addNotification({type: "error", key: "LIFECYCLE_POLICY_NOT_DELETED"})); + }); +}); + const lifeCycleSlice = createSlice({ name: 'lifeCycle', initialState, diff --git a/src/slices/workflowSlice.ts b/src/slices/workflowSlice.ts index e6a9db5b46..a0c8a688b5 100644 --- a/src/slices/workflowSlice.ts +++ b/src/slices/workflowSlice.ts @@ -15,7 +15,7 @@ export type FieldSetField = { [key: string]: unknown } -type ConfigurationPanelField = { +export type ConfigurationPanelField = { // We could potentially specify 'fieldset' more, but I cannot find a definition // for which key value pairs are allowed fieldset?: FieldSetField[] // Values can be anything diff --git a/src/styles/main.scss b/src/styles/main.scss index 083aaa90c8..d880fef953 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -625,3 +625,8 @@ tr.info { overflow: hidden; text-overflow: ellipsis; } + +// React-JS-Cron +.my-project-cron-select-dropdown { + z-index: 10000; +} \ No newline at end of file diff --git a/src/utils/componentStyles.ts b/src/utils/componentStyles.ts index 599d0c68e6..8bee3a5c71 100644 --- a/src/utils/componentStyles.ts +++ b/src/utils/componentStyles.ts @@ -19,6 +19,8 @@ export function dropDownStyle(type: DropDownType): StylesConfig { ? 360 : type === "aclTemplate" || type === "comment" || type === "filter" ? 200 + : type === "policyAction" + ? 150 : 250; return { diff --git a/src/utils/dropDownUtils.ts b/src/utils/dropDownUtils.ts index c3c1b052e7..b80e040379 100644 --- a/src/utils/dropDownUtils.ts +++ b/src/utils/dropDownUtils.ts @@ -31,6 +31,10 @@ export const filterBySearch = (filterText: string, type: DropDownType, options: return options.filter((item) => t(item.label).toLowerCase().includes(filterText) ); + } else if (type === "policyAction") { + return options.filter((item) => + t(item).toLowerCase().includes(filterText) + ); } else { return options.filter((item) => item.value.toLowerCase().includes(filterText) @@ -127,7 +131,14 @@ export const formatDropDownOptions = ( label: item.label, }); } - } else { + } else if (type === "policyAction") { + for (const item of unformattedOptions) { + formattedOptions.push({ + value: item, + label: item, + }); + } + }else { for (const item of unformattedOptions) { formattedOptions.push({ value: item.value, diff --git a/src/utils/lifeCycleUtils.ts b/src/utils/lifeCycleUtils.ts new file mode 100644 index 0000000000..f39117dde7 --- /dev/null +++ b/src/utils/lifeCycleUtils.ts @@ -0,0 +1,16 @@ +import { LifeCyclePolicy, TargetFilter } from "../slices/lifeCycleSlice"; + +export const parseTargetFiltersForSubmit = ( + targetFiltersArray: (TargetFilter & { filter: string })[] +) => { + const targetFilters: LifeCyclePolicy["targetFilters"] = {} + for (const filter of targetFiltersArray) { + targetFilters[filter.filter] = { + value: filter.value, + type: filter.type, + must: filter.must + } + } + + return targetFilters; +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 8e6857d36b..a5e0948c82 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -165,3 +165,13 @@ export const translateOverrideFallback = (asset: UploadAssetOption, t: TFunction return result; } + +/** + * (Hopefully) Typesafe way of checking if an object has a specific property + */ +export function hasOwnProperty( + obj: O, + key: K, +): obj is O & Record { + return Object.prototype.hasOwnProperty.call(obj, key); +} diff --git a/src/utils/validate.ts b/src/utils/validate.ts index 7d1829bdea..0cc0eb2346 100644 --- a/src/utils/validate.ts +++ b/src/utils/validate.ts @@ -225,3 +225,30 @@ export const EditGroupSchema = Yup.object().shape({ export const AdopterRegistrationSchema = Yup.object().shape({ email: Yup.string().email(), }); + +// Validation Schema used in lifecycle policy modal +export const LifeCyclePolicySchema = [ + Yup.object().shape({ + title: Yup.string().required("Required"), + isActive: Yup.bool().required("Required"), + targetType: Yup.string().required("Required"), + timing: Yup.string().required("Required"), + action: Yup.string().required("Required"), + actionParameters: Yup.object().shape({ + // Property only required if it actually exists on the object + workflowId: Yup.string().required("Required"), + // workflowId: Yup.string().when("workflowId", { + // is: (exists: any) => !!exists, + // then: Yup.string().required("Required"), + // }), + }), + actionDate: Yup.date().when("timing", { + is: (timing: string) => timing === "SPECIFIC_DATE", + then: () => Yup.date().required("Required"), + }), + cronTrigger: Yup.string().when("timing", { + is: (timing: string) => timing === "REPEATING", + then: () => Yup.string().required("Required"), + }), + }), +]; From 865e34c76c698330e2d8419a1286e6e2e70557e7 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 31 Jan 2025 16:19:18 +0100 Subject: [PATCH 04/23] Fix modal usage errors --- src/components/events/LifeCyclePolicies.tsx | 20 +++++++------- .../partials/LifeCyclePolicyActionCell.tsx | 26 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/events/LifeCyclePolicies.tsx b/src/components/events/LifeCyclePolicies.tsx index 476c455dc5..3cfd870c33 100644 --- a/src/components/events/LifeCyclePolicies.tsx +++ b/src/components/events/LifeCyclePolicies.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import MainNav from "../shared/MainNav"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; @@ -25,6 +25,7 @@ import { setOffset } from "../../slices/tableSlice"; import { fetchSeries } from "../../slices/seriesSlice"; import NewResourceModal from "../shared/NewResourceModal"; import { fetchLifeCyclePolicyActions, fetchLifeCyclePolicyTargetTypes, fetchLifeCyclePolicyTimings } from "../../slices/lifeCycleDetailsSlice"; +import { ModalHandle } from "../shared/modals/Modal"; /** * This component renders the table view of policies @@ -33,7 +34,7 @@ const LifeCyclePolicies = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); - const [displayNewPolicyModal, setNewPolicyModal] = useState(false); + const newPolicyModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); const policiesTotal = useAppSelector(state => getTotalLifeCyclePolicies(state)); @@ -96,11 +97,11 @@ const LifeCyclePolicies = () => { await dispatch(fetchLifeCyclePolicyTargetTypes()); await dispatch(fetchLifeCyclePolicyTimings()); - setNewPolicyModal(true); + newPolicyModalRef.current?.open() }; const hideNewPolicyModal = () => { - setNewPolicyModal(false); + newPolicyModalRef.current?.close?.() }; return ( @@ -109,12 +110,11 @@ const LifeCyclePolicies = () => { { /* Display modal for new event if add event button is clicked */ - displayNewPolicyModal && ( - - ) + } {/* Include Burger-button menu*/} diff --git a/src/components/events/partials/LifeCyclePolicyActionCell.tsx b/src/components/events/partials/LifeCyclePolicyActionCell.tsx index 78de162c74..e3f459cbf1 100644 --- a/src/components/events/partials/LifeCyclePolicyActionCell.tsx +++ b/src/components/events/partials/LifeCyclePolicyActionCell.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useAppDispatch, useAppSelector } from "../../../store"; import { Tooltip } from "../../shared/Tooltip"; @@ -9,6 +9,7 @@ import { hasAccess } from "../../../utils/utils"; import { getUserInformation } from "../../../selectors/userInfoSelectors"; import { fetchLifeCyclePolicyDetails } from "../../../slices/lifeCycleDetailsSlice"; import ConfirmModal from "../../shared/ConfirmModal"; +import { ModalHandle } from "../../shared/modals/Modal"; /** * This component renders the title cells of series in the table view @@ -22,7 +23,7 @@ const LifeCyclePolicyActionCell = ({ const dispatch = useAppDispatch(); const [displayLifeCyclePolicyDetails, setLifeCyclePolicyDetails] = useState(false); - const [displayDeleteConfirmation, setDeleteConfirmation] = useState(false); + const deleteConfirmationModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); @@ -37,11 +38,11 @@ const LifeCyclePolicyActionCell = ({ }; const hideDeleteConfirmation = () => { - setDeleteConfirmation(false); + deleteConfirmationModalRef.current?.close?.() }; const showDeleteConfirmation = async () => { - setDeleteConfirmation(true); + deleteConfirmationModalRef.current?.open() }; const deletingPolicy = (id: string) => { @@ -81,15 +82,14 @@ const LifeCyclePolicyActionCell = ({ )} - {displayDeleteConfirmation && ( - - )} + ); }; From c3c8672188aa4b510d994851ab68c9a875602970 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 2 Apr 2025 09:25:23 +0200 Subject: [PATCH 05/23] Fix various eslint complaints --- .../ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx | 3 +-- .../ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx | 3 +-- .../partials/wizards/LifeCyclePolicyGeneralFields.tsx | 10 +++++----- .../partials/wizards/NewLifeCyclePolicySummary.tsx | 10 +++++----- src/slices/lifeCycleDetailsSlice.ts | 5 ++--- src/slices/lifeCycleSlice.ts | 5 ++--- src/thunks/tableThunks.ts | 2 +- src/utils/lifeCycleUtils.ts | 2 +- 8 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx index bb5957f982..f18d56afb2 100644 --- a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx @@ -40,8 +40,7 @@ const LifeCyclePolicyDetailsAccessTab = ({ descriptionText={t("LIFECYCLE.POLICIES.DETAILS.ACCESS.DESCRIPTION")} policies={acl} fetchAccessPolicies={fetchLifeCyclePolicyDetailsAcls} - // TODO: Figure out why typescript is being funky here - // @ts-ignore + // @ts-expect-error: TODO: Figure out why typescript is being funky here saveNewAccessPolicies={updateLifeCyclePolicyAccess} editAccessRole={"ROLE_UI_LIFECYCLEPOLICY_DETAILS_ACL_EDIT"} policyChanged={policyChanged} diff --git a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx index 1682a6cb51..a93fdac59b 100644 --- a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx @@ -69,8 +69,7 @@ const LifeCyclePolicyGeneralTab = ({ // Access policies are handled in a different tab // Remove them here, else they will delete the ACL due to their formatting - // TODO: Find a typesafe (or straight up better) way to do this - // @ts-ignore + // @ts-expect-error: TODO: Find a typesafe (or straight up better) way to do this delete initialValues.accessControlEntries // Transform filters into something more editable diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx index ddb910deb1..3b002cdca8 100644 --- a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -167,7 +167,7 @@ const LifeCyclePolicyGeneralFields = ({ value: element, name: element }) ), + collection: targetTypes.map((element) => ({ value: element, name: element })), id: "language", }} component={RenderField} @@ -182,7 +182,7 @@ const LifeCyclePolicyGeneralFields = ({ value: element, name: element }) ), + collection: timings.map((element) => ({ value: element, name: element })), id: "language", }} component={RenderField} @@ -197,7 +197,7 @@ const LifeCyclePolicyGeneralFields = ({ value: element, name: element }) ), + collection: actions.map((element) => ({ value: element, name: element })), id: "language" }} component={RenderField} @@ -457,7 +457,7 @@ const DropdownField = ({ ) }; -const getTargetFilterRenderType = (filterName: string, targetFilterOptions: { id: string, type: string, collection?: unknown }[] ) => { +const getTargetFilterRenderType = (filterName: string, targetFilterOptions: { id: string, type: string, collection?: unknown }[]) => { const option = targetFilterOptions.find(e => e.id === filterName); if (option === undefined) { return "text"; @@ -469,7 +469,7 @@ const getTargetFilterRenderType = (filterName: string, targetFilterOptions: { id return option.type; } -const getTargetFilterRenderCollection = (filterName: string, targetFilterOptions: { id: string, type: string, collection?: unknown }[] ) => { +const getTargetFilterRenderCollection = (filterName: string, targetFilterOptions: { id: string, type: string, collection?: unknown }[]) => { const option = targetFilterOptions.find(e => e.id === filterName); return option !== undefined ? option.collection : undefined } diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx index ddf79b8a47..00a9485074 100644 --- a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx +++ b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx @@ -55,7 +55,7 @@ const NewLifeCyclePolicySummary = {t("LIFECYCLE.POLICIES.DETAILS.GENERAL.ACTIONDATE")} - {t("dateFormats.dateTime.medium", { dateTime: renderValidDate(formik.values.actionDate) } )} + {t("dateFormats.dateTime.medium", { dateTime: renderValidDate(formik.values.actionDate) })} {t("LIFECYCLE.POLICIES.DETAILS.GENERAL.CRONTRIGGER")} @@ -64,8 +64,8 @@ const NewLifeCyclePolicySummary = {t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.CAPTION")} - {formik.values.targetFiltersArray.map((filter) => ( - + {formik.values.targetFiltersArray.map((filter, key) => ( + {filter.filter} {filter.value} {filter.type} @@ -79,9 +79,9 @@ const NewLifeCyclePolicySummary = {formik.values.action === "START_WORKFLOW" && - {/* @ts-ignore */} + {/* @ts-expect-error: Potentially unknown */} {formik.values.actionParameters.workflowId} - {/* @ts-ignore */} + {/* @ts-expect-error: Potentially unknown */} {formik.values.actionParameters.workflowParameters} } diff --git a/src/slices/lifeCycleDetailsSlice.ts b/src/slices/lifeCycleDetailsSlice.ts index 447254d3ef..1cce3d158e 100644 --- a/src/slices/lifeCycleDetailsSlice.ts +++ b/src/slices/lifeCycleDetailsSlice.ts @@ -137,11 +137,10 @@ export const updateLifeCyclePolicy = createAppAsyncThunk('lifeCyclePolicyDetails let stringified = value if (stringified instanceof Date) { stringified = stringified.toJSON() - } - else if (stringified === Object(stringified)) { + } else if (stringified === Object(stringified)) { stringified = JSON.stringify(stringified) } - // @ts-ignore + // @ts-expect-error: ??? data.append(key, stringified); }) diff --git a/src/slices/lifeCycleSlice.ts b/src/slices/lifeCycleSlice.ts index 860da2037d..cd1461e691 100644 --- a/src/slices/lifeCycleSlice.ts +++ b/src/slices/lifeCycleSlice.ts @@ -101,11 +101,10 @@ export const postNewLifeCyclePolicy = createAppAsyncThunk('lifeCycle/postNewLife let stringified = value if (stringified instanceof Date) { stringified = stringified.toJSON() - } - else if (stringified === Object(stringified)) { + } else if (stringified === Object(stringified)) { stringified = JSON.stringify(stringified) } - // @ts-ignore + // @ts-expect-error: ??? data.append(key, stringified); }) diff --git a/src/thunks/tableThunks.ts b/src/thunks/tableThunks.ts index 77c82bb7fa..4ed43a3876 100644 --- a/src/thunks/tableThunks.ts +++ b/src/thunks/tableThunks.ts @@ -120,7 +120,7 @@ export const loadSeriesIntoTable = (): AppThunk => (dispatch, getState) => { }; export const loadLifeCyclePoliciesIntoTable = (): AppThunk => (dispatch, getState) => { - const { lifeCycle, table } = getState() as RootState; + const { lifeCycle, table } = getState(); const pagination = table.pagination; const resource = lifeCycle.results; const total = lifeCycle.total; diff --git a/src/utils/lifeCycleUtils.ts b/src/utils/lifeCycleUtils.ts index f39117dde7..4b29e2bab8 100644 --- a/src/utils/lifeCycleUtils.ts +++ b/src/utils/lifeCycleUtils.ts @@ -3,7 +3,7 @@ import { LifeCyclePolicy, TargetFilter } from "../slices/lifeCycleSlice"; export const parseTargetFiltersForSubmit = ( targetFiltersArray: (TargetFilter & { filter: string })[] ) => { - const targetFilters: LifeCyclePolicy["targetFilters"] = {} + const targetFilters: LifeCyclePolicy["targetFilters"] = {} for (const filter of targetFiltersArray) { targetFilters[filter.filter] = { value: filter.value, From 8815f373611598a7d21ef783cafae69574ea7717 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 9 Apr 2025 14:05:14 +0200 Subject: [PATCH 06/23] Fix undefined string --- src/components/events/LifeCyclePolicies.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/events/LifeCyclePolicies.tsx b/src/components/events/LifeCyclePolicies.tsx index dcebf9c8f8..8636121697 100644 --- a/src/components/events/LifeCyclePolicies.tsx +++ b/src/components/events/LifeCyclePolicies.tsx @@ -88,8 +88,6 @@ const LifeCyclePolicies = () => { onShowModal: showNewPolicyModal, text: "LIFECYCLE.POLICIES.TABLE.ADD_POLICY", resource: "lifecyclepolicy", - hotkeySequence: availableHotkeys.general.NEW_LIFECYCLEPOLICY.sequence, - hotkeyDescription: availableHotkeys.general.NEW_LIFECYCLEPOLICY.description, }} > From e3307b1515ec3906de02749f6a0fe70820c85749 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 26 May 2025 11:29:34 +0200 Subject: [PATCH 07/23] Fix various issues from the latest merges Some of the PRs that were merged into this branch required changes that were not made. This fixes a couple issues introduced this way. --- .../ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx | 10 ++++++++-- .../partials/wizards/LifeCyclePolicyGeneralFields.tsx | 3 +++ .../partials/wizards/NewLifeCyclePolicySummary.tsx | 4 ++-- .../partials/wizards/NewLifeCyclePolicyWizard.tsx | 6 +++--- src/configs/modalConfig.ts | 4 ++-- .../opencastproject/adminui/languages/lang-en_US.json | 8 +++++++- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx index f18d56afb2..1007c02c2c 100644 --- a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyAccessTab.tsx @@ -36,12 +36,18 @@ const LifeCyclePolicyDetailsAccessTab = ({ resourceId={seriesId} header={header} buttonText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.LABEL"} - saveButtonText={"SAVE"} - descriptionText={t("LIFECYCLE.POLICIES.DETAILS.ACCESS.DESCRIPTION")} policies={acl} + policyTemplateId={0} // TODO: Improve? fetchAccessPolicies={fetchLifeCyclePolicyDetailsAcls} // @ts-expect-error: TODO: Figure out why typescript is being funky here saveNewAccessPolicies={updateLifeCyclePolicyAccess} + descriptionText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.DESCRIPTION"} + policyTableHeaderText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.NON_USER_ROLES"} + policyTableRoleText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.ROLE"} + policyTableNewText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.NEW"} + userPolicyTableHeaderText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.USERS"} + userPolicyTableRoleText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.USER"} + userPolicyTableNewText={"LIFECYCLE.POLICIES.DETAILS.ACCESS.NEW_USER"} editAccessRole={"ROLE_UI_LIFECYCLEPOLICY_DETAILS_ACL_EDIT"} policyChanged={policyChanged} setPolicyChanged={setPolicyChanged} diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx index 3b002cdca8..5ad5a4fb6f 100644 --- a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -453,6 +453,9 @@ const DropdownField = ({ }} placeholder={`-- ${t("SELECT_NO_OPTION_SELECTED")} --`} creatable={creatable} + customCSS={{ + width: "100%" + }} /> ) }; diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx index 00a9485074..c18de69e84 100644 --- a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx +++ b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx @@ -67,7 +67,7 @@ const NewLifeCyclePolicySummary = ( {filter.filter} - {filter.value} + {filter.value.toString()} {filter.type} {filter.must.toString()} @@ -93,7 +93,7 @@ const NewLifeCyclePolicySummary = {/*Summary access configuration*/} diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx index b3999085ce..db85b941f8 100644 --- a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx +++ b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx @@ -87,12 +87,11 @@ const NewLifeCyclePolicyWizard = ({ const fixedValues = { ...values, targetFilters: parseTargetFiltersForSubmit(values.targetFiltersArray), - accessControlEntries: values.acls + accessControlEntries: values.policies } if (fixedValues.action === "START_WORKFLOW") { fixedValues.actionParameters["workflowParameters"] = JSON.parse(values.actionParameters["workflowParameters"] as string) } - // values["accessControlEntries"] = values.acls; const response = dispatch(postNewLifeCyclePolicy(fixedValues)); console.info(response); close(); @@ -167,12 +166,13 @@ const getInitialValues = ( ) => { let initialValues = initialFormValuesNewLifeCyclePolicy; - initialValues["acls"] = [ + initialValues["policies"] = [ { role: user.userRole, read: true, write: true, actions: [], + user: user.user, }, ]; diff --git a/src/configs/modalConfig.ts b/src/configs/modalConfig.ts index eb93588838..219e26110d 100644 --- a/src/configs/modalConfig.ts +++ b/src/configs/modalConfig.ts @@ -229,7 +229,7 @@ export const initialFormValuesNewLifeCyclePolicy: { actionDate: string, cronTrigger: string, actionParameters: { [key: string]: unknown } - acls: TransformedAcl[] + policies: TransformedAcl[] targetFiltersArray: (TargetFilter & { filter: string })[], } = { title: "", @@ -245,6 +245,6 @@ export const initialFormValuesNewLifeCyclePolicy: { workflowParameters: `{"straightToPublishing": true}` }, - acls: [], + policies: [], targetFiltersArray: [], }; diff --git a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json index 2943dce112..c0a90d825d 100644 --- a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json +++ b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json @@ -1327,7 +1327,13 @@ }, "ACCESS": { "LABEL": "Select a template", - "DESCRIPTION": "At least one role with Read and Write permissions is required." + "DESCRIPTION": "At least one role with Read and Write permissions is required.", + "NON_USER_ROLES": "Roles and Groups authorized for the policy", + "ROLE": "Role", + "NEW": "New policy", + "USER": "User", + "USERS": "Users who are authorized for the policy", + "NEW_USER": "New user" }, "TAB": { "GENERAL": "General", From bbc759935e9353f06330558faec84a2097a10d16 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 17 Jun 2025 16:16:41 +0200 Subject: [PATCH 08/23] Validate lifecycle filters Ensure that lifecycle filters are porperly filled out before they can be saved. --- .../partials/wizards/LifeCyclePolicyGeneralFields.tsx | 2 +- src/utils/validate.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx index 5ad5a4fb6f..8767fe5ccd 100644 --- a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -312,7 +312,7 @@ const LifeCyclePolicyGeneralFields = timing === "REPEATING", then: () => Yup.string().required("Required"), }), + targetFiltersArray: Yup.array().of( + Yup.object().shape({ + filter: Yup.string().required('Required'), + value: Yup.mixed().required('Required'), + type: Yup.string().required('Required'), + must: Yup.boolean(), + }) + ), }), ]; From effe7893d11876bfaef4e55965924026e53caf03 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 5 Aug 2025 15:43:51 +0200 Subject: [PATCH 09/23] Fix eslint const instead of let --- src/components/events/LifeCyclePolicies.tsx | 2 +- .../ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx | 2 +- .../partials/wizards/LifeCyclePolicyGeneralFields.tsx | 4 +++- .../partials/wizards/NewLifeCyclePolicyWizard.tsx | 4 ++-- src/slices/lifeCycleDetailsSlice.ts | 10 +++++----- src/slices/lifeCycleSlice.ts | 4 ++-- src/thunks/tableThunks.ts | 2 +- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/events/LifeCyclePolicies.tsx b/src/components/events/LifeCyclePolicies.tsx index c26188548e..9cc04a70c5 100644 --- a/src/components/events/LifeCyclePolicies.tsx +++ b/src/components/events/LifeCyclePolicies.tsx @@ -56,7 +56,7 @@ const LifeCyclePolicies = () => { loadLifeCyclePolicies(); // Fetch policies repeatedly - let fetchInterval = setInterval(loadLifeCyclePolicies, 5000); + const fetchInterval = setInterval(loadLifeCyclePolicies, 5000); return () => { allowLoadIntoTable = false; diff --git a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx index f48959fe0d..4fdfaef157 100644 --- a/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/LifeCyclePolicyGeneralTab.tsx @@ -61,7 +61,7 @@ const LifeCyclePolicyGeneralTab = ({ // set current values of metadata fields as initial values const getInitialValues = (policy: LifeCyclePolicy) => { - let initialValues: LifeCyclePolicy & {workflowParameters: ConfigurationPanelField[], targetFiltersArray: (TargetFilter & { filter: string })[]} = { + const initialValues: LifeCyclePolicy & {workflowParameters: ConfigurationPanelField[], targetFiltersArray: (TargetFilter & { filter: string })[]} = { workflowParameters: [], targetFiltersArray: [], ...policy, diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx index 1fc6bf60fa..73d4b5184d 100644 --- a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -449,7 +449,9 @@ const DropdownField = ({ required={true} handleChange={element => { setFieldValue(clearFieldName, undefined); - element && setFieldValue(field.name, element.value); + if (element) { + setFieldValue(field.name, element.value); + } }} placeholder={`-- ${t("SELECT_NO_OPTION_SELECTED")} --`} creatable={creatable} diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx index a54cbcde47..391a06f26d 100644 --- a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx +++ b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx @@ -58,7 +58,7 @@ const NewLifeCyclePolicyWizard = ({ setSnapshot(values); // set page as completely filled out - let updatedPageCompleted = pageCompleted; + const updatedPageCompleted = pageCompleted; updatedPageCompleted[page] = true; setPageCompleted(updatedPageCompleted); @@ -164,7 +164,7 @@ const NewLifeCyclePolicyWizard = ({ const getInitialValues = ( user: UserInfoState, ) => { - let initialValues = initialFormValuesNewLifeCyclePolicy; + const initialValues = initialFormValuesNewLifeCyclePolicy; initialValues["policies"] = [ { diff --git a/src/slices/lifeCycleDetailsSlice.ts b/src/slices/lifeCycleDetailsSlice.ts index ea4a1ec7b5..a440168e03 100644 --- a/src/slices/lifeCycleDetailsSlice.ts +++ b/src/slices/lifeCycleDetailsSlice.ts @@ -50,7 +50,7 @@ export const fetchLifeCyclePolicyDetails = createAppAsyncThunk("lifeCyclePolicyD data.actionParameters = JSON.parse(data.actionParameters); data.targetFilters = JSON.parse(data.targetFilters); - let accessPolicies : { + const accessPolicies : { id: number, allow: boolean, role: string, @@ -59,8 +59,8 @@ export const fetchLifeCyclePolicyDetails = createAppAsyncThunk("lifeCyclePolicyD let acls: TransformedAcl[] = []; const json = accessPolicies; - let newPolicies: { [key: string]: TransformedAcl } = {}; - let policyRoles: string[] = []; + const newPolicies: { [key: string]: TransformedAcl } = {}; + const policyRoles: string[] = []; for (let i = 0; i < json.length; i++) { const policy: Ace = json[i]; if (!newPolicies[policy.role]) { @@ -114,7 +114,7 @@ export const updateLifeCyclePolicyAccess = createAppAsyncThunk("lifeCyclePolicyD }, { dispatch }) => { const { id, policies } = params; - let data = new URLSearchParams(); + const data = new URLSearchParams(); data.append("accessControlEntries", JSON.stringify(policies.acl.ace)); axios.put(`/api/lifecyclemanagement/policies/${id}`, data) @@ -131,7 +131,7 @@ export const updateLifeCyclePolicyAccess = createAppAsyncThunk("lifeCyclePolicyD }); export const updateLifeCyclePolicy = createAppAsyncThunk("lifeCyclePolicyDetails/updateLifeCyclePolicy", async (policy: LifeCyclePolicy, { dispatch }) => { - let data = new URLSearchParams(); + const data = new URLSearchParams(); Object.entries(policy).forEach(([key, value]) => { let stringified = value; diff --git a/src/slices/lifeCycleSlice.ts b/src/slices/lifeCycleSlice.ts index feffce53ba..ec78f38acd 100644 --- a/src/slices/lifeCycleSlice.ts +++ b/src/slices/lifeCycleSlice.ts @@ -65,7 +65,7 @@ const initialState: LifeCycleState = { export const fetchLifeCyclePolicies = createAppAsyncThunk("lifeCycle/fetchLifeCyclePolicies", async (_, { getState }) => { const state = getState(); - let params = getURLParams(state, "lifeCyclePolicies"); + const params = getURLParams(state, "lifeCyclePolicies"); const res = await axios.get("/api/lifecyclemanagement/policies", { params: params }); return res.data; }); @@ -85,7 +85,7 @@ export const postNewLifeCyclePolicy = createAppAsyncThunk("lifeCycle/postNewLife }, { dispatch }, ) => { - let data = new URLSearchParams(); + const data = new URLSearchParams(); // Format filter collections // for (const filterName in policy.targetFilters) { diff --git a/src/thunks/tableThunks.ts b/src/thunks/tableThunks.ts index 58ac5cf973..62275174af 100644 --- a/src/thunks/tableThunks.ts +++ b/src/thunks/tableThunks.ts @@ -127,7 +127,7 @@ export const loadLifeCyclePoliciesIntoTable = (): AppThunk => (dispatch, getStat const pages = calculatePages(total / pagination.limit, pagination.offset); - let tableData = { + const tableData = { resource: "lifeCyclePolicies" as const, rows: resource.map(obj => { return { ...obj, selected: false }; From 3294697d3fd009f4e2c1004deebd33a3a7c08d35 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 6 Aug 2025 11:14:03 +0200 Subject: [PATCH 10/23] Fix eslint for lifecycle --- src/components/events/LifeCyclePolicies.tsx | 3 --- .../wizards/LifeCyclePolicyGeneralFields.tsx | 13 +++++----- .../wizards/NewLifeCyclePolicySummary.tsx | 2 +- .../wizards/NewLifeCyclePolicyWizard.tsx | 3 ++- src/components/shared/wizard/RenderField.tsx | 13 +++------- src/slices/lifeCycleDetailsSlice.ts | 25 +++++++++++++------ src/slices/lifeCycleSlice.ts | 13 ++++++---- src/thunks/tableThunks.ts | 2 +- 8 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/components/events/LifeCyclePolicies.tsx b/src/components/events/LifeCyclePolicies.tsx index 9cc04a70c5..9ee5deecd1 100644 --- a/src/components/events/LifeCyclePolicies.tsx +++ b/src/components/events/LifeCyclePolicies.tsx @@ -40,9 +40,6 @@ const LifeCyclePolicies = () => { dispatch(fetchFilters("lifeCyclePolicies")); - // Reset text filter - dispatch(editTextFilter("")); - // Load policies on mount const loadLifeCyclePolicies = async () => { // Fetching policies from server diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx index 73d4b5184d..b583102dee 100644 --- a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -290,8 +290,9 @@ const LifeCyclePolicyGeneralFields = - - {({ replace, remove, push }) => ( + ( <> {Object.entries(formik.values.targetFiltersArray).map(([key, filter], index) => { return ( @@ -335,7 +336,7 @@ const LifeCyclePolicyGeneralFields = - ); - })} - - - - - - - )} - /> + + )} + /> + ); + })} @@ -473,7 +480,7 @@ const getTargetFilterRenderCollection = (filterName: string, targetFilterOptions return option !== undefined ? option.collection : undefined; }; -const WorkflowSelector = ({ +const WorkflowSelector = ({ formik, }: { formik: FormikProps, diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx index ab1d75c028..ded3484cf3 100644 --- a/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx +++ b/src/components/events/partials/wizards/NewLifeCyclePolicySummary.tsx @@ -63,13 +63,17 @@ const NewLifeCyclePolicySummary = {t("LIFECYCLE.POLICIES.DETAILS.GENERAL.TARGETFILTERS.CAPTION")} - {formik.values.targetFiltersArray.map((filter, key) => ( - - {filter.filter} - {filter.value.toString()} - {filter.type} - {filter.must.toString()} - + {Object.entries(formik.values.targetFiltersTransformed) + .filter(([outerKey]) => outerKey === "dublincore/episode") + .map(([_outerKey, filters]) => ( + filters.map((filter, key) => ( + + {filter.filter} + {filter.value.toString()} + {filter.type} + {filter.must.toString()} + + )) ))} diff --git a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx index 26213f4ebe..050b75e7f0 100644 --- a/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx +++ b/src/components/events/partials/wizards/NewLifeCyclePolicyWizard.tsx @@ -86,7 +86,7 @@ const NewLifeCyclePolicyWizard = ({ const handleSubmit = (values: typeof initialValues) => { const fixedValues = { ...values, - targetFilters: parseTargetFiltersForSubmit(values.targetFiltersArray), + targetFilters: parseTargetFiltersForSubmit(values.targetFiltersTransformed), accessControlEntries: values.policies, }; diff --git a/src/configs/modalConfig.ts b/src/configs/modalConfig.ts index 145f8325bc..708e613f9c 100644 --- a/src/configs/modalConfig.ts +++ b/src/configs/modalConfig.ts @@ -230,7 +230,7 @@ export const initialFormValuesNewLifeCyclePolicy: { cronTrigger: string, actionParameters: { [key: string]: unknown } policies: TransformedAcl[] - targetFiltersArray: (TargetFilter & { filter: string })[], + targetFiltersTransformed: { [key: string]: (TargetFilter & { filter: string })[] }, } = { title: "", isActive: true, @@ -247,5 +247,7 @@ export const initialFormValuesNewLifeCyclePolicy: { }, policies: [], - targetFiltersArray: [], + targetFiltersTransformed: { + "dublincore/episode": [], + }, }; diff --git a/src/slices/lifeCycleSlice.ts b/src/slices/lifeCycleSlice.ts index 8156145277..ed99509dc1 100644 --- a/src/slices/lifeCycleSlice.ts +++ b/src/slices/lifeCycleSlice.ts @@ -10,6 +10,7 @@ import { addNotification } from "./notificationSlice"; // type LifeCyclePolicyTiming = "SPECIFIC_DATE" | "REPEATING" | "ALWAYS"; // type LifeCyclePolicyAction = "START_WORKFLOW" // type LifeCyclePolicyTargetType = "EVENT" +export type CommonMetadataCatalogFlavor = "dublincore/episode"; // TODO: Get this from the backend export type TargetFilter = { value: string | string[], type: TargetFiltersType, @@ -30,7 +31,7 @@ export type LifeCyclePolicy = { isCreatedFromConfig: boolean, actionDate: string, // Date cronTrigger: string, - targetFilters: { [key: string]: TargetFilter }, + targetFilters: { [key: string]: { [key: string]: TargetFilter } }, accessControlEntries: TransformedAcl[] } @@ -85,7 +86,7 @@ export const postNewLifeCyclePolicy = createAppAsyncThunk("lifeCycle/postNewLife isActive: boolean, actionDate: string, cronTrigger: string, - targetFilters: { [key: string]: TargetFilter }, + targetFilters: { [key: string]: { [key: string]: TargetFilter } }, accessControlEntries: TransformedAcl[] }, { dispatch }, diff --git a/src/utils/lifeCycleUtils.ts b/src/utils/lifeCycleUtils.ts index cd7757c1c2..7de6597fcb 100644 --- a/src/utils/lifeCycleUtils.ts +++ b/src/utils/lifeCycleUtils.ts @@ -1,16 +1,38 @@ -import { LifeCyclePolicy, TargetFilter } from "../slices/lifeCycleSlice"; - -export const parseTargetFiltersForSubmit = ( - targetFiltersArray: (TargetFilter & { filter: string })[], -) => { - const targetFilters: LifeCyclePolicy["targetFilters"] = {}; - for (const filter of targetFiltersArray) { - targetFilters[filter.filter] = { - value: filter.value, - type: filter.type, - must: filter.must, - }; +import { TargetFilter } from "../slices/lifeCycleSlice"; + +export function parseTargetFiltersForSubmit( + transformed: { [key: string]: (TargetFilter & { filter: string })[] }, +): { [key: string]: { [key: string]: TargetFilter } } { + const result: { [key: string]: { [key: string]: TargetFilter } } = {}; + + for (const outerKey in transformed) { + const list = transformed[outerKey]; + const innerMap: { [key: string]: TargetFilter } = {}; + + for (const item of list) { + const { filter, ...rest } = item; + innerMap[filter] = rest; + } + + result[outerKey] = innerMap; + } + + return result; +} + +export function parseTargetFiltersForEditing( + targetFilters: { [key: string]: { [key: string]: TargetFilter } }, +): { [key: string]: (TargetFilter & { filter: string })[] } { + const result: { [key: string]: (TargetFilter & { filter: string })[] } = {}; + + for (const outerKey in targetFilters) { + const innerMap = targetFilters[outerKey]; + + result[outerKey] = Object.entries(innerMap).map(([innerKey, filterObj]) => ({ + ...filterObj, + filter: innerKey, + })); } - return targetFilters; + return result; }; From 47c03587d478bbd0ce2f06798e2603ece9784aae Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 2 Dec 2025 11:21:52 +0100 Subject: [PATCH 15/23] Fix issue with ":" in metadata dropdowns The displayed value for a selected value in a metadata dropdown would sometimes be cut off if the string for the value looked something like "Test.Test: 1". This fixes that. --- src/utils/resourceUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/resourceUtils.ts b/src/utils/resourceUtils.ts index f0143baa79..7213d4491e 100644 --- a/src/utils/resourceUtils.ts +++ b/src/utils/resourceUtils.ts @@ -304,7 +304,7 @@ export const getMetadataCollectionFieldName = ( return name.label ? t(name.label) : ""; } - return collectionField ? t(collectionField.name as ParseKeys) : ""; + return collectionField ? t(collectionField.name as ParseKeys, { nsSeparator: false }) : ""; } return ""; From dc3f98d2a9dfb92c324adff5c1192d40472e3f96 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 2 Dec 2025 11:27:07 +0100 Subject: [PATCH 16/23] Fix translation key --- src/slices/lifeCycleDetailsSlice.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slices/lifeCycleDetailsSlice.ts b/src/slices/lifeCycleDetailsSlice.ts index f531c7b79c..47fc9048a2 100644 --- a/src/slices/lifeCycleDetailsSlice.ts +++ b/src/slices/lifeCycleDetailsSlice.ts @@ -146,7 +146,7 @@ export const updateLifeCyclePolicyAccess = createAppAsyncThunk("lifeCyclePolicyD await axios.put(`/api/lifecyclemanagement/policies/${id}`, data) .then(response => { console.info(response); - dispatch(addNotification({ type: "success", key: "LIFECYCLEPOLICY_ADDED" })); + dispatch(addNotification({ type: "success", key: "LIFECYCLE_POLICY_ADDED" })); return true; }) .catch(response => { @@ -173,7 +173,7 @@ export const updateLifeCyclePolicy = createAppAsyncThunk("lifeCyclePolicyDetails await axios.put(`/api/lifecyclemanagement/policies/${policy.id}`, data) .then(response => { console.info(response); - dispatch(addNotification({ type: "success", key: "LIFECYCLEPOLICY_ADDED" })); + dispatch(addNotification({ type: "success", key: "LIFECYCLE_POLICY_ADDED" })); }) .catch(response => { console.error(response); From d645ff6c3f2cc79e2fd37f2ec5ec69074e47c26b Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 2 Dec 2025 11:47:11 +0100 Subject: [PATCH 17/23] Fix missing delete button for lifecycle policy filters --- .../wizards/LifeCyclePolicyGeneralFields.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx index 40fb61497d..1dd213e905 100644 --- a/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx +++ b/src/components/events/partials/wizards/LifeCyclePolicyGeneralFields.tsx @@ -14,6 +14,8 @@ import { getWorkflowDef } from "../../../../selectors/workflowSelectors"; import { fetchWorkflowDef } from "../../../../slices/workflowSlice"; import RenderWorkflowConfig, { Configuration } from "./RenderWorkflowConfig"; import { setDefaultConfig } from "../../../../utils/workflowPanelUtils"; +import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor"; +import { LuCircleX } from "react-icons/lu"; /** * This component renders the metadata page for new events and series in the wizards. @@ -381,17 +383,19 @@ const LifeCyclePolicyGeneralFields = -