diff --git a/packages/core/client/package.json b/packages/core/client/package.json index 299e51243c..4f34f438f6 100644 --- a/packages/core/client/package.json +++ b/packages/core/client/package.json @@ -25,6 +25,7 @@ "antd": "~4.19.5", "axios": "^0.26.1", "classnames": "^2.3.1", + "cron-parser": "^4.6.0", "cronstrue": "^2.11.0", "file-saver": "^2.0.5", "i18next": "^21.6.0", diff --git a/packages/core/client/src/locale/en_US.ts b/packages/core/client/src/locale/en_US.ts index 286f442aaf..66d71eb3b4 100644 --- a/packages/core/client/src/locale/en_US.ts +++ b/packages/core/client/src/locale/en_US.ts @@ -53,6 +53,12 @@ export default { "Form": "Form", "Select data source": "Select data source", "Calendar": "Calendar", + 'Delete events': 'Delete events', + 'This event': 'This event', + 'This and following events': 'This and following events', + 'All events': 'All events', + 'Delete this event?': 'Delete this event?', + 'Delete Event': 'Delete Event', "Kanban": "Kanban", "Select grouping field": "Select grouping field", "Media": "Media", diff --git a/packages/core/client/src/locale/zh_CN.ts b/packages/core/client/src/locale/zh_CN.ts index 2ff4e2d7df..9dd0db23cf 100644 --- a/packages/core/client/src/locale/zh_CN.ts +++ b/packages/core/client/src/locale/zh_CN.ts @@ -54,6 +54,12 @@ export default { "Form": "表单", "Select data source": "选择数据源", "Calendar": "日历", + 'Delete events': '删除日程', + 'This event': '此日程', + 'This and following events': '此日程及后续日程', + 'All events': '所有日程', + 'Delete this event?': '是否删除这个日程?', + 'Delete Event': '删除日程', "Kanban": "看板", "Select grouping field": "选择分组字段", "Media": "多媒体", diff --git a/packages/core/client/src/schema-component/antd/calendar/Calendar.tsx b/packages/core/client/src/schema-component/antd/calendar/Calendar.tsx index cdf0c1f4b9..8beeaf4c73 100644 --- a/packages/core/client/src/schema-component/antd/calendar/Calendar.tsx +++ b/packages/core/client/src/schema-component/antd/calendar/Calendar.tsx @@ -7,6 +7,7 @@ import React, { useMemo, useState } from 'react'; import { Calendar as BigCalendar, momentLocalizer } from 'react-big-calendar'; import * as dates from 'react-big-calendar/lib/utils/dates'; import { useTranslation } from 'react-i18next'; +import { parseExpression } from 'cron-parser'; import { RecordProvider } from '../../../'; import { i18n } from '../../../i18n'; import { useProps } from '../../hooks/useProps'; @@ -16,6 +17,8 @@ import { CalendarToolbarContext } from './context'; import './style.less'; import type { ToolbarProps } from './types'; +const Weeks = ['month', 'week', 'day'] as const; + const localizer = momentLocalizer(moment); function Toolbar(props: ToolbarProps) { @@ -62,22 +65,104 @@ const messages: any = { showMore: (count) => i18n.t('{{count}} more items', { count }), }; -const useEvents = (dataSource: any, fieldNames: any) => { +const useEvents = (dataSource: any, fieldNames: any, date: Date, view: typeof Weeks[number]) => { const { t } = useTranslation(); - return useMemo( - () => - Array.isArray(dataSource) - ? dataSource?.map((item) => { - return { - id: get(item, fieldNames.id || 'id'), - title: get(item, fieldNames.title) || t('Untitle'), - start: new Date(get(item, fieldNames.start)), - end: new Date(get(item, fieldNames.end || fieldNames.start)), - }; - }) - : [], - [dataSource, fieldNames], - ); + return useMemo(() => { + if (!Array.isArray(dataSource)) return []; + const events = []; + + dataSource.forEach((item) => { + const { cron, exclude = [] } = item; + const start = moment(get(item, fieldNames.start) || moment()); + const end = moment(get(item, fieldNames.end) || start); + const intervalTime = end.diff(start, 'millisecond', true); + + const dateM = moment(date); + const startDate = dateM.clone().startOf('month'); + const endDate = startDate.clone().endOf('month'); + if (view === 'month') { + startDate.startOf('week'); + endDate.endOf('week'); + } + const push = (fields?: Record) => { + // 必须在这个月的开始时间和结束时间,切在日程的开始时间之后 + const eventStart: moment.Moment = fields?.start || start; + if (eventStart.isBefore(start) || !eventStart.isBetween(startDate, endDate)) { + return; + } + const event = { + id: get(item, fieldNames.id || 'id'), + title: get(item, fieldNames.title) || t('Untitle'), + end: eventStart.add(intervalTime, 'millisecond'), + ...fields, + start: eventStart, + }; + + let out = false; + const res = exclude?.some((d) => { + if (d.endsWith('_after')) { + d = d.replace(/_after$/, ''); + out = true; + return event.start.isSameOrAfter(d); + } else { + return event.start.isSame(d); + } + }); + + if (res) return out; + events.push(event); + }; + + if (cron === 'every_week') { + let nextStart = start + .clone() + .year(startDate.year()) + .month(startDate.month()) + .date(startDate.date()) + .day(start.day()); + while (nextStart.isBefore(endDate)) { + if ( + push({ + start: nextStart.clone(), + }) + ) { + break; + } + nextStart.add(1, 'week'); + } + } else if (cron === 'every_month') { + push({ + start: start.clone().year(dateM.year()).month(dateM.month()), + }); + } else if (cron === 'every_year') { + push({ start: start.clone().year(dateM.year()) }); + } else { + push(); + if (!cron) return; + try { + const interval = parseExpression(cron, { + startDate: startDate.toDate(), + endDate: endDate.toDate(), + iterator: true, + currentDate: start.toDate(), + utc: true, + }); + while (interval.hasNext()) { + const { value } = interval.next(); + if ( + push({ + start: moment(value.toDate()), + }) + ) + break; + } + } catch (err) { + console.error(err); + } + } + }); + return events; + }, [dataSource, fieldNames, date, view]); }; const CalendarRecordViewer = (props) => { @@ -107,21 +192,27 @@ const CalendarRecordViewer = (props) => { export const Calendar: any = observer((props: any) => { const { dataSource, fieldNames, showLunar } = useProps(props); - const events = useEvents(dataSource, fieldNames); + const [date, setDate] = useState(new Date()); + const [view, setView] = useState('month'); + const events = useEvents(dataSource, fieldNames, date, view); const [visible, setVisible] = useState(false); const [record, setRecord] = useState({}); return ( -
+
{ console.log('onSelectSlot', slotInfo); }} @@ -133,6 +224,7 @@ export const Calendar: any = observer((props: any) => { if (!record) { return; } + record.__event = event; setRecord(record); setVisible(true); }} @@ -147,7 +239,6 @@ export const Calendar: any = observer((props: any) => { return `${local.format(start, 'Y-M', culture)} - ${local.format(end, 'Y-M', culture)}`; }, }} - defaultDate={new Date()} components={{ toolbar: (props) => , week: { diff --git a/packages/core/client/src/schema-component/antd/calendar/DeleteEvent.tsx b/packages/core/client/src/schema-component/antd/calendar/DeleteEvent.tsx new file mode 100644 index 0000000000..cb688a0365 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/calendar/DeleteEvent.tsx @@ -0,0 +1,66 @@ +import { observer } from '@formily/react'; +import { Modal, Radio, Space, Typography } from 'antd'; +import React, { useState } from 'react'; +import { createPortal } from 'react-dom'; +import { useTranslation } from 'react-i18next'; +import { useBlockRequestContext, useFilterByTk } from '../../../block-provider'; +import { useRecord } from '../../../record-provider'; +import { useActionContext } from '../action'; +const { Text } = Typography; + +export const DeleteEvent = observer(() => { + const { visible, setVisible } = useActionContext(); + const { exclude = [], cron, ...record } = useRecord(); + const startDate = record.__parent.__event.start.format(); + const filterByTk = useFilterByTk(); + const { resource, service, __parent } = useBlockRequestContext(); + const [value, onChange] = useState(startDate); + const [loading, setLoading] = useState(false); + const onOk = async () => { + setLoading(true); + if (value === 'all' || !cron) { + await resource.destroy({ + filterByTk, + }); + } else { + await resource.update({ + filterByTk, + values: { + exclude: (exclude || []).concat(value), + }, + }); + } + setLoading(false); + __parent?.service?.refresh?.(); + service?.refresh?.(); + setVisible?.(false, true); + }; + + const { t } = useTranslation(); + return createPortal( + setVisible(false)} + onOk={() => onOk()} + confirmLoading={loading} + > + {cron ? ( + onChange(event.target.value)}> + + {t('This event')} + {t('This and following events')} + {t('All events')} + + + ) : ( + + {t('Delete this event?')} + + )} + , + document.body, + ); +}); + +export default DeleteEvent; diff --git a/packages/core/client/src/schema-component/antd/calendar/index.ts b/packages/core/client/src/schema-component/antd/calendar/index.ts index d844deb549..b01d255cfb 100644 --- a/packages/core/client/src/schema-component/antd/calendar/index.ts +++ b/packages/core/client/src/schema-component/antd/calendar/index.ts @@ -1,3 +1,4 @@ +import DeleteEvent from './DeleteEvent'; import { ActionBar } from '../action'; import { Calendar } from './Calendar'; import { CalendarDesigner } from './Calendar.Designer'; @@ -10,6 +11,7 @@ import { ViewSelect } from './ViewSelect'; Calendar.ActionBar = ActionBar; Calendar.Event = Event; +Calendar.DeleteEvent = DeleteEvent; Calendar.Title = Title; Calendar.Today = Today; Calendar.Nav = Nav; diff --git a/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx new file mode 100644 index 0000000000..a7cbbe7d29 --- /dev/null +++ b/packages/core/client/src/schema-initializer/buttons/CalendarFormActionInitializers.tsx @@ -0,0 +1,160 @@ +// 表单的操作配置 +export const CalendarFormActionInitializers = { + title: '{{t("Configure actions")}}', + icon: 'SettingOutlined', + style: { + marginLeft: 8, + }, + items: [ + { + type: 'itemGroup', + title: '{{t("Enable actions")}}', + children: [ + { + type: 'item', + title: '{{t("Edit")}}', + component: 'UpdateActionInitializer', + schema: { + 'x-component': 'Action', + 'x-decorator': 'ACLActionProvider', + 'x-component-props': { + type: 'primary', + }, + }, + }, + { + type: 'item', + title: '{{t("Delete")}}', + component: 'DestroyActionInitializer', + schema: { + 'x-component': 'Action', + 'x-decorator': 'ACLActionProvider', + }, + }, + { + type: 'item', + title: '{{t("Delete Event")}}', + component: 'DeleteEventActionInitializer', + schema: { + 'x-component': 'Action', + 'x-decorator': 'ACLActionProvider', + }, + }, + { + type: 'item', + title: '{{t("Print")}}', + component: 'PrintActionInitializer', + schema: { + 'x-component': 'Action', + 'x-decorator': 'ACLActionProvider', + }, + }, + ], + }, + + { + type: 'divider', + }, + { + type: 'subMenu', + title: '{{t("Customize")}}', + children: [ + { + type: 'item', + title: '{{t("Popup")}}', + component: 'CustomizeActionInitializer', + schema: { + type: 'void', + title: '{{ t("Popup") }}', + 'x-action': 'customize:popup', + 'x-designer': 'Action.Designer', + 'x-component': 'Action', + 'x-component-props': { + openMode: 'drawer', + }, + properties: { + drawer: { + type: 'void', + title: '{{ t("Popup") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'TabPaneInitializers', + properties: { + tab1: { + type: 'void', + title: '{{t("Details")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'RecordBlockInitializers', + properties: {}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + type: 'item', + title: '{{t("Update record")}}', + component: 'CustomizeActionInitializer', + schema: { + title: '{{ t("Update record") }}', + 'x-component': 'Action', + 'x-designer': 'Action.Designer', + 'x-action': 'customize:update', + 'x-action-settings': { + assignedValues: {}, + onSuccess: { + manualClose: true, + redirecting: false, + successMessage: '{{t("Updated successfully")}}', + }, + }, + 'x-component-props': { + useProps: '{{ useCustomizeUpdateActionProps }}', + }, + }, + }, + { + type: 'item', + title: '{{t("Custom request")}}', + component: 'CustomizeActionInitializer', + schema: { + title: '{{ t("Custom request") }}', + 'x-component': 'Action', + 'x-action': 'customize:form:request', + 'x-designer': 'Action.Designer', + 'x-action-settings': { + requestSettings: {}, + skipValidator: false, + onSuccess: { + manualClose: false, + redirecting: false, + successMessage: '{{t("Request success")}}', + }, + }, + 'x-component-props': { + useProps: '{{ useCustomizeRequestActionProps }}', + }, + }, + }, + ], + }, + ], +}; diff --git a/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx index 52202d4886..31a6b8aa65 100644 --- a/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx @@ -12,13 +12,13 @@ const recursiveParent = (schema: Schema) => { } else { return recursiveParent(schema.parent); } -} +}; const useRelationFields = () => { const fieldSchema = useFieldSchema(); const { getCollectionFields } = useCollectionManager(); let fields = []; - + if (fieldSchema['x-initializer']) { fields = useCollection().fields; } else { @@ -52,7 +52,7 @@ const useRelationFields = () => { // component: 'RecordAssociationFormBlockInitializer', // }, ], - } + }; } if (['hasMany', 'belongsToMany'].includes(field.type)) { @@ -90,7 +90,7 @@ const useRelationFields = () => { component: 'RecordAssociationCalendarBlockInitializer', }, ], - } + }; } return { @@ -101,7 +101,7 @@ const useRelationFields = () => { component: 'RecordAssociationBlockInitializer', }; }) as any; - return relationFields; + return relationFields; }; export const RecordBlockInitializers = (props: any) => { @@ -124,6 +124,7 @@ export const RecordBlockInitializers = (props: any) => { type: 'item', title: '{{t("Details")}}', component: 'RecordReadPrettyFormBlockInitializer', + actionInitializers: 'CalendarFormActionInitializers', }, { key: 'form', diff --git a/packages/core/client/src/schema-initializer/buttons/index.ts b/packages/core/client/src/schema-initializer/buttons/index.ts index 75b1e03ed6..d8cfc76d2a 100644 --- a/packages/core/client/src/schema-initializer/buttons/index.ts +++ b/packages/core/client/src/schema-initializer/buttons/index.ts @@ -9,6 +9,7 @@ export * from './FormActionInitializers'; export * from './FormItemInitializers'; export * from './KanbanActionInitializers'; export * from './ReadPrettyFormActionInitializers'; +export * from './CalendarFormActionInitializers'; export * from './ReadPrettyFormItemInitializers'; export * from './RecordBlockInitializers'; export * from './RecordFormBlockInitializers'; @@ -18,4 +19,3 @@ export * from './TableActionInitializers'; export * from './TableColumnInitializers'; export * from './TableSelectorInitializers'; export * from './TabPaneInitializers'; - diff --git a/packages/core/client/src/schema-initializer/items/DeleteEventActionInitializer.tsx b/packages/core/client/src/schema-initializer/items/DeleteEventActionInitializer.tsx new file mode 100644 index 0000000000..cfcd275bed --- /dev/null +++ b/packages/core/client/src/schema-initializer/items/DeleteEventActionInitializer.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +import { ActionInitializer } from './ActionInitializer'; + +export const DeleteEventActionInitializer = (props) => { + const schema = { + title: '{{ t("Delete Event") }}', + 'x-action': 'deleteEvent', + 'x-component': 'Action', + 'x-designer': 'Action.Designer', + 'x-component-props': { + icon: 'DeleteOutlined', + }, + properties: { + modal: { + 'x-component': 'CalendarV2.DeleteEvent', + }, + }, + }; + return ; +}; diff --git a/packages/core/client/src/schema-initializer/items/RecordReadPrettyFormBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordReadPrettyFormBlockInitializer.tsx index 3dc1a7c741..4081d84bd2 100644 --- a/packages/core/client/src/schema-initializer/items/RecordReadPrettyFormBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordReadPrettyFormBlockInitializer.tsx @@ -1,59 +1,60 @@ -import React from "react"; +import React from 'react'; import { FormOutlined } from '@ant-design/icons'; -import { useBlockAssociationContext, useBlockRequestContext } from "../../block-provider"; -import { useCollection } from "../../collection-manager"; -import { useSchemaTemplateManager } from "../../schema-templates"; -import { SchemaInitializer } from "../SchemaInitializer"; -import { createReadPrettyFormBlockSchema, useRecordCollectionDataSourceItems } from "../utils"; +import { useBlockAssociationContext, useBlockRequestContext } from '../../block-provider'; +import { useCollection } from '../../collection-manager'; +import { useSchemaTemplateManager } from '../../schema-templates'; +import { SchemaInitializer } from '../SchemaInitializer'; +import { createReadPrettyFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; export const RecordReadPrettyFormBlockInitializer = (props) => { - const { onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props; + const { onCreateBlockSchema, componentType, createBlockSchema, insert, ...others } = props; - const { getTemplateSchemaByMode } = useSchemaTemplateManager(); - const collection = useCollection(); - const association = useBlockAssociationContext(); - const { block } = useBlockRequestContext(); - const actionInitializers = block !== 'TableField' ? 'ReadPrettyFormActionInitializers' : null; + const { getTemplateSchemaByMode } = useSchemaTemplateManager(); + const collection = useCollection(); + const association = useBlockAssociationContext(); + const { block } = useBlockRequestContext(); + const actionInitializers = + block !== 'TableField' ? props.actionInitializers || 'ReadPrettyFormActionInitializers' : null; - return ( - } - {...others} - key={'123'} - onClick={async ({ item }) => { - if (item.template) { - const s = await getTemplateSchemaByMode(item); - if (item.template.componentName === 'ReadPrettyFormItem') { - const blockSchema = createReadPrettyFormBlockSchema({ - actionInitializers, - association, - collection: collection.name, - action: 'get', - useSourceId: '{{ useSourceIdFromParentRecord }}', - useParams: '{{ useParamsFromRecord }}', - template: s, - }); - if (item.mode === 'reference') { - blockSchema['x-template-key'] = item.template.key; - } - insert(blockSchema); - } else { - insert(s); + return ( + } + {...others} + key={'123'} + onClick={async ({ item }) => { + if (item.template) { + const s = await getTemplateSchemaByMode(item); + if (item.template.componentName === 'ReadPrettyFormItem') { + const blockSchema = createReadPrettyFormBlockSchema({ + actionInitializers, + association, + collection: collection.name, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + template: s, + }); + if (item.mode === 'reference') { + blockSchema['x-template-key'] = item.template.key; } + insert(blockSchema); } else { - insert( - createReadPrettyFormBlockSchema({ - actionInitializers, - association, - collection: collection.name, - action: 'get', - useSourceId: '{{ useSourceIdFromParentRecord }}', - useParams: '{{ useParamsFromRecord }}', - }), - ); + insert(s); } - }} - items={useRecordCollectionDataSourceItems('ReadPrettyFormItem')} - /> - ); - }; + } else { + insert( + createReadPrettyFormBlockSchema({ + actionInitializers, + association, + collection: collection.name, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + }), + ); + } + }} + items={useRecordCollectionDataSourceItems('ReadPrettyFormItem')} + /> + ); +}; diff --git a/packages/core/client/src/schema-initializer/items/index.tsx b/packages/core/client/src/schema-initializer/items/index.tsx index ab7613381f..64d99cef75 100644 --- a/packages/core/client/src/schema-initializer/items/index.tsx +++ b/packages/core/client/src/schema-initializer/items/index.tsx @@ -12,6 +12,7 @@ export * from './CustomizeActionInitializer'; export * from './CustomizeBulkEditActionInitializer'; export * from './DataBlockInitializer'; export * from './DestroyActionInitializer'; +export * from './DeleteEventActionInitializer'; export * from './DetailsBlockInitializer'; export * from './FilterActionInitializer'; export * from './FormBlockInitializer'; @@ -36,4 +37,3 @@ export * from './TableSelectorInitializer'; export * from './UpdateActionInitializer'; export * from './UpdateSubmitActionInitializer'; export * from './ViewActionInitializer'; - diff --git a/yarn.lock b/yarn.lock index 6bc0a9be73..407580e3f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5352,7 +5352,14 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@^16.9.8", "@types/react-dom@^17.0.0": +"@types/react-dom@^16.9.8": + version "16.9.17" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.17.tgz#29100cbcc422d7b7dba7de24bb906de56680dd34" + integrity sha512-qSRyxEsrm5btPXnowDOs5jSkgT8ldAA0j6Qp+otHUh+xHzy3sXmgNfyhucZjAjkgpdAUw9rJe0QRtX/l+yaS4g== + dependencies: + "@types/react" "^16" + +"@types/react-dom@^17.0.0": version "17.0.11" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== @@ -5412,7 +5419,7 @@ "@types/history" "*" "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.11", "@types/react@^16.9.43", "@types/react@^17.0.0": +"@types/react@*", "@types/react@>=16.9.11", "@types/react@^17.0.0": version "17.0.34" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102" integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg== @@ -5421,6 +5428,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^16", "@types/react@^16.9.43": + version "16.14.34" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.34.tgz#d129324ffda312044e1c47aab18696e4ed493282" + integrity sha512-b99nWeGGReLh6aKBppghVqp93dFJtgtDOzc8NXM6hewD8PQ2zZG5kBLgbx+VJr7Q7WBMjHxaIl3dwpwwPIUgyA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -8718,6 +8734,13 @@ cron-parser@4.4.0: dependencies: luxon "^1.28.0" +cron-parser@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.6.0.tgz#404c3fdbff10ae80eef6b709555d577ef2fd2e0d" + integrity sha512-guZNLMGUgg6z4+eGhmHGw7ft+v6OQeuHzd1gcLxCo9Yg/qoxmG3nindp2/uwGCLizEisf2H0ptqeVXeoCpP6FA== + dependencies: + luxon "^3.0.1" + croner@~4.1.92: version "4.1.97" resolved "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz#6e373dc7bb3026fab2deb0d82685feef20796766" @@ -14872,6 +14895,11 @@ luxon@^1.28.0: resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== +luxon@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.0.4.tgz#d179e4e9f05e092241e7044f64aaa54796b03929" + integrity sha512-aV48rGUwP/Vydn8HT+5cdr26YYQiUZ42NM6ToMoaGKwYfWbfLeRkEu1wXWMHBZT6+KyLfcbbtVcoQFCbbPjKlw== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"