diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts index f4e664ceb..878683eae 100644 --- a/packages/api/src/app.ts +++ b/packages/api/src/app.ts @@ -45,6 +45,7 @@ const plugins = [ '@nocobase/plugin-action-logs', // '@nocobase/plugin-file-manager', '@nocobase/plugin-permissions', + '@nocobase/plugin-export', // '@nocobase/plugin-automations', // '@nocobase/plugin-china-region', ]; diff --git a/packages/client/package.json b/packages/client/package.json index 8faf1d0f0..ded5d7cb3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -33,12 +33,14 @@ "html-react-parser": "^1.2.7", "lodash": "^4.17.21", "monaco-editor": "^0.25.2", + "react-big-calendar": "^0.33.6", "react-dnd": "^14.0.2", "react-dnd-html5-backend": "^14.0.0", "react-dnd-preview": "^6.0.2", "react-dnd-touch-backend": "^14.0.0", "react-helmet": "^6.1.0", "react-hooks-global-state": "^1.0.1", + "react-modal-hook": "^3.0.0", "umi-request": "^1.3.5" } } diff --git a/packages/client/src/resource.ts b/packages/client/src/resource.ts index eef38dcf2..efb9c416d 100644 --- a/packages/client/src/resource.ts +++ b/packages/client/src/resource.ts @@ -99,6 +99,40 @@ export class Resource { }); } + export(options: any) { + const { resourceName } = this.options; + const { columns, ...others } = options; + const url = `${resourceName}:export`; + return request(url, { + method: 'post', + params: { + columns: JSON.stringify(columns), + ...others, + }, + parseResponse: false, + responseType: 'blob' + }).then(async (response: Response) => { + const filename = decodeURI(response.headers.get('Content-Disposition').replace('attachment; filename=', '')); + // ReadableStream + let res = new Response(response.body); + let blob = await res.blob(); + let url = URL.createObjectURL(blob); + let a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + // cleanup + URL.revokeObjectURL(url); + document.body.removeChild(a); + a = null; + blob = null; + url = null; + res = null; + }); + } + destroy(filter: any) { const { resourceName } = this.options; const url = `${resourceName}:destroy`; diff --git a/packages/client/src/schemas/table/index.tsx b/packages/client/src/schemas/table/index.tsx index 95c8a4e47..26a1aabf3 100644 --- a/packages/client/src/schemas/table/index.tsx +++ b/packages/client/src/schemas/table/index.tsx @@ -220,6 +220,52 @@ const useTableDestroyAction = () => { }; }; +const useTableExportAction = () => { + const { + resource, + field, + service, + selectedRowKeys, + setSelectedRowKeys, + refresh, + schema, + props: { refreshRequestOnChange, rowKey }, + } = useTable(); + const ctx = useContext(TableRowContext); + + const actionField = useField(); + const fieldNames = actionField.componentProps.fieldNames || []; + const { getField } = useCollectionContext(); + + const columns = fieldNames + .map((name) => { + const f = getField(name); + return { + title: f?.uiSchema.title, + name, + sort: f?.sort, + }; + }) + .sort((a, b) => a.sort - b.sort); + + return { + async run() { + const rowKeys = selectedRowKeys || []; + const { filter = {}, ...others } = service.params[0]; + if (rowKeys.length) { + filter[`${rowKey}.in`] = rowKeys; + } + await resource.export({ + ...others, + columns, + perPage: -1, + page: 1, + filter, + }); + }, + }; +}; + const useTableRowRecord = () => { const ctx = useContext(TableRowContext); return ctx.record; @@ -817,7 +863,24 @@ function generateActionSchema(type) { fieldNames: [], }, }, - export: {}, + export: { + key: uid(), + type: 'void', + name: uid(), + title: '导出', + 'x-align': 'right', + 'x-decorator': 'AddNew.Displayed', + 'x-decorator-props': { + displayName: 'export', + }, + 'x-action-type': 'export', + 'x-component': 'Action', + 'x-designable-bar': 'Table.ExportActionDesignableBar', + 'x-component-props': { + fieldNames: [], + useAction: '{{ Table.useTableExportAction }}', + }, + }, create: { key: uid(), type: 'void', @@ -993,7 +1056,7 @@ function AddActionButton() { {[ { title: '筛选', name: 'filter' }, - // { title: '导出', name: 'export' }, + { title: '导出', name: 'export' }, { title: '新增', name: 'create' }, { title: '删除', name: 'destroy' }, ].map((item) => ( @@ -1394,6 +1457,124 @@ Table.Filter.DesignableBar = () => { ); }; +Table.ExportActionDesignableBar = () => { + const { schema, remove, refresh, insertAfter } = useDesignable(); + const [visible, setVisible] = useState(false); + const displayed = useDisplayedMapContext(); + const { fields } = useCollectionContext(); + const field = useField(); + let fieldNames = field.componentProps.fieldNames || []; + if (fieldNames.length === 0) { + fieldNames = fields.map((field) => field.name); + } + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + + { + setVisible(visible); + }} + overlay={ + + + {fields.map((collectionField) => ( + { + if (checked) { + fieldNames.push(collectionField.name); + } else { + const index = fieldNames.indexOf(collectionField.name); + if (index > -1) { + fieldNames.splice(index, 1); + } + } + console.log({ fieldNames, field }); + schema['x-component-props']['fieldNames'] = fieldNames; + field.componentProps.fieldNames = fieldNames; + updateSchema(schema); + }} + /> + ))} + + + { + const values = await FormDialog('修改名称和图标', () => { + return ( + + + + ); + }).open({ + initialValues: { + title: schema['title'], + icon: schema['x-component-props']?.['icon'], + }, + }); + schema['title'] = values.title; + schema['x-component-props']['icon'] = values.icon; + field.componentProps.icon = values.icon; + field.title = values.title; + updateSchema(schema); + refresh(); + }} + > + 修改名称和图标 + + + { + const displayName = + schema?.['x-decorator-props']?.['displayName']; + const data = remove(); + await removeSchema(data); + if (displayName) { + displayed.remove(displayName); + } + setVisible(false); + }} + > + 移除 + + + } + > + + + +
+ ); +}; + Table.Operation = observer((props: any) => { const { designable, schema } = useDesignable(); return ( @@ -2094,6 +2275,7 @@ Table.useTableFilterAction = useTableFilterAction; Table.useTableCreateAction = useTableCreateAction; Table.useTableUpdateAction = useTableUpdateAction; Table.useTableDestroyAction = useTableDestroyAction; +Table.useTableExportAction = useTableExportAction; Table.useTableIndex = useTableIndex; Table.useTableRowRecord = useTableRowRecord; Table.SimpleDesignableBar = SimpleDesignableBar; diff --git a/packages/plugin-export/.npmignore b/packages/plugin-export/.npmignore new file mode 100644 index 000000000..461574b2f --- /dev/null +++ b/packages/plugin-export/.npmignore @@ -0,0 +1,7 @@ +node_modules +*.log +docs +__tests__ +tsconfig.json +src +.fatherrc.ts \ No newline at end of file diff --git a/packages/plugin-export/package.json b/packages/plugin-export/package.json new file mode 100644 index 000000000..c737c1623 --- /dev/null +++ b/packages/plugin-export/package.json @@ -0,0 +1,19 @@ +{ + "name": "@nocobase/plugin-export", + "version": "0.4.0-alpha.7", + "main": "lib/index.js", + "license": "MIT", + "devDependencies": { + "@types/node-xlsx": "^0.15.1" + }, + "dependencies": { + "@nocobase/actions": "^0.4.0-alpha.7", + "@nocobase/database": "^0.4.0-alpha.7", + "@nocobase/resourcer": "^0.4.0-alpha.7", + "node-xlsx": "^0.16.1" + }, + "peerDependencies": { + "@nocobase/plugin-collections": "*", + "@nocobase/plugin-permissions": "*" + } +} diff --git a/packages/plugin-export/src/actions/export.ts b/packages/plugin-export/src/actions/export.ts new file mode 100644 index 000000000..521ab4760 --- /dev/null +++ b/packages/plugin-export/src/actions/export.ts @@ -0,0 +1,70 @@ +import xlsx from 'node-xlsx'; +import { actions } from '@nocobase/actions'; +import render from '../renders'; + +async function _export(ctx: actions.Context, next: actions.Next) { + let { columns } = ctx.action.params; + if (typeof columns === 'string') { + columns = JSON.parse(columns); + } + ctx.action.mergeParams({ + 'fields[appends]': columns.map((column: any) => column.name).join(','), + }, { + payload: 'replace', + }); + console.log({ columns }); + await actions.common.list(ctx, async () => { + const { + db, + action: { + params: { + resourceName + } + }, + body, + } = ctx; + + const table = db.getTable(resourceName); + const tableOptions = table.getOptions(); + + let fields = body.rows.length + ? Object.keys(body.rows[0].get()) + .map(key => tableOptions.fields.find(({ name }) => name === key)) + .filter(item => item && !item.developerMode) + .sort((a, b) => a.sort - b.sort) + : []; + + if (columns && columns.length) { + fields = columns.map(column => { + const field = table.getField(column.name); + return { + ...field.options, + ...column, + }; + }); + } + + const { rows, ranges } = render({ + fields, + data: body.rows + }, ctx); + + ctx.body = xlsx.build([{ + name: tableOptions.title, + data: rows, + options: { + '!merges': ranges + } + }]); + + ctx.set({ + 'Content-Type': 'application/octet-stream', + // to avoid "invalid character" error in header (RFC) + 'Content-Disposition': `attachment; filename=${encodeURI(tableOptions.title||tableOptions.name)}.xlsx` + }); + }); + + await next(); +} + +export default _export; diff --git a/packages/plugin-export/src/renders/index.ts b/packages/plugin-export/src/renders/index.ts new file mode 100644 index 000000000..e5e104faf --- /dev/null +++ b/packages/plugin-export/src/renders/index.ts @@ -0,0 +1,162 @@ +import * as renders from './renders'; + +function getInterfaceRender(name: string): Function { + return renders[name] || renders._; +} + +function renderHeader(params, ctx) { + const { + fields, + headers = [], + rowIndex = 0 + } = params; + + let { colIndex = 0 } = params; + + if (!headers[rowIndex]) { + headers.push([]); + } + const row = headers[rowIndex]; + + fields.forEach((field, i) => { + const nextColIndex = colIndex + i; + row.push({ + field, + rowIndex, + colIndex: nextColIndex + }); + if (field.interface === 'subTable') { + const subTable = ctx.db.getTable(field.target); + const subFields = subTable.getOptions().fields.filter(field => Boolean(field.__index)); + renderHeader({ + fields: subFields, + headers, + rowIndex: rowIndex + 1, + colIndex: nextColIndex + }, ctx); + colIndex += subFields.length; + } + }); + + Object.assign(params, { headers }); +} + +function renderRows({ fields, data }, ctx) { + return data.reduce((result, row) => { + const thisRow = []; + const rowIndex = 0; + let colOffset = 0; + fields.forEach((field, i) => { + if (!thisRow[rowIndex]) { + thisRow.push([]); + } + const cells = thisRow[rowIndex]; + if (field.interface !== 'subTable') { + const render = getInterfaceRender(field.interface); + cells.push({ + value: render(field, row), + rowIndex: result.length + rowIndex, + colIndex: i + colOffset + }); + return; + } + + const subTable = ctx.db.getTable(field.target); + const subFields = subTable.getOptions().fields.filter(item => Boolean(item.__index)); + const subRows = renderRows({ fields: subFields, data: row.get(field.name) || [] }, ctx); + + // const { rows: subRowGroups } = subTableRows; + subRows.forEach((cells, j) => { + const subRowIndex = rowIndex + j; + if (!thisRow[subRowIndex]) { + thisRow.push([]); + } + const subCells = thisRow[subRowIndex]; + subCells.push(...cells.map(cell => ({ + ...cell, + rowIndex: result.length + subRowIndex, + colIndex: cell.colIndex + i + }))); + }); + colOffset += subFields.length; + }); + + thisRow.forEach((cells) => { + cells.forEach(cell => { + const relRowIndex = cell.rowIndex - result.length; + Object.assign(cell, { + rowSpan: relRowIndex >= thisRow.length - 1 + || thisRow[relRowIndex + 1].find(item => item.colIndex === cell.colIndex) + ? 1 + : thisRow.length - relRowIndex + }); + }); + }); + + return result.concat(thisRow); + }, []); +} + +export default function({ fields, data }, ctx) { + const headers = []; + renderHeader({ + fields, + headers + }, ctx); + const ranges = []; + // 计算全表最大的列索引(由于无论如何最大列都是单个单元格,所以等价于长度) + const maxColIndex = Math.max(...headers.map(row => row[row.length - 1].colIndex)); + // 遍历所有单元格,计算需要合并的坐标范围 + headers.forEach((row, rowIndex) => { + row.forEach((cell, cellIndex) => { + // 跨行合并的行数为 + cell.rowSpan = cell.rowIndex >= headers.length - 1 + || headers[cell.rowIndex + 1].find(item => item.colIndex === cell.colIndex) + ? 1 + : headers.length - cell.rowIndex; + + const nextCell = headers.slice(0, rowIndex + 1) + .map(r => r.find(item => item.colIndex > cell.colIndex)) + .filter(c => Boolean(c)) + .reduce((min, c) => min && Math.min(min.colIndex, c.colIndex) === min.colIndex ? min : c, null); + cell.colSpan = nextCell + ? nextCell.colIndex - cell.colIndex + : maxColIndex - cell.colIndex + 1; + + if (cell.rowSpan > 1 || cell.colSpan > 1) { + ranges.push({ + s: { c: cell.colIndex, r: cell.rowIndex }, + e: { c: cell.colIndex + cell.colSpan - 1, r: cell.rowIndex + cell.rowSpan - 1 } + }); + } + }); + }); + + const rows = renderRows({ fields, data }, ctx) + .map(row => { + const cells = Array(maxColIndex).fill(null); + row.forEach(cell => { + cells.splice(cell.colIndex, 1, cell.value); + if (cell.rowSpan > 1) { + ranges.push({ + s: { c: cell.colIndex, r: cell.rowIndex + headers.length }, + e: { c: cell.colIndex, r: cell.rowIndex + cell.rowSpan - 1 + headers.length } + }); + } + }); + return cells; + }); + + return { + rows: [ + ...headers.map(row => { + // 补齐无数据单元格,以供合并 + const cells = Array(maxColIndex).fill(null); + row.forEach(cell => cells.splice(cell.colIndex, 1, cell.field.title)) + return cells; + }), + ...rows + ], + ranges + }; +} diff --git a/packages/plugin-export/src/renders/renders.ts b/packages/plugin-export/src/renders/renders.ts new file mode 100644 index 000000000..a8da3a0e3 --- /dev/null +++ b/packages/plugin-export/src/renders/renders.ts @@ -0,0 +1,60 @@ +import moment from 'moment'; + +export function _(field, row) { + return row.get(field.name); +} + +export function datetime(field, row) { + const value = row.get(field.name); + return moment(value).format(field.showTime ? `${field.dateFormat} ${field.timeFormat}` : field.dateFormat); +} + +export function percent(field, row) { + const value = row.get(field.name); + return value && `${value}%`; +} + +export function boolean(field, row) { + const value = row.get(field.name); + // TODO(feature): i18n + return value ? '是' : '否'; +} + +export function select(field, row) { + const value = row.get(field.name); + const option = field.dataSource.find(item => item.value === value); + return option && option.label; +} + +export function multipleSelect(field, row) { + const values = row.get(field.name); + return values && values.map(value => { + const option = field.dataSource.find(item => item.value === value); + return option && option.label; + }); +} + +export const radio = select; + +export const checkboxes = multipleSelect; + +export function subTable(field, row) { + // TODO: need title field to be defined + return (row.get(field.name) || []).map(item => item[field.sourceKey]); +} + +export function linkTo(field, row) { + return (row.get(field.name) || []).map(item => item[field.labelField]); +} + +export function attachment(field, row) { + return (row.get(field.name) || []).map(item => item[field.url]).join(' '); +} + +export function chinaRegion(field, row) { + const value = row.get(field.name); + const values = (Array.isArray(value) ? value : [value]).sort((a, b) => + a.level !== b.level ? a.level - b.level : a.sort - b.sort, + ); + return values.map(item => item.name).join('/'); +} diff --git a/packages/plugin-export/src/server.ts b/packages/plugin-export/src/server.ts new file mode 100644 index 000000000..42c4db145 --- /dev/null +++ b/packages/plugin-export/src/server.ts @@ -0,0 +1,28 @@ +import Resourcer from '@nocobase/resourcer'; +import _export from './actions/export'; + +export const ACTION_NAME_EXPORT = 'export'; + +export default async function (options = {}) { + const resourcer: Resourcer = this.resourcer; + + resourcer.registerActionHandler(ACTION_NAME_EXPORT, _export); + + // TODO(temp): 继承 list 权限的临时写法 + resourcer.use(async (ctx, next) => { + if (ctx.action.params.actionName === ACTION_NAME_EXPORT) { + ctx.action.mergeParams({ + actionName: 'list' + }); + + console.log('action name in export has been rewritten to:', ctx.action.params.actionName); + + const permissionPlugin = ctx.app.getPluginInstance('@nocobase/plugin-permissions'); + if (permissionPlugin) { + return permissionPlugin.middleware(ctx, next); + } + } + + await next(); + }); +} diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 74b6dc460..5bf71159c 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -20,7 +20,9 @@ export default { const app = new Application(options); app.use(bodyParser()); - app.use(cors()); + app.use(cors({ + exposeHeaders: ['content-disposition'], + })); app.resourcer.registerActionHandlers({ ...actions.common, ...actions.associate }); diff --git a/yarn.lock b/yarn.lock index 4bf0ef53f..63dc52e41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1695,6 +1695,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.14.6", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7": + version "7.15.3" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" + integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.14.5", "@babel/template@^7.3.3", "@babel/template@^7.4.0", "@babel/template@^7.4.4": version "7.14.5" resolved "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -3258,6 +3265,11 @@ dependencies: "@octokit/openapi-types" "^7.3.2" +"@popperjs/core@^2.5.3": + version "2.9.3" + resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz#8b68da1ebd7fc603999cf6ebee34a4899a14b88e" + integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== + "@qixian.cs/path-to-regexp@^6.1.0": version "6.1.0" resolved "https://registry.npmjs.org/@qixian.cs/path-to-regexp/-/path-to-regexp-6.1.0.tgz#6b84ad01596332aba95fa29d2e70104698cd5c45" @@ -3278,6 +3290,13 @@ resolved "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a" integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg== +"@restart/hooks@^0.3.25": + version "0.3.27" + resolved "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.27.tgz#91f356d66d4699a8cd8b3d008402708b6a9dc505" + integrity sha512-s984xV/EapUIfkjlf8wz9weP2O9TNKR96C68FfMEy2bE69+H4cNv3RD4Mf97lW7Htt7PjZrYTjSC8f3SB9VCXw== + dependencies: + dequal "^2.0.2" + "@rollup/plugin-babel@5.2.1": version "5.2.1" resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.1.tgz#20fc8f8864dc0eaa1c5578408459606808f72924" @@ -3779,6 +3798,11 @@ dependencies: "@types/node" "*" +"@types/node-xlsx@^0.15.1": + version "0.15.2" + resolved "https://registry.npmjs.org/@types/node-xlsx/-/node-xlsx-0.15.2.tgz#1371c4af8fe2b6626253f8b85719adfe88355b20" + integrity sha512-rG1SmpWvSkse23rL/5bgej5yQ2y+WoWMrKE3iE4upuHWQwzdLCYEkrwr1pMZDx642R/tJCDGGUig7U/cT24Ecg== + "@types/node@*", "@types/node@>= 8": version "15.12.2" resolved "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" @@ -3892,7 +3916,7 @@ "@types/history" "*" "@types/react" "*" -"@types/react@*", "@types/react@^16.9.43", "@types/react@^17.0.0": +"@types/react@*", "@types/react@>=16.9.11", "@types/react@^16.9.43", "@types/react@^17.0.0": version "17.0.11" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451" integrity sha512-yFRQbD+whVonItSk7ZzP/L+gPTJVBkL/7shLEF+i9GC/1cV3JmUxEQz6+9ylhUpWSDuqo1N9qEvqS6vTj4USUA== @@ -3943,6 +3967,11 @@ resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== +"@types/warning@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" + integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI= + "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" @@ -4520,6 +4549,14 @@ address@>=0.0.1, address@^1.0.0: resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== +adler-32@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25" + integrity sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU= + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -5670,6 +5707,11 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer-from@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + buffer-writer@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" @@ -5925,6 +5967,15 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +cfb@^1.1.4: + version "1.2.0" + resolved "https://registry.npmjs.org/cfb/-/cfb-1.2.0.tgz#6a4d0872b525ed60349e1ef51fb4b0bf73eca9a8" + integrity sha512-sXMvHsKCICVR3Naq+J556K+ExBo9n50iKl6LGarlnvuA2035uMlGA/qVrc0wQtow5P1vJEw9UyrKLCbtIKz+TQ== + dependencies: + adler-32 "~1.2.0" + crc-32 "~1.2.0" + printj "~1.1.2" + chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -6224,6 +6275,11 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" +clsx@^1.0.4: + version "1.1.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + co-body@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/co-body/-/co-body-6.1.0.tgz#d87a8efc3564f9bfe3aced8ef5cd04c7a8766547" @@ -6258,6 +6314,14 @@ code-point-at@^1.0.0: resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +codepage@~1.14.0: + version "1.14.0" + resolved "https://registry.npmjs.org/codepage/-/codepage-1.14.0.tgz#8cbe25481323559d7d307571b0fff91e7a1d2f99" + integrity sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k= + dependencies: + commander "~2.14.1" + exit-on-epipe "~1.0.1" + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -6351,6 +6415,16 @@ commander@^2.19.0, commander@^2.20.0: resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@~2.14.1: + version "2.14.1" + resolved "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" + integrity sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw== + +commander@~2.17.1: + version "2.17.1" + resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -6709,6 +6783,14 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +crc-32@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -7157,6 +7239,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-arithmetic@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz#e5d6434e9deb71f79760a37b729e4a515e730ddf" + integrity sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg== + date-fns@^2.0.1: version "2.23.0" resolved "https://registry.nlark.com/date-fns/download/date-fns-2.23.0.tgz?cache=0&sync_timestamp=1627020353586&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fdate-fns%2Fdownload%2Fdate-fns-2.23.0.tgz#4e886c941659af0cf7b30fafdd1eaa37e88788a9" @@ -7388,6 +7475,11 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== +dequal@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" + integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -7520,6 +7612,14 @@ dom-align@1.x, dom-align@^1.7.0: resolved "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz#0f8164ebd0c9c21b0c790310493cd855892acd4b" integrity sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg== +dom-helpers@^5.1.0, dom-helpers@^5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -8373,6 +8473,11 @@ exenv@^1.2.0: resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -8590,6 +8695,11 @@ fecha@~4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce" integrity sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q== +fflate@^0.3.8: + version "0.3.11" + resolved "https://registry.npmjs.org/fflate/-/fflate-0.3.11.tgz#2c440d7180fdeb819e64898d8858af327b042a5d" + integrity sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A== + figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -8797,6 +8907,11 @@ formstream@^1.1.0: mime "^2.5.2" pause-stream "~0.0.11" +frac@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b" + integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -12098,6 +12213,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash-es@^4.17.11: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -13297,6 +13417,15 @@ node-schedule@^2.0.0: long-timeout "0.1.1" sorted-array-functions "^1.3.0" +node-xlsx@^0.16.1: + version "0.16.2" + resolved "https://registry.npmjs.org/node-xlsx/-/node-xlsx-0.16.2.tgz#40f580187eae0e032cac96e958e97cb6ceca09f6" + integrity sha512-ZT3Y4Zg2BFC2UWdp9B/6x3GqrFL0Bf0cXKy9IyhcwlKbcDAf5GuPAPSqrWFQK68NIpfTNA1Kr/NNjpwYxUgHTA== + dependencies: + "@babel/runtime" "^7.14.6" + buffer-from "^1.1.1" + xlsx "^0.17.0" + nodemon@^2.0.12: version "2.0.12" resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5" @@ -15098,6 +15227,11 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +printj@~1.1.0, printj@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + prism-react-renderer@^1.1.1: version "1.2.1" resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89" @@ -15880,6 +16014,23 @@ react-attr-converter@^0.3.1: resolved "https://registry.npmjs.org/react-attr-converter/-/react-attr-converter-0.3.1.tgz#4a2abf6d907b7ddae4d862dfec80e489ce41ad6e" integrity sha512-dSxo2Mn6Zx4HajeCeQNLefwEO4kNtV/0E682R1+ZTyFRPqxDa5zYb5qM/ocqw9Bxr/kFQO0IUiqdV7wdHw+Cdg== +react-big-calendar@^0.33.6: + version "0.33.6" + resolved "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-0.33.6.tgz#87594b4ce54b4f3cc7f69d52f2d34ddf6b2eb00b" + integrity sha512-4crVUE1W7+zIUfbfrXxd9nDOVktwn8M8Ffrjd39r9zFIveZu1zhrWwi+qqu46EQw2xRnb+mFx+mcwUozkqYlkA== + dependencies: + "@babel/runtime" "^7.1.5" + clsx "^1.0.4" + date-arithmetic "^4.1.0" + dom-helpers "^5.1.0" + invariant "^2.2.4" + lodash "^4.17.11" + lodash-es "^4.17.11" + memoize-one "^5.1.1" + prop-types "^15.7.2" + react-overlays "^4.1.1" + uncontrollable "^7.0.0" + react-dnd-html5-backend@^14.0.0: version "14.0.0" resolved "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-14.0.0.tgz#28d660a2ad1e07447c34a65cd25f7de8f1657194" @@ -15990,6 +16141,11 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-modal-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/react-modal-hook/-/react-modal-hook-3.0.0.tgz#24b2ff2acf288ef25c11e4fb11b329458a823ea3" + integrity sha512-mNkJwgEtOoIabuILlWnGAto993WVkimZASBWk/rAGZ6tNIqjx4faQtNdr/X31vw+QGfKhspXc4vPi1jbfkk2Yg== + react-native-swipeout@^2.2.2: version "2.3.6" resolved "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz#47dac8a835825cf3f2eef9e495574a3d9ab6d3fa" @@ -15999,6 +16155,20 @@ react-native-swipeout@^2.2.2: prop-types "^15.5.10" react-tween-state "^0.1.5" +react-overlays@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/react-overlays/-/react-overlays-4.1.1.tgz#0060107cbe1c5171a744ccda3fbf0556d064bc5f" + integrity sha512-WtJifh081e6M24KnvTQoNjQEpz7HoLxqt8TwZM7LOYIkYJ8i/Ly1Xi7RVte87ZVnmqQ4PFaFiNHZhSINPSpdBQ== + dependencies: + "@babel/runtime" "^7.12.1" + "@popperjs/core" "^2.5.3" + "@restart/hooks" "^0.3.25" + "@types/warning" "^3.0.0" + dom-helpers "^5.2.0" + prop-types "^15.7.2" + uncontrollable "^7.0.0" + warning "^4.0.3" + react-property@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/react-property/-/react-property-1.0.1.tgz#4ae4211557d0a0ae050a71aa8ad288c074bea4e6" @@ -17685,6 +17855,13 @@ sqlstring@^2.3.2: resolved "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg== +ssf@~0.11.2: + version "0.11.2" + resolved "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c" + integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g== + dependencies: + frac "~1.1.2" + sshpk@^1.7.0: version "1.16.1" resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -18911,6 +19088,16 @@ unc-path-regex@^0.1.2: resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= +uncontrollable@^7.0.0: + version "7.2.1" + resolved "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + undefsafe@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" @@ -19658,11 +19845,21 @@ wkx@^0.5.0: dependencies: "@types/node" "*" +wmf@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da" + integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw== + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +word@~0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961" + integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== + wordwrap@0.0.2: version "0.0.2" resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" @@ -19785,6 +19982,22 @@ xdg-basedir@^4.0.0: resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== +xlsx@^0.17.0: + version "0.17.0" + resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.17.0.tgz#028176a0140967dcee1817d221678461e47481c8" + integrity sha512-bZ36FSACiAyjoldey1+7it50PMlDp1pcAJrZKcVZHzKd8BC/z6TQ/QAN8onuqcepifqSznR6uKnjPhaGt6ig9A== + dependencies: + adler-32 "~1.2.0" + cfb "^1.1.4" + codepage "~1.14.0" + commander "~2.17.1" + crc-32 "~1.2.0" + exit-on-epipe "~1.0.1" + fflate "^0.3.8" + ssf "~0.11.2" + wmf "~1.0.1" + word "~0.3.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"