mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 03:43:50 +00:00
Improve xlsx import (#1271)
* use xlsx * add existing custom fields to import map * keep colours of existing custom fields * use unknown * fix * revert * allow space and upper case * fix test * define empty sheet
This commit is contained in:
committed by
GitHub
parent
59c1c77d48
commit
42984eed87
+5
-5
@@ -3,9 +3,8 @@ import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { Button, IconButton, Input, Select, Tooltip } from '@chakra-ui/react';
|
||||
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
|
||||
import { IoTrash } from '@react-icons/all-files/io5/IoTrash';
|
||||
import { ImportMap } from 'ontime-utils';
|
||||
import { ImportMap, isAlphanumericWithSpace } from 'ontime-utils';
|
||||
|
||||
import { isAlphanumeric } from '../../../../../common/utils/regex';
|
||||
import * as Panel from '../../PanelUtils';
|
||||
import useGoogleSheet from '../useGoogleSheet';
|
||||
import { useSheetStore } from '../useSheetStore';
|
||||
@@ -190,9 +189,10 @@ export default function ImportMapForm(props: ImportMapFormProps) {
|
||||
defaultValue={ontimeName}
|
||||
placeholder='Name of the field as shown in Ontime'
|
||||
{...register(`custom.${index}.ontimeName`, {
|
||||
pattern: {
|
||||
value: isAlphanumeric,
|
||||
message: 'Custom field name must be alphanumeric',
|
||||
validate: (value) => {
|
||||
if (!isAlphanumericWithSpace(value))
|
||||
return 'Only alphanumeric characters and space are allowed';
|
||||
return true;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
+5
-4
@@ -24,7 +24,8 @@ export default function PreviewRundown(props: PreviewRundownProps) {
|
||||
// we only count Ontime Events which are 1 based in client
|
||||
let eventIndex = 0;
|
||||
|
||||
const fieldHeaders = Object.keys(customFields);
|
||||
const fieldKeys = Object.keys(customFields);
|
||||
const fieldLabels = fieldKeys.map((key) => customFields[key].label);
|
||||
|
||||
return (
|
||||
<Panel.Table>
|
||||
@@ -44,8 +45,8 @@ export default function PreviewRundown(props: PreviewRundownProps) {
|
||||
<th>Colour</th>
|
||||
<th>Timer Type</th>
|
||||
<th>End Action</th>
|
||||
{fieldHeaders.map((field) => (
|
||||
<th key={field}>{field}</th>
|
||||
{fieldLabels.map((label) => (
|
||||
<th key={label}>{label}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -102,7 +103,7 @@ export default function PreviewRundown(props: PreviewRundownProps) {
|
||||
<Tag>{event.endAction}</Tag>
|
||||
</td>
|
||||
{isOntimeEvent(event) &&
|
||||
fieldHeaders.map((field) => {
|
||||
fieldKeys.map((field) => {
|
||||
let value = '';
|
||||
if (field in event.custom) {
|
||||
value = event.custom[field];
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
"lowdb": "^7.0.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-osc": "^9.0.2",
|
||||
"node-xlsx": "^0.23.0",
|
||||
"ontime-utils": "workspace:*",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"steno": "^4.0.2",
|
||||
"ws": "^8.13.0"
|
||||
"ws": "^8.13.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.17",
|
||||
|
||||
@@ -8,13 +8,15 @@ import { ImportMap } from 'ontime-utils';
|
||||
|
||||
import { extname } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import xlsx from 'node-xlsx';
|
||||
import xlsx from 'xlsx';
|
||||
import type { WorkBook } from 'xlsx';
|
||||
|
||||
import { parseExcel } from '../../utils/parser.js';
|
||||
import { parseRundown } from '../../utils/parserFunctions.js';
|
||||
import { deleteFile } from '../../utils/parserUtils.js';
|
||||
import { getCustomFields } from '../../services/rundown-service/rundownCache.js';
|
||||
|
||||
let excelData: { name: string; data: unknown[][] }[] = [];
|
||||
let excelData: WorkBook = xlsx.utils.book_new();
|
||||
|
||||
export async function saveExcelFile(filePath: string) {
|
||||
if (!existsSync(filePath)) {
|
||||
@@ -23,24 +25,25 @@ export async function saveExcelFile(filePath: string) {
|
||||
if (extname(filePath) != '.xlsx') {
|
||||
throw new Error('Wrong file format');
|
||||
}
|
||||
excelData = xlsx.parse(filePath, { cellDates: true });
|
||||
excelData = xlsx.readFile(filePath, { cellDates: true, cellFormula: false });
|
||||
|
||||
await deleteFile(filePath);
|
||||
}
|
||||
|
||||
export function listWorksheets() {
|
||||
return excelData.map((value) => value.name);
|
||||
export function listWorksheets(): string[] {
|
||||
return excelData.SheetNames;
|
||||
}
|
||||
|
||||
export function generateRundownPreview(options: ImportMap): { rundown: OntimeRundown; customFields: CustomFields } {
|
||||
const data = excelData.find(({ name }) => name.toLowerCase() === options.worksheet.toLowerCase())?.data;
|
||||
const data = excelData.Sheets[options.worksheet];
|
||||
|
||||
if (!data) {
|
||||
throw new Error(`Could not find data to import, maybe the worksheet name is incorrect: ${options.worksheet}`);
|
||||
}
|
||||
|
||||
const dataFromExcel = parseExcel(data, options);
|
||||
const arrayOfData: unknown[][] = xlsx.utils.sheet_to_json(data, { header: 1, blankrows: false, raw: false });
|
||||
|
||||
const dataFromExcel = parseExcel(arrayOfData, getCustomFields(), options);
|
||||
// we run the parsed data through an extra step to ensure the objects shape
|
||||
const { rundown, customFields } = parseRundown(dataFromExcel);
|
||||
if (rundown.length === 0) {
|
||||
@@ -48,7 +51,7 @@ export function generateRundownPreview(options: ImportMap): { rundown: OntimeRun
|
||||
}
|
||||
|
||||
// clear the data
|
||||
excelData = [];
|
||||
excelData = undefined;
|
||||
|
||||
return { rundown, customFields };
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { parseExcel } from '../../utils/parser.js';
|
||||
import { logger } from '../../classes/Logger.js';
|
||||
import { parseRundown } from '../../utils/parserFunctions.js';
|
||||
import { getRundown } from '../rundown-service/rundownUtils.js';
|
||||
import { getCustomFields } from '../rundown-service/rundownCache.js';
|
||||
|
||||
import { cellRequestFromEvent, type ClientSecret, getA1Notation, validateClientSecret } from './sheetUtils.js';
|
||||
|
||||
@@ -286,7 +287,7 @@ export async function upload(sheetId: string, options: ImportMap) {
|
||||
throw new Error(`Sheet read failed: ${readResponse.statusText}`);
|
||||
}
|
||||
|
||||
const { rundownMetadata } = parseExcel(readResponse.data.values, options);
|
||||
const { rundownMetadata } = parseExcel(readResponse.data.values, getCustomFields(), options);
|
||||
const rundown = getRundown();
|
||||
const titleRow = Object.values(rundownMetadata)[0]['row'];
|
||||
const updateRundown = Array<sheets_v4.Schema$Request>();
|
||||
@@ -363,7 +364,7 @@ export async function download(
|
||||
throw new Error(`Sheet read failed: ${googleResponse.statusText}`);
|
||||
}
|
||||
|
||||
const dataFromSheet = parseExcel(googleResponse.data.values, options);
|
||||
const dataFromSheet = parseExcel(googleResponse.data.values, getCustomFields(), options);
|
||||
const { customFields, rundown } = parseRundown(dataFromSheet);
|
||||
if (rundown.length < 1) {
|
||||
throw new Error('Sheet: Could not find data to import in the worksheet');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { assertType, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
CustomFields,
|
||||
DatabaseModel,
|
||||
EndAction,
|
||||
OntimeEvent,
|
||||
@@ -788,7 +789,7 @@ describe('getCustomFieldData()', () => {
|
||||
},
|
||||
} as ImportMap;
|
||||
|
||||
const result = getCustomFieldData(importMap);
|
||||
const result = getCustomFieldData(importMap, {});
|
||||
expect(result.customFields).toStrictEqual({
|
||||
lighting: {
|
||||
type: 'string',
|
||||
@@ -807,6 +808,61 @@ describe('getCustomFieldData()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
// it is an inverted record of <importKey, ontimeKey>
|
||||
expect(result.customFieldImportKeys).toStrictEqual({
|
||||
lx: 'lighting',
|
||||
sound: 'sound',
|
||||
av: 'video',
|
||||
});
|
||||
});
|
||||
it('keeps colour information from existing fields', () => {
|
||||
const importMap = {
|
||||
worksheet: 'event schedule',
|
||||
timeStart: 'time start',
|
||||
linkStart: 'link start',
|
||||
timeEnd: 'time end',
|
||||
duration: 'duration',
|
||||
cue: 'cue',
|
||||
title: 'title',
|
||||
isPublic: 'public',
|
||||
skip: 'skip',
|
||||
note: 'notes',
|
||||
colour: 'colour',
|
||||
endAction: 'end action',
|
||||
timerType: 'timer type',
|
||||
timeWarning: 'warning time',
|
||||
timeDanger: 'danger time',
|
||||
custom: {
|
||||
lighting: 'lx',
|
||||
sound: 'sound',
|
||||
video: 'av',
|
||||
},
|
||||
} as ImportMap;
|
||||
|
||||
const customFields: CustomFields = {
|
||||
lighting: { label: 'lx', type: 'string', colour: 'red' },
|
||||
sound: { label: 'sound', type: 'string', colour: 'green' },
|
||||
};
|
||||
|
||||
const result = getCustomFieldData(importMap, customFields);
|
||||
expect(result.customFields).toStrictEqual({
|
||||
lighting: {
|
||||
type: 'string',
|
||||
colour: 'red',
|
||||
label: 'lighting',
|
||||
},
|
||||
sound: {
|
||||
type: 'string',
|
||||
colour: 'green',
|
||||
label: 'sound',
|
||||
},
|
||||
video: {
|
||||
type: 'string',
|
||||
colour: '',
|
||||
label: 'video',
|
||||
},
|
||||
});
|
||||
|
||||
// it is an inverted record of <importKey, ontimeKey>
|
||||
expect(result.customFieldImportKeys).toStrictEqual({
|
||||
lx: 'lighting',
|
||||
@@ -919,7 +975,7 @@ describe('parseExcel()', () => {
|
||||
note: 'Ballyhoo',
|
||||
custom: {
|
||||
user0: 'a0',
|
||||
user1: 'a1',
|
||||
User1: 'a1',
|
||||
user2: 'a2',
|
||||
user3: 'a3',
|
||||
user4: 'a4',
|
||||
@@ -952,21 +1008,27 @@ describe('parseExcel()', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const parsedData = parseExcel(testdata, importMap);
|
||||
const existingCustomFields: CustomFields = {
|
||||
user0: { type: 'string', colour: 'red', label: 'user0' },
|
||||
User1: { type: 'string', colour: 'green', label: 'user1' },
|
||||
user2: { type: 'string', colour: 'blue', label: 'user2' },
|
||||
};
|
||||
|
||||
const parsedData = parseExcel(testdata, existingCustomFields, importMap);
|
||||
expect(parsedData.customFields).toStrictEqual({
|
||||
user0: {
|
||||
type: 'string',
|
||||
colour: '',
|
||||
colour: 'red',
|
||||
label: 'user0',
|
||||
},
|
||||
user1: {
|
||||
User1: {
|
||||
type: 'string',
|
||||
colour: '',
|
||||
colour: 'green',
|
||||
label: 'User1',
|
||||
},
|
||||
user2: {
|
||||
type: 'string',
|
||||
colour: '',
|
||||
colour: 'blue',
|
||||
label: 'user2',
|
||||
},
|
||||
user3: {
|
||||
@@ -1123,7 +1185,7 @@ describe('parseExcel()', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const parsedData = parseExcel(testdata, importMap);
|
||||
const parsedData = parseExcel(testdata, {}, importMap);
|
||||
expect(parsedData.customFields).toStrictEqual({
|
||||
niu1: {
|
||||
type: 'string',
|
||||
@@ -1229,7 +1291,7 @@ describe('parseExcel()', () => {
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
const result = parseExcel(testdata, {}, importMap);
|
||||
expect(result.rundown.length).toBe(1);
|
||||
expect((result.rundown.at(0) as OntimeEvent).title).toBe('A song from the hearth');
|
||||
});
|
||||
@@ -1322,7 +1384,7 @@ describe('parseExcel()', () => {
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
const result = parseExcel(testdata, {}, importMap);
|
||||
expect(result.rundown.length).toBe(2);
|
||||
expect((result.rundown.at(0) as OntimeEvent).type).toBe(SupportedEvent.Block);
|
||||
});
|
||||
@@ -1392,7 +1454,7 @@ describe('parseExcel()', () => {
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
const result = parseExcel(testdata, {}, importMap);
|
||||
expect(result.rundown.length).toBe(2);
|
||||
expect((result.rundown.at(0) as OntimeEvent).type).toBe(SupportedEvent.Event);
|
||||
expect((result.rundown.at(0) as OntimeEvent).timerType).toBe(TimerType.CountDown);
|
||||
@@ -1510,7 +1572,7 @@ describe('parseExcel()', () => {
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
const result = parseExcel(testdata, {}, importMap);
|
||||
expect(result.rundown.length).toBe(3);
|
||||
expect((result.rundown.at(0) as OntimeEvent).type).toBe(SupportedEvent.Event);
|
||||
expect((result.rundown.at(0) as OntimeEvent).timerType).toBe(TimerType.CountDown);
|
||||
@@ -1551,7 +1613,7 @@ describe('parseExcel()', () => {
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testData, importMap);
|
||||
const result = parseExcel(testData, {}, importMap);
|
||||
const { rundown } = parseRundown(result);
|
||||
const events = rundown.filter((e) => e.type === SupportedEvent.Event) as OntimeEvent[];
|
||||
expect((events.at(0) as OntimeEvent).timeStart).toEqual(16200000);
|
||||
@@ -1588,7 +1650,7 @@ describe('parseExcel()', () => {
|
||||
custom: {},
|
||||
};
|
||||
|
||||
const result = parseExcel(testData, importMap);
|
||||
const result = parseExcel(testData, {}, importMap);
|
||||
const { rundown } = parseRundown(result);
|
||||
const events = rundown.filter((e) => e.type === SupportedEvent.Event) as OntimeEvent[];
|
||||
expect((events.at(0) as OntimeEvent).timeStart).toEqual(16200000); //<--leading white space in MAP
|
||||
@@ -1640,7 +1702,7 @@ describe('parseExcel()', () => {
|
||||
custom: {},
|
||||
};
|
||||
|
||||
const result = parseExcel(testData, importMap);
|
||||
const result = parseExcel(testData, {}, importMap);
|
||||
const parseResult = parseRundown(result);
|
||||
|
||||
cache.init(parseResult.rundown, parseResult.customFields);
|
||||
@@ -1774,7 +1836,7 @@ describe('parseExcel()', () => {
|
||||
['MEET4', '#779BE7', '', '', 30, true, 'Meeting 4', '', 'count-up', 'none', 11, '00:05:00', 'TRUE', 'FALSE'],
|
||||
];
|
||||
|
||||
const parsedData = parseExcel(testData);
|
||||
const parsedData = parseExcel(testData, {});
|
||||
const { rundown } = parsedData;
|
||||
|
||||
// elements in bug report
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
customFieldLabelToKey,
|
||||
defaultImportMap,
|
||||
generateId,
|
||||
type ImportMap,
|
||||
@@ -54,18 +55,22 @@ function parseBooleanString(value: unknown): boolean {
|
||||
return value.toLowerCase() !== 'false';
|
||||
}
|
||||
|
||||
export function getCustomFieldData(importMap: ImportMap): {
|
||||
export function getCustomFieldData(
|
||||
importMap: ImportMap,
|
||||
existingCustomFields: CustomFields,
|
||||
): {
|
||||
customFields: CustomFields;
|
||||
customFieldImportKeys: Record<keyof CustomFields, string>;
|
||||
} {
|
||||
const customFields = {};
|
||||
const customFieldImportKeys = {};
|
||||
for (const ontimeLabel in importMap.custom) {
|
||||
const ontimeKey = ontimeLabel.toLowerCase();
|
||||
const ontimeKey = customFieldLabelToKey(ontimeLabel);
|
||||
const importLabel = importMap.custom[ontimeLabel].toLowerCase();
|
||||
const colour = ontimeKey in existingCustomFields ? existingCustomFields[ontimeKey].colour : '';
|
||||
customFields[ontimeKey] = {
|
||||
type: 'string',
|
||||
colour: '',
|
||||
colour,
|
||||
label: ontimeLabel,
|
||||
};
|
||||
customFieldImportKeys[importLabel] = ontimeKey;
|
||||
@@ -79,7 +84,11 @@ export function getCustomFieldData(importMap: ImportMap): {
|
||||
* @param {ImportOptions} options - an object that contains the import map
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
export const parseExcel = (excelData: unknown[][], options?: Partial<ImportMap>): ExcelData => {
|
||||
export const parseExcel = (
|
||||
excelData: unknown[][],
|
||||
existingCustomFields: CustomFields,
|
||||
options?: Partial<ImportMap>,
|
||||
): ExcelData => {
|
||||
const rundownMetadata = {};
|
||||
const importMap: ImportMap = { ...defaultImportMap, ...options };
|
||||
|
||||
@@ -89,7 +98,7 @@ export const parseExcel = (excelData: unknown[][], options?: Partial<ImportMap>)
|
||||
}
|
||||
}
|
||||
|
||||
const { customFields, customFieldImportKeys } = getCustomFieldData(importMap);
|
||||
const { customFields, customFieldImportKeys } = getCustomFieldData(importMap, existingCustomFields);
|
||||
const rundown: OntimeRundown = [];
|
||||
|
||||
// title stuff: strings
|
||||
|
||||
Generated
+93
-93
@@ -4,6 +4,30 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
catalogs:
|
||||
default:
|
||||
'@types/node':
|
||||
specifier: 20.14.10
|
||||
version: 20.14.10
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: 7.16.1
|
||||
version: 7.16.1
|
||||
'@typescript-eslint/parser':
|
||||
specifier: 7.16.1
|
||||
version: 7.16.1
|
||||
eslint:
|
||||
specifier: 8.56.0
|
||||
version: 8.56.0
|
||||
eslint-plugin-prettier:
|
||||
specifier: 5.1.3
|
||||
version: 5.1.3
|
||||
prettier:
|
||||
specifier: 3.3.1
|
||||
version: 3.3.1
|
||||
typescript:
|
||||
specifier: 5.5.3
|
||||
version: 5.5.3
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -291,9 +315,6 @@ importers:
|
||||
node-osc:
|
||||
specifier: ^9.0.2
|
||||
version: 9.0.2
|
||||
node-xlsx:
|
||||
specifier: ^0.23.0
|
||||
version: 0.23.0
|
||||
ontime-utils:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/utils
|
||||
@@ -306,6 +327,9 @@ importers:
|
||||
ws:
|
||||
specifier: ^8.13.0
|
||||
version: 8.13.0
|
||||
xlsx:
|
||||
specifier: ^0.18.5
|
||||
version: 0.18.5
|
||||
devDependencies:
|
||||
'@types/cors':
|
||||
specifier: ^2.8.17
|
||||
@@ -2358,6 +2382,7 @@ packages:
|
||||
|
||||
abab@2.0.6:
|
||||
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
|
||||
deprecated: Use your platform's native atob() and btoa() methods instead
|
||||
|
||||
accepts@1.3.8:
|
||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
@@ -2371,10 +2396,6 @@ packages:
|
||||
peerDependencies:
|
||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
|
||||
acorn-walk@8.3.1:
|
||||
resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
acorn-walk@8.3.2:
|
||||
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@@ -2384,15 +2405,9 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.8.1:
|
||||
resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.8.2:
|
||||
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
adler-32@1.3.1:
|
||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
@@ -2651,6 +2666,10 @@ packages:
|
||||
caniuse-lite@1.0.30001570:
|
||||
resolution: {integrity: sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==}
|
||||
|
||||
cfb@1.2.2:
|
||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
chai@4.3.10:
|
||||
resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -2700,6 +2719,10 @@ packages:
|
||||
clone-response@1.0.3:
|
||||
resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
|
||||
|
||||
codepage@1.15.0:
|
||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
color-convert@1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
|
||||
@@ -2974,6 +2997,7 @@ packages:
|
||||
domexception@4.0.0:
|
||||
resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==}
|
||||
engines: {node: '>=12'}
|
||||
deprecated: Use your platform's native DOMException instead
|
||||
|
||||
dot-case@3.0.4:
|
||||
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
|
||||
@@ -3033,10 +3057,6 @@ packages:
|
||||
end-of-stream@1.4.4:
|
||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||
|
||||
entities@4.4.0:
|
||||
resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
@@ -3369,6 +3389,10 @@ packages:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
frac@1.1.2:
|
||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
framer-motion@10.11.2:
|
||||
resolution: {integrity: sha512-IrwuC9regNOU99JoM/Z62CAMA3awGV6AcF7e3bcgXk/ZoNlGSt5aVq0J7UAwtLmCkwVlRvBkiMnvv2mZ1GW2pg==}
|
||||
peerDependencies:
|
||||
@@ -3487,10 +3511,12 @@ packages:
|
||||
|
||||
glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@9.3.2:
|
||||
resolution: {integrity: sha512-BTv/JhKXFEHsErMte/AnfiSv8yYOLLiyH2lTg8vn02O21zWFgHPTfxtgn1QRe7NRgggUhC8hacR2Re94svHqeA==}
|
||||
@@ -4178,11 +4204,6 @@ packages:
|
||||
node-releases@2.0.14:
|
||||
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
|
||||
|
||||
node-xlsx@0.23.0:
|
||||
resolution: {integrity: sha512-r3KaSZSsSrK92rbPXnX/vDdxURmPPik0rjJ3A+Pybzpjyrk4G6WyGfj8JIz5dMMEpCmWVpmO4qoVPBxnpLv/8Q==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
hasBin: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4779,10 +4800,6 @@ packages:
|
||||
snake-case@3.0.4:
|
||||
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
|
||||
|
||||
source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map-js@1.2.0:
|
||||
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4801,6 +4818,10 @@ packages:
|
||||
sprintf-js@1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
|
||||
ssf@0.11.2:
|
||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5315,10 +5336,18 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
wmf@1.0.2:
|
||||
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
word-wrap@1.2.3:
|
||||
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
word@0.3.0:
|
||||
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5326,18 +5355,6 @@ packages:
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
ws@8.12.0:
|
||||
resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
ws@8.13.0:
|
||||
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -5350,9 +5367,8 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xlsx@https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz:
|
||||
resolution: {tarball: https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz}
|
||||
version: 0.19.3
|
||||
xlsx@0.18.5:
|
||||
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
|
||||
@@ -5427,33 +5443,6 @@ packages:
|
||||
react:
|
||||
optional: true
|
||||
|
||||
catalogs:
|
||||
default:
|
||||
'@types/node':
|
||||
specifier: 20.14.10
|
||||
version: 20.14.10
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: 7.16.1
|
||||
version: 7.16.1
|
||||
'@typescript-eslint/parser':
|
||||
specifier: 7.16.1
|
||||
version: 7.16.1
|
||||
eslint:
|
||||
specifier: 8.56.0
|
||||
version: 8.56.0
|
||||
eslint-config-prettier:
|
||||
specifier: 9.1.0
|
||||
version: 9.1.0
|
||||
eslint-plugin-prettier:
|
||||
specifier: 5.1.3
|
||||
version: 5.1.3
|
||||
prettier:
|
||||
specifier: 3.3.1
|
||||
version: 3.3.1
|
||||
typescript:
|
||||
specifier: 5.5.3
|
||||
version: 5.5.3
|
||||
|
||||
snapshots:
|
||||
|
||||
7zip-bin@5.2.0: {}
|
||||
@@ -7633,22 +7622,18 @@ snapshots:
|
||||
|
||||
acorn-globals@7.0.1:
|
||||
dependencies:
|
||||
acorn: 8.8.2
|
||||
acorn-walk: 8.3.1
|
||||
acorn: 8.11.2
|
||||
acorn-walk: 8.3.2
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.11.2):
|
||||
dependencies:
|
||||
acorn: 8.11.2
|
||||
|
||||
acorn-walk@8.3.1: {}
|
||||
|
||||
acorn-walk@8.3.2: {}
|
||||
|
||||
acorn@8.11.2: {}
|
||||
|
||||
acorn@8.8.1: {}
|
||||
|
||||
acorn@8.8.2: {}
|
||||
adler-32@1.3.1: {}
|
||||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
@@ -8014,6 +7999,11 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001570: {}
|
||||
|
||||
cfb@1.2.2:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
crc-32: 1.2.2
|
||||
|
||||
chai@4.3.10:
|
||||
dependencies:
|
||||
assertion-error: 1.1.0
|
||||
@@ -8080,6 +8070,8 @@ snapshots:
|
||||
dependencies:
|
||||
mimic-response: 1.0.1
|
||||
|
||||
codepage@1.15.0: {}
|
||||
|
||||
color-convert@1.9.3:
|
||||
dependencies:
|
||||
color-name: 1.1.3
|
||||
@@ -8449,8 +8441,6 @@ snapshots:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
entities@4.4.0: {}
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
env-paths@2.2.1: {}
|
||||
@@ -8953,6 +8943,8 @@ snapshots:
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
frac@1.1.2: {}
|
||||
|
||||
framer-motion@10.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
tslib: 2.5.0
|
||||
@@ -9509,7 +9501,7 @@ snapshots:
|
||||
jsdom@21.1.0:
|
||||
dependencies:
|
||||
abab: 2.0.6
|
||||
acorn: 8.8.1
|
||||
acorn: 8.11.2
|
||||
acorn-globals: 7.0.1
|
||||
cssom: 0.5.0
|
||||
cssstyle: 2.3.0
|
||||
@@ -9532,7 +9524,7 @@ snapshots:
|
||||
whatwg-encoding: 2.0.0
|
||||
whatwg-mimetype: 3.0.0
|
||||
whatwg-url: 11.0.0
|
||||
ws: 8.12.0
|
||||
ws: 8.13.0
|
||||
xml-name-validator: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
@@ -9809,10 +9801,6 @@ snapshots:
|
||||
|
||||
node-releases@2.0.14: {}
|
||||
|
||||
node-xlsx@0.23.0:
|
||||
dependencies:
|
||||
xlsx: https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
normalize-url@6.1.0: {}
|
||||
@@ -9931,7 +9919,7 @@ snapshots:
|
||||
|
||||
parse5@7.1.2:
|
||||
dependencies:
|
||||
entities: 4.4.0
|
||||
entities: 4.5.0
|
||||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
@@ -10328,7 +10316,7 @@ snapshots:
|
||||
dependencies:
|
||||
chokidar: 3.5.3
|
||||
immutable: 4.2.2
|
||||
source-map-js: 1.0.2
|
||||
source-map-js: 1.2.0
|
||||
|
||||
sax@1.3.0: {}
|
||||
|
||||
@@ -10444,8 +10432,6 @@ snapshots:
|
||||
dot-case: 3.0.4
|
||||
tslib: 2.6.2
|
||||
|
||||
source-map-js@1.0.2: {}
|
||||
|
||||
source-map-js@1.2.0: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
@@ -10460,6 +10446,10 @@ snapshots:
|
||||
sprintf-js@1.1.3:
|
||||
optional: true
|
||||
|
||||
ssf@0.11.2:
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
@@ -10969,8 +10959,12 @@ snapshots:
|
||||
siginfo: 2.0.0
|
||||
stackback: 0.0.2
|
||||
|
||||
wmf@1.0.2: {}
|
||||
|
||||
word-wrap@1.2.3: {}
|
||||
|
||||
word@0.3.0: {}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
@@ -10979,11 +10973,17 @@ snapshots:
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
ws@8.12.0: {}
|
||||
|
||||
ws@8.13.0: {}
|
||||
|
||||
xlsx@https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz: {}
|
||||
xlsx@0.18.5:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
cfb: 1.2.2
|
||||
codepage: 1.15.0
|
||||
crc-32: 1.2.2
|
||||
ssf: 0.11.2
|
||||
wmf: 1.0.2
|
||||
word: 0.3.0
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user