This commit is contained in:
linfeng 2025-03-24 20:16:37 +08:00
parent e98f1dcfa0
commit 127ed07383
18 changed files with 959 additions and 234 deletions

View File

@ -23,7 +23,7 @@
"@ag-grid-enterprise/server-side-row-model": "~32.3.2", "@ag-grid-enterprise/server-side-row-model": "~32.3.2",
"@ag-grid-enterprise/set-filter": "~32.3.2", "@ag-grid-enterprise/set-filter": "~32.3.2",
"@ag-grid-enterprise/status-bar": "~32.3.2", "@ag-grid-enterprise/status-bar": "~32.3.2",
"@ant-design/icons": "^5.4.0", "@ant-design/icons": "^5.5.2",
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@ -41,11 +41,12 @@
"@trpc/client": "11.0.0-rc.456", "@trpc/client": "11.0.0-rc.456",
"@trpc/react-query": "11.0.0-rc.456", "@trpc/react-query": "11.0.0-rc.456",
"@trpc/server": "11.0.0-rc.456", "@trpc/server": "11.0.0-rc.456",
"@types/react-redux": "^7.1.34",
"@xyflow/react": "^12.3.6", "@xyflow/react": "^12.3.6",
"ag-grid-community": "~32.3.2", "ag-grid-community": "~32.3.2",
"ag-grid-enterprise": "~32.3.2", "ag-grid-enterprise": "~32.3.2",
"ag-grid-react": "~32.3.2", "ag-grid-react": "~32.3.2",
"antd": "^5.19.3", "antd": "^5.23.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
@ -64,12 +65,14 @@
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.54.2",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-redux": "^9.2.0",
"react-resizable": "^3.0.5", "react-resizable": "^3.0.5",
"react-router-dom": "^6.24.1", "react-router-dom": "^6.24.1",
"superjson": "^2.2.1", "superjson": "^2.2.1",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
"use-debounce": "^10.0.4", "use-debounce": "^10.0.4",
"uuid": "^10.0.0", "uuid": "^10.0.0",
"xlsx": "^0.18.5",
"yjs": "^13.6.20", "yjs": "^13.6.20",
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
@ -77,6 +80,7 @@
"@eslint/js": "^9.9.0", "@eslint/js": "^9.9.0",
"@types/react": "18.2.38", "@types/react": "18.2.38",
"@types/react-dom": "18.2.15", "@types/react-dom": "18.2.15",
"@types/xlsx": "^0.0.36",
"@vitejs/plugin-react-swc": "^3.5.0", "@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.9.0", "eslint": "^9.9.0",

View File

@ -1,9 +1,9 @@
import React from "react" import React from "react"
export default function HomePage() { export default function Dashboard() {
return ( return (
<div > <div >
</div> </div>
) )
} }

View File

@ -1,43 +1,30 @@
import { Layout, Menu, Avatar, Button } from 'antd'; import { Layout, Avatar, Dropdown, Menu } from 'antd';
import { UserOutlined, SettingOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import { useNavigate, Outlet, useLocation } from 'react-router-dom'; import { useNavigate, Outlet, useLocation } from 'react-router-dom';
import NavigationMenu from './NavigationMenu'; import NavigationMenu from './NavigationMenu';
import { Header } from 'antd/es/layout/layout';
import { useState } from 'react';
const { Sider, Content } = Layout; const { Sider, Content } = Layout;
export default function MainHeader() { export default function MainHeader() {
const navigate = useNavigate();
return ( return (
<Layout>
{/* 顶部Header */}
<Header className="flex justify-end items-center bg-white shadow-sm h-16 px-6">
<Avatar className='bg-black'></Avatar>
</Header>
{/* 主体布局 */}
<Layout className="h-screen"> <Layout className="h-screen">
{/* 左侧导航栏 */} {/* 左侧导航栏 */}
<Sider <Sider theme="light" width={240} className="shadow-sm">
width={280} <NavigationMenu />
className=" flex flex-col"
>
{/* 用户头像区域 */}
<div className="p-6 border-b">
<div className="relative group flex justify-center">
<Avatar
size={120}
icon={<UserOutlined />}
className=" transition-all duration-300"
/>
<Button
type="text"
icon={<SettingOutlined />}
className="!absolute bottom-0 right-6 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
onClick={() => navigate('/profile')}
/>
</div>
</div>
<NavigationMenu></NavigationMenu>
</Sider> </Sider>
{/* 新增可滚动内容区域 */}
<Layout className="flex-1"> {/* 内容区域 */}
<Content className="overflow-auto"> <Content className="overflow-auto p-6 bg-gray-50">
<Outlet /> <Outlet />
</Content> </Content>
</Layout> </Layout>

View File

@ -1,40 +1,307 @@
import React, { useEffect, useState } from "react";
import { Menu } from "antd"; import { Menu } from "antd";
// import { useSelector } from "react-redux";
import { useNavigate, useLocation } from "react-router-dom"; import { useNavigate, useLocation } from "react-router-dom";
// import styles from "./index.module.less";
// import logo from "../../assets/logo.png";
export default function NavigationMenu() { function getItem(
const navigate = useNavigate(); label: any,
const location = useLocation(); key: any,
console.log(location.pathname); icon: any,
// 导航菜单项配置 children: any,
const menuItems = [ type: any,
{ key: 'staff', label: '人员总览', path: '/' }, // 将path改为根路径 // permission: any
{ key: 'plan', label: '培训计划', path: '/plan' }, ) {
{ key: 'day', label: '每日填报', path: '/daily' }, return {
{ key: 'exam', label: '考核成绩', path: '/exam' }, key,
icon,
children,
label,
type,
// permission,
};
}
const items = [
getItem(
"首页概览",
"/",
<i className={`iconfont icon-icon-home`} />,
null,
null,
),
getItem(
"人员总览",
"/staff",
<i className="iconfont icon-icon-category" />,
null,
null,
),
getItem(
"训练计划",
"/plan",
<i className="iconfont icon-icon-user" />,
[
getItem("周训练计划", "/plan/weekplan", null, null, null),
getItem("月训练计划", "/plan/monthplan", null, null, null),
],
null,
),
getItem(
"每日填报",
"/daily",
<i className="iconfont icon-icon-user" />,
null,
null,
),
getItem(
"考核成绩",
"/assessment",
<i className="iconfont icon-icon-user" />,
[
getItem("岗位", "/assessment/positionassessment", null, null, null),
getItem("共同", "/assessment/commonassessment", null, null, null),
getItem("体育", "/assessment/sportsassessment", null, null, null),
],
null,
)
]; ];
return ( const NavigationMenu: React.FC = () => {
<> const location = useLocation();
<Menu const navigate = useNavigate();
theme="dark" const children2Parent: any = {
mode="inline" "^/plan/weekplan": ["/plan"],
className="!bg-transparent !border-0 pt-4 [&_.ant-menu-item]:!mt-2" "^/plan/monthplan": ["/plan"],
selectedKeys={[ // 改用selectedKeys替代defaultSelectedKeys // 添加考核成绩子路径的匹配规则
menuItems.find((item) => location.pathname.startsWith(item.path))?.key, "^/assessment/positionassessment": ["/assessment"],
]} "^/assessment/commonassessment": ["/assessment"],
> "^/assessment/sportsassessment": ["/assessment"]
{menuItems.map((item) => ( };
<Menu.Item
key={item.key} // 同时在 useEffect 中更新路径判断逻辑
className="!h-14 !flex !items-center !text-gray-300 hover:!text-white group !rounded-lg mx-4" useEffect(() => {
onClick={() => navigate(item.path)} if (location.pathname.indexOf("/staff") !== -1) {
> setSelectedKeys(["/staff"]);
<div className="flex items-center justify-center w-full px-4 transition-all duration-300 h-full rounded-lg"> setOpenKeys(openKeyMerge("/staff"));
<span className="text-xl font-medium">{item.label}</span> } else if (
</div> // 添加考核成绩路径的判断
</Menu.Item> location.pathname.startsWith("/assessment/") ||
))} location.pathname === "/plan/weekplan" ||
</Menu> location.pathname === "/plan/monthplan"
</> ) {
); setSelectedKeys([location.pathname]);
setOpenKeys([location.pathname.split('/').slice(0, 2).join('/')]);
} else {
setSelectedKeys([location.pathname]);
setOpenKeys(openKeyMerge(location.pathname));
} }
}, [location.pathname]);
const hit = (pathname: string): string[] => {
for (let p in children2Parent) {
if (pathname.search(p) >= 0) {
return children2Parent[p];
}
}
return [];
};
const openKeyMerge = (pathname: string): string[] => {
let newOpenKeys = hit(pathname);
for (let i = 0; i < openKeys.length; i++) {
let isIn = false;
for (let j = 0; j < newOpenKeys.length; j++) {
if (newOpenKeys[j] === openKeys[i]) {
isIn = true;
break;
}
}
if (isIn) {
continue;
}
newOpenKeys.push(openKeys[i]);
}
return newOpenKeys;
};
// 选中的菜单
const [selectedKeys, setSelectedKeys] = useState<string[]>([
location.pathname,
]);
// 展开菜单
const [openKeys, setOpenKeys] = useState<string[]>(hit(location.pathname));
// const permissions = useSelector(
// (state: any) => state.loginUser.value.permissions
// );
const [activeMenus, setActiveMenus] = useState<any>(items);
const onClick = (e: any) => {
navigate(e.key);
};
// console.log(items)
useEffect(() => {
setActiveMenus(items);
// console.log(activeMenus)
},[items])
// useEffect(() => {
// checkMenuPermissions(items, permissions);
// }, [items, permissions]);
// const checkMenuPermissions = (items: any, permissions: any) => {
// let menus: any = [];
// if (permissions.length === 0) {
// setActiveMenus(menus);
// return;
// }
// for (let i in items) {
// let menuItem = items[i];
// // 一级菜单=>没有子菜单&配置了权限
// if (menuItem.children === null) {
// if (
// menuItem.permission !== null &&
// typeof permissions[menuItem.permission] === "undefined"
// ) {
// continue;
// }
// menus.push(menuItem);
// continue;
// }
// let children = [];
// for (let j in menuItem.children) {
// let childrenItem = menuItem.children[j];
// if (
// typeof permissions[childrenItem.permission] !== "undefined" ||
// !childrenItem.permission
// ) {
// // 存在权限
// children.push(childrenItem);
// }
// }
// if (children.length > 0) {
// menus.push(Object.assign({}, menuItem, { children: children }));
// }
// }
// setActiveMenus(menus);
// };
useEffect(() => {
if (location.pathname.indexOf("/staff") !== -1) {
setSelectedKeys(["/staff"]);
setOpenKeys(openKeyMerge("/staff")); // 修正为当前路径的父级
} else if (
location.pathname === "/weekplan" ||
location.pathname === "/monthplan"
) {
setSelectedKeys([location.pathname]);
setOpenKeys(["/plan"]); // 直接展开训练计划父菜单
} else {
setSelectedKeys([location.pathname]);
setOpenKeys(openKeyMerge(location.pathname));
}
}, [location.pathname]);
return (
<div className='w-[200px] h-full bg-#fff'>
<div
style={{
textDecoration: "none",
cursor: "pointer",
position: "sticky",
top: 0,
zIndex: 10,
background: "#fff",
}}
onClick={() => {
window.location.href = "/";
}}
>
{/*
<img src={logo} className="w-[124px] h-[40px]"/> */}
</div>
<div className='w-[200px] h-[calc(100%-74px)] overflow-y-auto overflow-x-hidden'>
<Menu
onClick={onClick}
style={{
width: 200,
background: "#ffffff",
}}
selectedKeys={selectedKeys}
openKeys={openKeys}
mode="inline"
items={activeMenus}
onSelect={(data: any) => {
setSelectedKeys(data.selectedKeys);
}}
onOpenChange={(keys: any) => {
setOpenKeys(keys);
}}
/>
</div>
</div>
);
};
export default NavigationMenu;
// import { Menu } from "antd";
// import { useNavigate, useLocation } from "react-router-dom";
// export default function NavigationMenu() {
// const navigate = useNavigate();
// const location = useLocation();
// console.log(location.pathname);
// // 导航菜单项配置
// const menuItems = [
// { key: 'staff', label: '人员总览', path: '/' }, // 将path改为根路径
// { key: 'plan', label: '培训计划', path: '/plan' },
// { key: 'day', label: '每日填报', path: '/daily' },
// { key: 'exam', label: '考核成绩', path: '/exam' },
// ];
// return (
// <>
// <Menu
// theme="dark"
// mode="inline"
// className="!bg-transparent !border-0 pt-4 [&_.ant-menu-item]:!mt-2"
// selectedKeys={[ // 改用selectedKeys替代defaultSelectedKeys
// menuItems.find((item) => location.pathname.startsWith(item.path))?.key,
// ]}
// >
// {menuItems.map((item) => (
// <Menu.Item
// key={item.key}
// className="!h-14 !flex !items-center !text-gray-300 hover:!text-white group !rounded-lg mx-4"
// onClick={() => navigate(item.path)}
// >
// <div className="flex items-center justify-center w-full px-4 transition-all duration-300 h-full rounded-lg">
// <span className="text-xl font-medium">{item.label}</span>
// </div>
// </Menu.Item>
// ))}
// </Menu>
// </>
// );
// }

View File

View File

@ -0,0 +1,227 @@
import { useState } from 'react'
import * as XLSX from 'xlsx'
import { Table, Select, Pagination } from 'antd'
import type { ColumnsType, ColumnType } from 'antd/es/table'
import { UploadOutlined } from '@ant-design/icons'
interface TableData {
key: string
[key: string]: any
}
export default function WeekPlanPage() {
const [data, setData] = useState<TableData[]>([])
const [columns, setColumns] = useState<ColumnsType<TableData>>([])
const [currentPage, setCurrentPage] = useState(1)
const [trainingStatus, setTrainingStatus] = useState<Record<string, string>>({})
const pageSize = 1 // 每页显示一个第一列的唯一值
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (!file) return
const reader = new FileReader()
reader.onload = (e) => {
const data = new Uint8Array(e.target?.result as ArrayBuffer)
const workbook = XLSX.read(data, { type: 'array' })
const firstSheet = workbook.Sheets[workbook.SheetNames[0]]
// 处理合并单元格
if (firstSheet['!merges']) {
firstSheet['!merges'].forEach(merge => {
// 获取合并区域的起始单元格的值
const firstCell = XLSX.utils.encode_cell({ r: merge.s.r, c: merge.s.c })
const firstCellValue = firstSheet[firstCell]?.v
// 将合并区域内的所有单元格设置为相同的值
for (let row = merge.s.r; row <= merge.e.r; row++) {
for (let col = merge.s.c; col <= merge.e.c; col++) {
const cell = XLSX.utils.encode_cell({ r: row, c: col })
if (!firstSheet[cell]) {
firstSheet[cell] = { t: 's', v: firstCellValue }
}
}
}
})
}
const jsonData: any[] = XLSX.utils.sheet_to_json(firstSheet, {
header: 1,
defval: '',
blankrows: false,
raw: false
})
// 处理表头和数据
const headers = jsonData[0]
const tableData: TableData[] = jsonData.slice(1).map((row, index) => ({
key: index.toString(),
...headers.reduce((acc: any, header: string, idx: number) => {
acc[header] = row[idx]
return acc
}, {})
}))
// 创建列配置
const tableColumns: ColumnsType<TableData> = headers.map((header: string, index: number) => ({
title: header,
dataIndex: header,
key: header,
width: 150,
align: 'center',
filterMultiple: true,
filters: Array.from(new Set(tableData.map(item => item[header])))
.filter(Boolean)
.map(value => ({ text: String(value), value: String(value) })),
onFilter: (value, record) => String(record[header]) === value,
...(index < headers.length - 1 && {
render: (text, record, index) => ({
children: text,
props: {
rowSpan: calculateRowSpan(tableData, index, header),
style: {
border: '1px solid #f0f0f0',
borderBottom: '1px solid #f0f0f0'
}
}
})
})
}))
// 添加是否参训列
tableColumns.push({
title: '是否参训',
dataIndex: 'isTraining',
key: 'isTraining',
width: 120,
align: 'center',
render: (_, record) => (
<Select
value={trainingStatus[record.key] || undefined}
onChange={(value) => {
setTrainingStatus(prev => ({
...prev,
[record.key]: value
}))
}}
style={{ width: '100%' }}
options={[
{ value: '参训', label: '参训' },
{ value: '不参训', label: '不参训' }
]}
placeholder="请选择"
/>
)
})
setColumns(tableColumns)
setData(tableData)
}
reader.readAsArrayBuffer(file)
}
// 计算行合并
const calculateRowSpan = (data: TableData[], rowIndex: number, column: string) => {
if (rowIndex === 0) {
let count = 1
for (let i = 1; i < data.length; i++) {
if (data[i][column] === data[rowIndex][column]) {
count++
} else {
break
}
}
return count
}
if (rowIndex > 0 && data[rowIndex][column] === data[rowIndex - 1][column]) {
return 0
}
let count = 1
for (let i = rowIndex + 1; i < data.length; i++) {
if (data[i][column] === data[rowIndex][column]) {
count++
} else {
break
}
}
return count
}
// 修改分页数据获取逻辑
const getPageData = () => {
const firstColumn = columns[0] as ColumnType<TableData>
const firstColumnName = firstColumn?.dataIndex as string
if (!firstColumnName) return []
// 获取第一列的所有唯一值
const uniqueValues = Array.from(
new Set(data.map(item => item[firstColumnName]))
).filter(Boolean)
// 获取当前页应该显示的值
const currentValue = uniqueValues[(currentPage - 1)]
// 返回第一列等于当前值的所有行
return data.filter(item => item[firstColumnName] === currentValue)
}
console.log(data)
const handleSave = () => {
const saveData = data.map(item => ({
...item,
isTraining: trainingStatus[item.key] || ''
}))
console.log('要保存的数据:', saveData)
// 这里添加保存到后端的逻辑
}
return (
<div className="p-4">
<div className="w-full mb-6 p-8 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50 hover:bg-gray-100 transition-colors cursor-pointer relative">
<input
type="file"
accept=".xlsx,.xls"
onChange={handleFileUpload}
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
/>
<div className="flex flex-col items-center justify-center text-gray-600">
<UploadOutlined className="text-3xl mb-2" />
<p className="text-lg font-medium"></p>
<p className="text-sm text-gray-500"> .xlsx, .xls Excel文件</p>
</div>
</div>
{data.length > 0 && (
<>
<Table
columns={columns}
dataSource={getPageData()}
pagination={false}
bordered
style={{
border: '1px solid #f0f0f0'
}}
className="!border-collapse [&_th]:!border [&_td]:!border [&_th]:!border-solid [&_td]:!border-solid [&_th]:!border-[#f0f0f0] [&_td]:!border-[#f0f0f0]"
/>
<div className="flex justify-end mt-4">
<button
onClick={handleSave}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
</button>
</div>
<Pagination
className="mt-4"
current={currentPage}
total={Array.from(new Set(data.map(item =>
item[(columns[0] as ColumnType<TableData>)?.dataIndex as string]
))).filter(Boolean).length}
pageSize={1}
onChange={(page) => {
setCurrentPage(page)
}}
/>
</>
)}
</div>
)
}

View File

@ -8,20 +8,21 @@ import { useMainContext } from "../layout/MainProvider";
import StaffTable from "./stafftable/page"; import StaffTable from "./stafftable/page";
import StaffModal from "./staffmodal/page"; import StaffModal from "./staffmodal/page";
export default function StaffMessage() { export default function StaffMessage() {
const {form, formValue, setFormValue, setVisible, setSearchValue} = useMainContext(); const {form, formValue, setFormValue, setVisible, setSearchValue, editingRecord} = useMainContext();
useEffect(()=>{ useEffect(()=>{
setFormValue( setFormValue(
{ {
username: "", username: "",
deptId: "", deptId: "",
absent: false, absent: false,
position: "", positionId: "",
trainSituations: "", trainSituations: "",
} }
) )
},[]) },[])
const handleNew = () => { const handleNew = () => {
form.setFieldsValue(formValue); form.setFieldsValue(formValue);
console.log(editingRecord);
setVisible(true); setVisible(true);
} }
// 添加防抖的搜索处理函数 // 添加防抖的搜索处理函数

View File

@ -2,12 +2,12 @@ import { api, useStaff } from "@nice/client";
import { useMainContext } from "../../layout/MainProvider"; import { useMainContext } from "../../layout/MainProvider";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { Button, Form, Input, Modal, Select } from "antd"; import { Button, Form, Input, Modal, Select } from "antd";
import DepartmentSelect from "@web/src/components/models/department/department-select";
import { useEffect } from "react"; import { useEffect } from "react";
import TrainContentTreeSelect from "@web/src/components/models/trainContent/train-content-tree-select";
import DepartmentChildrenSelect from "@web/src/components/models/department/department-children-select";
export default function StaffModal() { export default function StaffModal() {
const { data: traincontents} = api.trainSituation.findMany.useQuery({ const { data: traincontents} = api.trainSituation.findMany.useQuery({
select:{ select:{
id: true, id: true,
trainContent:{ trainContent:{
@ -18,79 +18,88 @@ export default function StaffModal() {
} }
}, },
} }
});
useEffect(() => {
traincontents?.forEach((situation)=>{
console.log(situation.trainContent.title);
});
}, [traincontents]);
const { form, formValue, setVisible, visible, editingRecord } = useMainContext() });
// useEffect(() => {
// traincontents?.forEach((situation)=>{
// console.log(situation.id);
// });
// }, [traincontents]);
const { form, formValue, setVisible, visible, editingRecord, setEditingRecord } = useMainContext()
const { create, update } = useStaff(); const { create, update } = useStaff();
const handleOk = async () => { const handleOk = async () => {
const values = await form.getFieldsValue(); const values = await form.getFieldsValue();
console.log(values.username);
try { try {
if (editingRecord && editingRecord.id) { const staffData = {
const result = await update.mutateAsync(
{
where: {
id: editingRecord.id,
},
data: {
username: values.username, username: values.username,
deptId: values.deptId,
position: values.position,
absent: values.absent,
trainSituations: values.trainSituations ? {
upsert: values.trainSituations.map((situation) => ({
where: { id: situation.id || "" },
update: {
mustTrainTime: situation.mustTrainTime,
trainContent: { connect: { id: situation.trainContentId } },
// 其他字段...
},
}))
} : undefined,
updatedAt: new Date()
} as any
}
);
// console.log(result);
} else {
await create.mutateAsync(
{
data: {
username: values.username,
deptId: values.deptId,
createdAt: new Date(),
showname: values.username, showname: values.username,
department: values.deptId ? {
connect: { id: values.deptId }
} : undefined,
position: values.positionId ? {
connect: { id: values.positionId }
} : undefined,
absent: values.absent, absent: values.absent,
trainSituations: { trainSituations: values.trainSituations?.length > 0 ? {
create: values.trainSituations.map((situation) => ({ create: values.trainSituations.map((situation) => ({
trainContent: { connect: { id: situation.trainContentId } }, trainContent: {
mustTrainTime: situation.mustTrainTime, connect: { id: situation.trainContentId }
// 其他必填字段... },
mustTrainTime: parseFloat(situation.mustTrainTime) || 0,
alreadyTrainTime: 0,
score: 0
})) }))
} : undefined
};
if (editingRecord?.id) {
await update.mutateAsync({
where: { id: editingRecord.id },
data: {
...staffData,
trainSituations: {
deleteMany: {},
create: values.trainSituations?.map((situation) => ({
trainContent: {
connect: { id: situation.trainContentId }
},
mustTrainTime: parseFloat(situation.mustTrainTime) || 0,
alreadyTrainTime: situation.alreadyTrainTime || 0,
// score: situation.score || 0
})) || []
} }
} }
});
} else {
await create.mutateAsync({
data: staffData
});
} }
);
}
toast.success("保存成功"); toast.success("保存成功");
setVisible(false); setVisible(false);
setEditingRecord(null);
form.resetFields();
} catch (error) { } catch (error) {
console.error("保存失败:", error);
toast.error("保存失败"); toast.error("保存失败");
throw error;
} }
}; };
const handleCancel = () => { const handleCancel = () => {
setVisible(false); setVisible(false);
setEditingRecord(null);
form.resetFields();
}; };
useEffect(() => {
if (visible&&editingRecord) {
form.setFieldsValue(editingRecord);
}
}, [visible,editingRecord]);
return ( return (
<> <>
{/* 模态框样式更新 */}
<Modal <Modal
title="编辑员工信息" title="编辑员工信息"
visible={visible} visible={visible}
@ -104,21 +113,18 @@ export default function StaffModal() {
<Form.Item <Form.Item
name={"username"} name={"username"}
label="姓名" label="姓名"
// labelClassName="text-gray-300"
> >
<Input className="rounded-lg" /> <Input className="rounded-lg" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={"deptId"} name={"deptId"}
label="部门" label="部门"
// labelClassName="text-gray-300"
> >
<DepartmentSelect></DepartmentSelect> <DepartmentChildrenSelect></DepartmentChildrenSelect>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={"positionId"} name={["position", "type"]}
label="职务" label="职务"
// labelClassName="text-gray-300"
> >
<Input className="rounded-lg" /> <Input className="rounded-lg" />
</Form.Item> </Form.Item>
@ -139,11 +145,11 @@ export default function StaffModal() {
<div key={key} className="flex space-x-2"> <div key={key} className="flex space-x-2">
<Form.Item <Form.Item
{...restField} {...restField}
name={[name, 'trainContent']} name={[name, 'trainContentId']} // 从 trainContent 改为 trainContentId
label="培训内容" label="培训内容"
className="flex-1" className="flex-1"
> >
<TrainContentTreeSelect></TrainContentTreeSelect> <TrainContentTreeSelect/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
{...restField} {...restField}
@ -162,7 +168,7 @@ export default function StaffModal() {
</Form.List> </Form.List>
</Form> </Form>
</Modal> </Modal>
<Button onClick={() =>{console.log(traincontents);}}>TEST</Button> {/* <Button onClick={() =>{console.log(traincontents);}}>TEST</Button> */}
</> </>
) )
} }

View File

@ -1,25 +1,60 @@
import { Button, Select, Table } from "antd" import { Button, Select, Table, Modal } from "antd"
import { StaffDto } from "@nice/common"; import { StaffDto } from "@nice/common";
import { useStaff, api } from "@nice/client"; import { useStaff, api } from "@nice/client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import React from "react"; import React from "react";
import { useMainContext } from "../../layout/MainProvider"; import { useMainContext } from "../../layout/MainProvider";
import { render } from "react-dom";
// 提取处理嵌套字段的函数
const getNestedValue = (record: any, dataIndex: string | string[]) => {
if (Array.isArray(dataIndex)) {
return dataIndex.reduce((obj, key) => obj?.[key], record);
}
return record[dataIndex];
};
export default function StaffTable() { export default function StaffTable() {
const{form, setVisible,searchValue} = useMainContext() const{form, setVisible,searchValue} = useMainContext()
const { data: staffs, isLoading } = api.staff.findMany.useQuery({ const { data: staffs, isLoading } = api.staff.findMany.useQuery({
where: { where: {
deletedAt: null,
username: { username: {
contains: searchValue contains: searchValue
} }
},
include: {
department: true,
position: true,
trainSituations: {
include: {
trainContent: {
select: {
id: true,
title: true
}
}
}
}
} }
}); });
// console.log(staffs.map((staff) => staff.absent)); useEffect(() => {
const { create, update } = useStaff(); console.log(staffs);
}, [staffs]);
const { softDeleteByIds } = useStaff();
const {editingRecord, setEditingRecord} = useMainContext(); const {editingRecord, setEditingRecord} = useMainContext();
const colnums = [ const [isTrainingModalVisible, setIsTrainingModalVisible] = useState(false);
const [selectedTrainings, setSelectedTrainings] = useState([]);
const showTrainingDetails = (situations) => {
setSelectedTrainings(situations);
setIsTrainingModalVisible(true);
};
// 修正拼写错误
const columns = [
{ {
title: "姓名", title: "姓名",
dataIndex: "username", dataIndex: "username",
@ -27,52 +62,81 @@ export default function StaffTable() {
}, },
{ {
title: "部门", title: "部门",
dataIndex: "deptId", dataIndex: ["department", "name"],
key: "deptId", key: "deptId",
render: (_, record) => record.department?.name || "无部门"
}, },
{ {
title: "职务", title: "职务",
dataIndex: "positionId", dataIndex: ["position", "type"],
key: "positionId", key: "position",
render: (_, record) => record.position?.type || "无职务"
}, },
{ {
title: "在位", title: "在位",
dataIndex: "absent", dataIndex: "absent",
key: "absent", key: "absent",
render: (_, record) => ( render: (absent) => absent === null ? "未知" : absent ? "否" : "是"
<Select
// defaultValue={record.absent ? "是" : "否"}
style={{ width: 120 }}
onChange={async (value) => {
try {
await update.mutateAsync({
where: { id: record.id },
data: { absent: value }
});
toast.success("状态更新成功");
} catch (error) {
toast.error("状态更新失败");
}
}}
>
<Select.Option value="是"></Select.Option>
<Select.Option value="否"></Select.Option>
</Select>
)
}, },
{ {
title: "应时", title: "培训情况",
dataIndex: "trainSituation", dataIndex: "trainSituations",
key: "trainSituation", key: "trainSituations",
render: (situations) => {
if (!situations?.length) return "无培训记录";
return (
<div>
{situations.slice(0, 2).map((s) => (
<div key={s.id} className="mb-1">
<span>{s.trainContent?.title}: </span>
<span> {s.alreadyTrainTime}/{s.mustTrainTime} </span>
</div>
))}
{situations.length > 2 && (
<span
className="text-blue-500 cursor-pointer hover:underline"
onClick={() => showTrainingDetails(situations)}
>
{situations.length - 2}
</span>
)}
</div>
);
}
}, },
{ {
title: "操作", title: "操作",
key: "action", key: "action",
render: (_, record) => ( render: (_, record) => (
<div className="flex space-x-2">
<Button <Button
type="primary" type="primary"
key={record.id} key={record.id}
onClick={() => handleEdit(record)}></Button> onClick={() => handleEdit(record)}
>
</Button>
<Button
danger
onClick={async () => {
if (confirm("确定要删除该员工吗?")) {
try {
await softDeleteByIds.mutateAsync({
ids: [record.id],
});
toast.success("删除成功");
} catch (error) {
// 添加更详细的错误日志
console.error('删除员工时出错:', error);
toast.error("删除失败");
}
}
}}
>
</Button>
</div>
), ),
} }
]; ];
@ -84,7 +148,7 @@ export default function StaffTable() {
}, [editingRecord]); }, [editingRecord]);
const handleEdit = (record) => { const handleEdit = (record) => {
setEditingRecord(record); setEditingRecord(record);
form.setFieldsValue(editingRecord); form.setFieldsValue(record); // 修正为设置当前记录的值
setVisible(true); setVisible(true);
}; };
return ( return (
@ -94,37 +158,21 @@ export default function StaffTable() {
( (
<Table <Table
key={"username"} key={"username"}
columns={colnums} columns={columns}
dataSource={staffs} dataSource={staffs}
className="table-auto w-full border-collapse border border-gray-300"
tableLayout="fixed" tableLayout="fixed"
pagination={{ pagination={{
position: ["bottomCenter"], position: ["bottomCenter"],
className: "flex justify-center mt-4", className: "flex justify-center mt-4",
pageSize: 12, pageSize: 10,
}} }}
onRow={(record) => ({
onMouseEnter: () => {
const row = document.querySelector(`tr[data-row-key="${record.id}"]`);
if (row) {
row.classList.add("bg-gray-100");
}
},
onMouseLeave: () => {
const row = document.querySelector(`tr[data-row-key="${record.id}"]`);
if (row) {
row.classList.remove("bg-gray-100");
}
},
})}
> >
<thead className="bg-gray-200"> <thead >
<tr> <tr>
{colnums.map((column) => ( {columns.map((column) => (
<th <th
key={column.key} key={column.key}
className="px-4 py-2 border border-gray-300 text-center font-bold"
> >
{column.title} {column.title}
</th> </th>
@ -135,14 +183,14 @@ export default function StaffTable() {
{staffs?.map((record) => ( {staffs?.map((record) => (
<tr <tr
key={record.id} key={record.id}
className="border border-gray-300"
> >
{colnums.map((column) => ( {columns.map((column) => (
<td // 使用提取的函数处理嵌套字段
key={column.key} <td key={column.key}>
className="px-4 py-2 border border-gray-300 text-center" {column.render?.(
> getNestedValue(record, column.dataIndex),
{column.render?.(record[column.dataIndex], record) || record[column.dataIndex]} record
) || getNestedValue(record, column.dataIndex)}
</td> </td>
))} ))}
</tr> </tr>
@ -151,8 +199,28 @@ export default function StaffTable() {
</Table> </Table>
) )
} }
<Modal
title="培训详情"
open={isTrainingModalVisible}
onCancel={() => setIsTrainingModalVisible(false)}
footer={null}
width={600}
>
<div className="max-h-96 overflow-y-auto">
{selectedTrainings.map((training) => (
<div key={training.id} className="mb-4 p-4 border rounded">
<h3 className="font-bold mb-2">{training.trainContent?.title}</h3>
<div className="grid grid-cols-2 gap-4">
<div>: {training.mustTrainTime} </div>
<div>: {training.alreadyTrainTime} </div>
<div>: {((training.alreadyTrainTime / training.mustTrainTime) * 100).toFixed(1)}%</div>
{/* <div>得分: {training.score}</div> */}
</div>
</div>
))}
</div>
</Modal>
</> </>
); );
} }

View File

@ -2,18 +2,25 @@ import { useAuth } from "@web/src/providers/auth-provider"
import { api } from "@nice/client" import { api } from "@nice/client"
import { Select } from "antd" import { Select } from "antd"
import { Department } from "packages/common/dist" import { Department } from "packages/common/dist"
import { useEffect } from "react"
export default function DepartmentChildrenSelect() { interface Props {
value?: string | null
onChange?: (value: string | null) => void
}
export default function DepartmentChildrenSelect({value, onChange}: Props) {
const { user, isAuthenticated } = useAuth() const { user, isAuthenticated } = useAuth()
// const { data: depts, isLoading: deptsLoading }
// const { user, isAuthenticated } = useAuth()
const { data: depts, isLoading: deptsLoading } const { data: depts, isLoading: deptsLoading }
: { data: Department[], isLoading: boolean }
= isAuthenticated ? api.department.getChildSimpleTree.useQuery({ = isAuthenticated ? api.department.getChildSimpleTree.useQuery({
rootId: user.deptId rootId: user.deptId
}) : { data: null, isLoading: false } }) : { data: null, isLoading: false }
const deptSelectOptions = depts?.map((dept) => ({ const deptSelectOptions = depts?.map((dept) => ({
label: dept.name, label: dept.title,
value: dept.id value: dept.id
})) }))
return ( return (
@ -21,6 +28,8 @@ export default function DepartmentChildrenSelect() {
placeholder="请选择单位" placeholder="请选择单位"
optionFilterProp="label" optionFilterProp="label"
options={deptSelectOptions} options={deptSelectOptions}
value={value}
onChange={onChange}
/> />
) )
} }

View File

@ -4,7 +4,11 @@ import { useAuth } from "@web/src/providers/auth-provider"
import { TreeSelect } from "antd" import { TreeSelect } from "antd"
import { useMemo } from "react" import { useMemo } from "react"
export default function TrainContentTreeSelect() { interface Props {
value?: string | null
onChange?: (value: string | null) => void
}
export default function TrainContentTreeSelect({ value, onChange }: Props) {
const { user, isAuthenticated } = useAuth() const { user, isAuthenticated } = useAuth()
const { data: trainContents, isLoading: trainContentsLoading } const { data: trainContents, isLoading: trainContentsLoading }
: { data: TrainContent[], isLoading: boolean } : { data: TrainContent[], isLoading: boolean }
@ -48,6 +52,8 @@ export default function TrainContentTreeSelect() {
treeData={treeData} treeData={treeData}
placeholder="请选择学科或课程" placeholder="请选择学科或课程"
treeDefaultExpandAll={false} treeDefaultExpandAll={false}
value={value}
onChange={onChange}
//onChange={onChange} //onChange={onChange}
/> />
) )

View File

@ -1,9 +1,10 @@
import TrainPlanCreateForm from "./TrainPlanCreateForm";
import TrainPlanWrite from "./TrainPlanWrite";
export default function DailyLayout(){ export default function DailyLayout(){
return ( return (
<div className="w-full h-[calc(100vh-100px)]"> <div className="w-full h-[calc(100vh-100px)]">
<TrainPlanCreateForm></TrainPlanCreateForm> <TrainPlanWrite></TrainPlanWrite>
</div> </div>
) )
} }

View File

@ -0,0 +1,3 @@
export default function TrainPlanWrite(){
return <div>TrainPlanWrite</div>
}

View File

@ -10,7 +10,9 @@ import LoginPage from "../app/login";
import HomePage from "../app/main/home/page"; import HomePage from "../app/main/home/page";
import StaffMessage from "../app/main/staffpage/page"; import StaffMessage from "../app/main/staffpage/page";
import MainLayout from "../app/main/layout/MainLayout"; import MainLayout from "../app/main/layout/MainLayout";
import DailyPage from "../app/main/dailyPage/page"; import DailyPage from "../app/main/daily/page";
import Dashboard from "../app/main/home/page";
import WeekPlanPage from "../app/main/plan/weekplan/page";
interface CustomIndexRouteObject extends IndexRouteObject { interface CustomIndexRouteObject extends IndexRouteObject {
name?: string; name?: string;
breadcrumb?: string; breadcrumb?: string;
@ -46,7 +48,7 @@ export const routes: CustomRouteObject[] = [
children: [ children: [
{ {
index: true, index: true,
element:<StaffMessage></StaffMessage>, element:<Dashboard></Dashboard>,
}, },
{ {
path: "/staff", path: "/staff",
@ -54,8 +56,39 @@ export const routes: CustomRouteObject[] = [
}, },
{ {
path:"/plan", path:"/plan",
children:[
{
path:"weekplan",
element:<WeekPlanPage></WeekPlanPage>
},
{
path:"monthplan",
element:<DailyPage></DailyPage> element:<DailyPage></DailyPage>
} }
]
},
{
path:"/daily",
element:<DailyPage></DailyPage>
},
{
path:"/assessment",
children:[
{
path:"positionassessment",
element:<DailyPage></DailyPage>
},
{
path:"commonassessment",
element:<DailyPage></DailyPage>
},
{
path:"sportsassessment",
element:<DailyPage></DailyPage>
}
]
},
], ],
}, },

View File

@ -2,7 +2,7 @@ server {
# 监听80端口 # 监听80端口
listen 80; listen 80;
# 服务器域名/IP地址使用环境变量 # 服务器域名/IP地址使用环境变量
server_name 192.168.43.206; server_name 192.168.252.77;
# 基础性能优化配置 # 基础性能优化配置
# 启用tcp_nopush以优化数据发送 # 启用tcp_nopush以优化数据发送
@ -100,7 +100,7 @@ server {
# 仅供内部使用 # 仅供内部使用
internal; internal;
# 代理到认证服务 # 代理到认证服务
proxy_pass http://192.168.43.206:3001/auth/file; proxy_pass http://192.168.252.77:3000/auth/file;
# 请求优化:不传递请求体 # 请求优化:不传递请求体
proxy_pass_request_body off; proxy_pass_request_body off;

View File

@ -264,7 +264,7 @@ importers:
specifier: ~32.3.2 specifier: ~32.3.2
version: 32.3.3 version: 32.3.3
'@ant-design/icons': '@ant-design/icons':
specifier: ^5.4.0 specifier: ^5.5.2
version: 5.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 5.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@dnd-kit/core': '@dnd-kit/core':
specifier: ^6.3.1 specifier: ^6.3.1
@ -317,6 +317,9 @@ importers:
'@trpc/server': '@trpc/server':
specifier: 11.0.0-rc.456 specifier: 11.0.0-rc.456
version: 11.0.0-rc.456 version: 11.0.0-rc.456
'@types/react-redux':
specifier: ^7.1.34
version: 7.1.34
'@xyflow/react': '@xyflow/react':
specifier: ^12.3.6 specifier: ^12.3.6
version: 12.3.6(@types/react@18.2.38)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 12.3.6(@types/react@18.2.38)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@ -330,7 +333,7 @@ importers:
specifier: ~32.3.2 specifier: ~32.3.2
version: 32.3.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 32.3.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
antd: antd:
specifier: ^5.19.3 specifier: ^5.23.0
version: 5.23.0(date-fns@2.30.0)(luxon@3.5.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 5.23.0(date-fns@2.30.0)(luxon@3.5.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
axios: axios:
specifier: ^1.7.2 specifier: ^1.7.2
@ -386,6 +389,9 @@ importers:
react-hot-toast: react-hot-toast:
specifier: ^2.4.1 specifier: ^2.4.1
version: 2.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 2.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react-redux:
specifier: ^9.2.0
version: 9.2.0(@types/react@18.2.38)(react@18.2.0)
react-resizable: react-resizable:
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) version: 3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@ -404,6 +410,9 @@ importers:
uuid: uuid:
specifier: ^10.0.0 specifier: ^10.0.0
version: 10.0.0 version: 10.0.0
xlsx:
specifier: ^0.18.5
version: 0.18.5
yjs: yjs:
specifier: ^13.6.20 specifier: ^13.6.20
version: 13.6.21 version: 13.6.21
@ -420,6 +429,9 @@ importers:
'@types/react-dom': '@types/react-dom':
specifier: 18.2.15 specifier: 18.2.15
version: 18.2.15 version: 18.2.15
'@types/xlsx':
specifier: ^0.0.36
version: 0.0.36
'@vitejs/plugin-react-swc': '@vitejs/plugin-react-swc':
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.7.2(@swc/helpers@0.5.15)(vite@5.4.11(@types/node@20.17.12)(less@4.2.2)(terser@5.37.0)) version: 3.7.2(@swc/helpers@0.5.15)(vite@5.4.11(@types/node@20.17.12)(less@4.2.2)(terser@5.37.0))
@ -1919,79 +1931,67 @@ packages:
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.0.5': '@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.0.4': '@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.0.4': '@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.0.4': '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.0.4': '@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.33.5': '@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.33.5': '@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.33.5': '@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.33.5': '@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.33.5': '@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.33.5': '@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.33.5': '@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@ -2459,61 +2459,51 @@ packages:
resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.30.1': '@rollup/rollup-linux-arm-musleabihf@4.30.1':
resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.30.1': '@rollup/rollup-linux-arm64-gnu@4.30.1':
resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.30.1': '@rollup/rollup-linux-arm64-musl@4.30.1':
resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loongarch64-gnu@4.30.1': '@rollup/rollup-linux-loongarch64-gnu@4.30.1':
resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-powerpc64le-gnu@4.30.1': '@rollup/rollup-linux-powerpc64le-gnu@4.30.1':
resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.30.1': '@rollup/rollup-linux-riscv64-gnu@4.30.1':
resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-s390x-gnu@4.30.1': '@rollup/rollup-linux-s390x-gnu@4.30.1':
resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.30.1': '@rollup/rollup-linux-x64-gnu@4.30.1':
resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.30.1': '@rollup/rollup-linux-x64-musl@4.30.1':
resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.30.1': '@rollup/rollup-win32-arm64-msvc@4.30.1':
resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==}
@ -2872,28 +2862,24 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@swc/core-linux-arm64-musl@1.10.6': '@swc/core-linux-arm64-musl@1.10.6':
resolution: {integrity: sha512-hB2xZFmXCKf2iJF5y2z01PSuLqEoUP3jIX/XlIHN+/AIP7PkSKsValE63LnjlnWPnSEI0IxUyRE3T3FzWE/fQQ==} resolution: {integrity: sha512-hB2xZFmXCKf2iJF5y2z01PSuLqEoUP3jIX/XlIHN+/AIP7PkSKsValE63LnjlnWPnSEI0IxUyRE3T3FzWE/fQQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@swc/core-linux-x64-gnu@1.10.6': '@swc/core-linux-x64-gnu@1.10.6':
resolution: {integrity: sha512-PRGPp0I22+oJ8RMGg8M4hXYxEffH3ayu0WoSDPOjfol1F51Wj1tfTWN4wVa2RibzJjkBwMOT0KGLGb/hSEDDXQ==} resolution: {integrity: sha512-PRGPp0I22+oJ8RMGg8M4hXYxEffH3ayu0WoSDPOjfol1F51Wj1tfTWN4wVa2RibzJjkBwMOT0KGLGb/hSEDDXQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@swc/core-linux-x64-musl@1.10.6': '@swc/core-linux-x64-musl@1.10.6':
resolution: {integrity: sha512-SoNBxlA86lnoV9vIz/TCyakLkdRhFSHx6tFMKNH8wAhz1kKYbZfDmpYoIzeQqdTh0tpx8e/Zu1zdK4smovsZqQ==} resolution: {integrity: sha512-SoNBxlA86lnoV9vIz/TCyakLkdRhFSHx6tFMKNH8wAhz1kKYbZfDmpYoIzeQqdTh0tpx8e/Zu1zdK4smovsZqQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@swc/core-win32-arm64-msvc@1.10.6': '@swc/core-win32-arm64-msvc@1.10.6':
resolution: {integrity: sha512-6L5Y2E+FVvM+BtoA+mJFjf/SjpFr73w2kHBxINxwH8/PkjAjkePDr5m0ibQhPXV61bTwX49+1otzTY85EsUW9Q==} resolution: {integrity: sha512-6L5Y2E+FVvM+BtoA+mJFjf/SjpFr73w2kHBxINxwH8/PkjAjkePDr5m0ibQhPXV61bTwX49+1otzTY85EsUW9Q==}
@ -3068,6 +3054,9 @@ packages:
'@types/graceful-fs@4.1.9': '@types/graceful-fs@4.1.9':
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
'@types/hoist-non-react-statics@3.3.6':
resolution: {integrity: sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==}
'@types/http-errors@2.0.4': '@types/http-errors@2.0.4':
resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
@ -3143,6 +3132,9 @@ packages:
'@types/react-dom@18.2.15': '@types/react-dom@18.2.15':
resolution: {integrity: sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==} resolution: {integrity: sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==}
'@types/react-redux@7.1.34':
resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==}
'@types/react@18.2.38': '@types/react@18.2.38':
resolution: {integrity: sha512-cBBXHzuPtQK6wNthuVMV6IjHAFkdl/FOPFIlkd81/Cd1+IqkHu/A+w4g43kaQQoYHik/ruaQBDL72HyCy1vuMw==} resolution: {integrity: sha512-cBBXHzuPtQK6wNthuVMV6IjHAFkdl/FOPFIlkd81/Cd1+IqkHu/A+w4g43kaQQoYHik/ruaQBDL72HyCy1vuMw==}
@ -3170,12 +3162,19 @@ packages:
'@types/supertest@6.0.2': '@types/supertest@6.0.2':
resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==}
'@types/use-sync-external-store@0.0.6':
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
'@types/uuid@10.0.0': '@types/uuid@10.0.0':
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
'@types/ws@8.5.13': '@types/ws@8.5.13':
resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
'@types/xlsx@0.0.36':
resolution: {integrity: sha512-mvfrKiKKMErQzLMF8ElYEH21qxWCZtN59pHhWGmWCWFJStYdMWjkDSAy6mGowFxHXaXZWe5/TW7pBUiWclIVOw==}
deprecated: This is a stub types definition for xlsx (https://github.com/sheetjs/js-xlsx). xlsx provides its own type definitions, so you don't need @types/xlsx installed!
'@types/yargs-parser@21.0.3': '@types/yargs-parser@21.0.3':
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
@ -3515,6 +3514,10 @@ packages:
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
hasBin: true hasBin: true
adler-32@1.3.1:
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
engines: {node: '>=0.8'}
ag-charts-community@10.3.3: ag-charts-community@10.3.3:
resolution: {integrity: sha512-LUlbVS+4sX1UHqZNuE9m3LISr7D4FEO/Y88fwit1fYPTk2l1ZJG/82gFnm5bQ+iG4QgNIwRa2TWm7w2MLnyJlA==} resolution: {integrity: sha512-LUlbVS+4sX1UHqZNuE9m3LISr7D4FEO/Y88fwit1fYPTk2l1ZJG/82gFnm5bQ+iG4QgNIwRa2TWm7w2MLnyJlA==}
@ -3879,6 +3882,10 @@ packages:
caniuse-lite@1.0.30001690: caniuse-lite@1.0.30001690:
resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==} resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==}
cfb@1.2.2:
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
engines: {node: '>=0.8'}
chainsaw@0.1.0: chainsaw@0.1.0:
resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}
@ -3989,6 +3996,10 @@ packages:
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
codepage@1.15.0:
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
engines: {node: '>=0.8'}
collect-v8-coverage@1.0.2: collect-v8-coverage@1.0.2:
resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==}
@ -4815,6 +4826,10 @@ packages:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
frac@1.1.2:
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
engines: {node: '>=0.8'}
fraction.js@4.3.7: fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@ -5005,6 +5020,9 @@ packages:
hls.js@1.5.18: hls.js@1.5.18:
resolution: {integrity: sha512-znxR+2jecWluu/0KOBqUcvVyAB5tLff10vjMGrpAlz1eFY+ZhF1bY3r82V+Bk7WJdk03iTjtja9KFFz5BrqjSA==} resolution: {integrity: sha512-znxR+2jecWluu/0KOBqUcvVyAB5tLff10vjMGrpAlz1eFY+ZhF1bY3r82V+Bk7WJdk03iTjtja9KFFz5BrqjSA==}
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
hosted-git-info@2.8.9: hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
@ -6664,6 +6682,18 @@ packages:
react-is@18.3.1: react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
react-redux@9.2.0:
resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==}
peerDependencies:
'@types/react': ^18.2.25 || ^19
react: ^18.0 || ^19
redux: ^5.0.0
peerDependenciesMeta:
'@types/react':
optional: true
redux:
optional: true
react-refresh@0.14.2: react-refresh@0.14.2:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -6731,6 +6761,9 @@ packages:
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
engines: {node: '>=4'} engines: {node: '>=4'}
redux@4.2.1:
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
reflect-metadata@0.2.2: reflect-metadata@0.2.2:
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
@ -7053,6 +7086,10 @@ packages:
sprintf-js@1.0.3: sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
ssf@0.11.2:
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
engines: {node: '>=0.8'}
stack-utils@2.0.6: stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -7686,10 +7723,18 @@ packages:
engines: {node: '>= 8'} engines: {node: '>= 8'}
hasBin: true hasBin: true
wmf@1.0.2:
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
engines: {node: '>=0.8'}
word-wrap@1.2.5: word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'} 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@5.1.0: wrap-ansi@5.1.0:
resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -7737,6 +7782,11 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
xlsx@0.18.5:
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
engines: {node: '>=0.8'}
hasBin: true
xml2js@0.6.2: xml2js@0.6.2:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
@ -10598,6 +10648,11 @@ snapshots:
dependencies: dependencies:
'@types/node': 20.17.12 '@types/node': 20.17.12
'@types/hoist-non-react-statics@3.3.6':
dependencies:
'@types/react': 18.2.38
hoist-non-react-statics: 3.3.2
'@types/http-errors@2.0.4': {} '@types/http-errors@2.0.4': {}
'@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-coverage@2.0.6': {}
@ -10667,6 +10722,13 @@ snapshots:
dependencies: dependencies:
'@types/react': 18.3.18 '@types/react': 18.3.18
'@types/react-redux@7.1.34':
dependencies:
'@types/hoist-non-react-statics': 3.3.6
'@types/react': 18.2.38
hoist-non-react-statics: 3.3.2
redux: 4.2.1
'@types/react@18.2.38': '@types/react@18.2.38':
dependencies: dependencies:
'@types/prop-types': 15.7.14 '@types/prop-types': 15.7.14
@ -10707,12 +10769,18 @@ snapshots:
'@types/methods': 1.1.4 '@types/methods': 1.1.4
'@types/superagent': 8.1.9 '@types/superagent': 8.1.9
'@types/use-sync-external-store@0.0.6': {}
'@types/uuid@10.0.0': {} '@types/uuid@10.0.0': {}
'@types/ws@8.5.13': '@types/ws@8.5.13':
dependencies: dependencies:
'@types/node': 20.17.12 '@types/node': 20.17.12
'@types/xlsx@0.0.36':
dependencies:
xlsx: 0.18.5
'@types/yargs-parser@21.0.3': {} '@types/yargs-parser@21.0.3': {}
'@types/yargs@17.0.33': '@types/yargs@17.0.33':
@ -11209,6 +11277,8 @@ snapshots:
acorn@8.14.0: {} acorn@8.14.0: {}
adler-32@1.3.1: {}
ag-charts-community@10.3.3: ag-charts-community@10.3.3:
dependencies: dependencies:
ag-charts-locale: 10.3.3 ag-charts-locale: 10.3.3
@ -11684,6 +11754,11 @@ snapshots:
caniuse-lite@1.0.30001690: {} caniuse-lite@1.0.30001690: {}
cfb@1.2.2:
dependencies:
adler-32: 1.3.1
crc-32: 1.2.2
chainsaw@0.1.0: chainsaw@0.1.0:
dependencies: dependencies:
traverse: 0.3.9 traverse: 0.3.9
@ -11785,6 +11860,8 @@ snapshots:
co@4.6.0: {} co@4.6.0: {}
codepage@1.15.0: {}
collect-v8-coverage@1.0.2: {} collect-v8-coverage@1.0.2: {}
color-convert@1.9.3: color-convert@1.9.3:
@ -12747,6 +12824,8 @@ snapshots:
forwarded@0.2.0: {} forwarded@0.2.0: {}
frac@1.1.2: {}
fraction.js@4.3.7: {} fraction.js@4.3.7: {}
framer-motion@11.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): framer-motion@11.16.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
@ -12925,6 +13004,10 @@ snapshots:
hls.js@1.5.18: {} hls.js@1.5.18: {}
hoist-non-react-statics@3.3.2:
dependencies:
react-is: 16.13.1
hosted-git-info@2.8.9: {} hosted-git-info@2.8.9: {}
hosted-git-info@4.1.0: hosted-git-info@4.1.0:
@ -14789,6 +14872,14 @@ snapshots:
react-is@18.3.1: {} react-is@18.3.1: {}
react-redux@9.2.0(@types/react@18.2.38)(react@18.2.0):
dependencies:
'@types/use-sync-external-store': 0.0.6
react: 18.2.0
use-sync-external-store: 1.4.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.2.38
react-refresh@0.14.2: {} react-refresh@0.14.2: {}
react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
@ -14869,6 +14960,10 @@ snapshots:
dependencies: dependencies:
redis-errors: 1.2.0 redis-errors: 1.2.0
redux@4.2.1:
dependencies:
'@babel/runtime': 7.26.0
reflect-metadata@0.2.2: {} reflect-metadata@0.2.2: {}
regenerator-runtime@0.14.1: {} regenerator-runtime@0.14.1: {}
@ -15256,6 +15351,10 @@ snapshots:
sprintf-js@1.0.3: {} sprintf-js@1.0.3: {}
ssf@0.11.2:
dependencies:
frac: 1.1.2
stack-utils@2.0.6: stack-utils@2.0.6:
dependencies: dependencies:
escape-string-regexp: 2.0.0 escape-string-regexp: 2.0.0
@ -15894,8 +15993,12 @@ snapshots:
dependencies: dependencies:
isexe: 2.0.0 isexe: 2.0.0
wmf@1.0.2: {}
word-wrap@1.2.5: {} word-wrap@1.2.5: {}
word@0.3.0: {}
wrap-ansi@5.1.0: wrap-ansi@5.1.0:
dependencies: dependencies:
ansi-styles: 3.2.1 ansi-styles: 3.2.1
@ -15931,6 +16034,16 @@ snapshots:
ws@8.18.0: {} ws@8.18.0: {}
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
xml2js@0.6.2: xml2js@0.6.2:
dependencies: dependencies:
sax: 1.4.1 sax: 1.4.1