瀏覽代碼

20230221: Integreated Proposal and Workgroup in dashboard

mkbeefcake 1 年之前
父節點
當前提交
f346989778

+ 1 - 20
package-lock.json

@@ -3263,7 +3263,6 @@
       "version": "16.14.35",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.35.tgz",
       "integrity": "sha512-NUEiwmSS1XXtmBcsm1NyRRPYjoZF2YTE89/5QiLt5mlGffYK9FQqOKuOLuXNrjPQV04oQgaZG+Yq02ZfHoFyyg==",
-      "dev": true,
       "requires": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -3273,8 +3272,7 @@
         "csstype": {
           "version": "3.1.1",
           "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
-          "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
-          "dev": true
+          "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
         }
       }
     },
@@ -3321,23 +3319,6 @@
       "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
       "requires": {
         "@types/react": "*"
-      },
-      "dependencies": {
-        "@types/react": {
-          "version": "18.0.28",
-          "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz",
-          "integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==",
-          "requires": {
-            "@types/prop-types": "*",
-            "@types/scheduler": "*",
-            "csstype": "^3.0.2"
-          }
-        },
-        "csstype": {
-          "version": "3.1.1",
-          "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
-          "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
-        }
       }
     },
     "@types/react-redux": {

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
     "@apollo/client": "^3.7.8",
     "@joystream/types": "0.20.5",
     "@material-ui/core": "^4.12.3",
-    "@material-ui/data-grid": "^4.0.0-alpha.35",
+    "@material-ui/data-grid": "^4.0.0-alpha.37",
     "@material-ui/icons": "^4.11.2",
     "@material-ui/lab": "^4.0.0-alpha.60",
     "axios": "^0.21.1",

+ 2 - 2
src/components/Dashboard/Channels.tsx

@@ -12,8 +12,8 @@ const Channels = (props: { council: ElectedCouncil | undefined }) => {
     <SubBlock title="Channels">
       { !loading && (
         <>
-          <Line content={"created"} value={created} />
-          <Line content={"total"} value={total} />
+          <Line content={"Created"} value={created} />
+          <Line content={"Total"} value={total} />
         </>
       )}
     </SubBlock>

+ 3 - 3
src/components/Dashboard/Election.tsx

@@ -13,9 +13,9 @@ const Election = (props: { council: ElectedCouncil | undefined}) => {
     <SubBlock title="Election">
       { !loading && (
         <>
-          <Line content={"candidates"} value={election? election.candidates.length : "-"} />
-          <Line content={"votes"} value={election? election.castVotes.length: "-"} />
-          <Line content={"staked"} value={election? sumStakes(election.candidates).toString().slice(0, length - 10): "-"} />
+          <Line content={"Candidates"} value={election? election.candidates.length : "-"} />
+          <Line content={"Votes"} value={election? election.castVotes.length: "-"} />
+          <Line content={"Staked"} value={election? sumStakes(election.candidates).toString().slice(0, length - 10): "-"} />
         </>
       )}
     </SubBlock>

+ 4 - 4
src/components/Dashboard/Forum.tsx

@@ -13,14 +13,14 @@ const Forum = (props: { council: ElectedCouncil | undefined}) => {
     <SubBlock title="Forum">
       { !thread.loading && (
         <>
-          <Line content={"threads new"} value={thread.created} />
-          <Line content={"threads total"} value={thread.total} />
+          <Line content={"Threads created"} value={thread.created} />
+          <Line content={"Threads total"} value={thread.total} />
         </>
       )}
       { !post.loading && (
         <>
-          <Line content={"posts new"} value={post.created} />
-          <Line content={"posts total"} value={post.total} />
+          <Line content={"Posts created"} value={post.created} />
+          <Line content={"Posts total"} value={post.total} />
         </>
       )}
     </SubBlock>

+ 70 - 0
src/components/Dashboard/Proposals.tsx

@@ -0,0 +1,70 @@
+import React from "react";
+import SubBlock from "./ui/SubBlock";
+import { ElectedCouncil } from "@/queries";
+import { useProposals } from '@/hooks';
+import { WorkingGroup } from "../../types";
+
+import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles';
+import Table from "@material-ui/core/Table";
+import TableBody from "@material-ui/core/TableBody";
+import TableCell from "@material-ui/core/TableCell";
+import TableContainer from "@material-ui/core/TableContainer";
+import TableHead from "@material-ui/core/TableHead";
+import TableRow from "@material-ui/core/TableRow";
+import TableFooter from '@material-ui/core/TableFooter';
+import TablePagination from '@material-ui/core/TablePagination';
+import IconButton from '@material-ui/core/IconButton';
+import FirstPageIcon from '@material-ui/icons/FirstPage';
+import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
+import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
+import LastPageIcon from '@material-ui/icons/LastPage';
+
+const Proposals = (props: { council: ElectedCouncil | undefined}) => {
+  const { council } = props;
+  const { proposals, loading, error } = useProposals({ council });
+
+	return (
+    <SubBlock title="Proposals" stretch={true}>
+      { !loading && (
+				<>
+					<TableContainer>
+						<Table aria-label="proposals table">
+							<TableHead>
+								<TableRow>
+									<TableCell>Title</TableCell>
+									<TableCell>Created Date</TableCell>
+									<TableCell>Link</TableCell>
+									<TableCell>Status</TableCell>
+								</TableRow>
+							</TableHead>
+							{proposals && ( <>
+								<TableBody>
+									{
+										proposals.map((proposal) => 
+											<TableRow key={proposal.id}>
+												<TableCell>{proposal.title}</TableCell>
+												<TableCell><i>{proposal.createdAt}</i></TableCell>
+												<TableCell>
+													<a
+														href={`https://pioneerapp.xyz/#/proposals/preview/${proposal.id}`}
+														target="_blank"
+														rel="noreferrer"
+													>
+														Link to porposal
+													</a>
+												</TableCell>
+												<TableCell><i>{proposal.status}</i></TableCell>
+											</TableRow>								
+										)
+									}
+								</TableBody>
+							</>)}
+						</Table>
+					</TableContainer>
+				</>
+      )}
+    </SubBlock>
+  );
+};
+
+export default Proposals;

+ 3 - 3
src/components/Dashboard/Validation.tsx

@@ -14,9 +14,9 @@ const Validation = (props: { council: ElectedCouncil | undefined }) => {
     <SubBlock title="Validation">
       { !loading && (
         <>
-          <Line content={"count"} value={validator} />
-          <Line content={"minted"} value={mint} />
-          <Line content={"staked"} value={stake} />
+          <Line content={"Count"} value={validator} />
+          <Line content={"Minted"} value={mint} />
+          <Line content={"Staked"} value={stake} />
         </>
       )}
     </SubBlock>

+ 2 - 2
src/components/Dashboard/Videos.tsx

@@ -12,8 +12,8 @@ const Videos = (props: { council: ElectedCouncil | undefined}) => {
     <SubBlock title="Videos">
       { !loading && (
         <>
-          <Line content={"created"} value={created} />
-          <Line content={"total"} value={total} />
+          <Line content={"Created"} value={created} />
+          <Line content={"Total"} value={total} />
         </>
       )}
     </SubBlock>

+ 211 - 0
src/components/Dashboard/Workgroup.tsx

@@ -0,0 +1,211 @@
+import React from "react";
+import SubBlock from "./ui/SubBlock";
+import { ElectedCouncil } from "@/queries";
+import { useWorkingGroups, useWorker } from '@/hooks';
+import { WorkingGroup } from "../../types";
+
+import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles';
+import Table from "@material-ui/core/Table";
+import TableBody from "@material-ui/core/TableBody";
+import TableCell from "@material-ui/core/TableCell";
+import TableContainer from "@material-ui/core/TableContainer";
+import TableHead from "@material-ui/core/TableHead";
+import TableRow from "@material-ui/core/TableRow";
+import TableFooter from '@material-ui/core/TableFooter';
+import TablePagination from '@material-ui/core/TablePagination';
+import IconButton from '@material-ui/core/IconButton';
+import FirstPageIcon from '@material-ui/icons/FirstPage';
+import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
+import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
+import LastPageIcon from '@material-ui/icons/LastPage';
+
+const useStyles1 = makeStyles((theme: Theme) =>
+  createStyles({
+    root: {
+      flexShrink: 0,
+      marginLeft: theme.spacing(2.5),
+    },
+  }),
+);
+
+interface TablePaginationActionsProps {
+  count: number;
+  page: number;
+  rowsPerPage: number;
+  onPageChange: (event: React.MouseEvent<HTMLButtonElement>, newPage: number) => void;
+}
+
+const TablePaginationActions = (props: TablePaginationActionsProps) => {
+  const theme = useTheme();
+  const { count, page, rowsPerPage, onPageChange } = props;
+
+  const handleFirstPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
+		console.log('first button is clicked')
+    onPageChange(event, 0);
+  };
+
+  const handleBackButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
+		console.log('back button is clicked')
+    onPageChange(event, page - 1);
+  };
+
+  const handleNextButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
+		console.log('next button is clicked')
+    onPageChange(event, page + 1);
+  };
+
+  const handleLastPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
+		console.log('last button is clicked')
+    onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
+  };
+
+  return (
+    <div >
+      <IconButton
+        onClick={handleFirstPageButtonClick}
+        disabled={page === 0}
+        aria-label="first page"
+      >
+        {theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
+      </IconButton>
+      <IconButton onClick={handleBackButtonClick} disabled={page === 0} aria-label="previous page">
+        {theme.direction === 'rtl' ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
+      </IconButton>
+      <IconButton
+        onClick={handleNextButtonClick}
+        disabled={page >= Math.ceil(count / rowsPerPage) - 1}
+        aria-label="next page"
+      >
+        {theme.direction === 'rtl' ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
+      </IconButton>
+      <IconButton
+        onClick={handleLastPageButtonClick}
+        disabled={page >= Math.ceil(count / rowsPerPage) - 1}
+        aria-label="last page"
+      >
+        {theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
+      </IconButton>
+    </div>
+  );
+}
+
+export function GroupWorkers(props: { council: ElectedCouncil, workingGroup : WorkingGroup }) {
+
+	const { council, workingGroup } = props;
+	const { workingTokens, rewardToken, workingTokensReward } = useWorkingGroups({ council });
+	const { exitedWorker, filledWorker, terminatedWorker } = useWorker({ council });
+
+	var token = workingTokens?.filter((data) => workingGroup.name === data.groupId).reduce((a: number, b) => {
+		return a + (b.budgetChangeAmount / 10000000000);
+	}, 0)
+
+	var reward = rewardToken?.filter((data) => workingGroup.name === data.groupId).reduce((a: number, b) => {
+		return a + (b.amount / 10000000000);
+	}, 0)
+
+	var updateReward = workingTokensReward?.filter((data) => workingGroup.name === data.groupId).reduce((a: number, b) => {
+		return a + (b.budgetChangeAmount / 10000000000);
+	}, 0)
+
+	var budget = updateReward! - reward!;
+
+
+	var exited = exitedWorker?.filter(data => workingGroup.name === data.groupId).reduce((a: number, b) => {
+		return isNaN(a + b.worker.length) ? 0 : a + b.worker.length;
+	}, 0)
+
+	var filled = filledWorker?.filter(data => workingGroup.name === data.groupId).reduce((a: number, b) => {
+		return isNaN(a + b.workersHired.length) ? 0 : a + b.workersHired.length;
+	}, 0)
+
+	var terminated = terminatedWorker?.filter(data => workingGroup.name === data.groupId).reduce((a: number, b) => {
+		return isNaN(a + b.worker.length) ? 0 : a + b.worker.length;
+	}, 0)
+
+	var worker = filled! - exited! - terminated!;
+
+	return (
+		<TableRow key={workingGroup.name}>
+			<TableCell>{workingGroup.name}</TableCell>
+			<TableCell><i>{Number.isNaN(worker) ? "-" : worker}</i></TableCell>
+			<TableCell><i>{token?.toFixed(0)}</i></TableCell>
+			<TableCell><i>{budget.toFixed(0)}</i></TableCell>
+		</TableRow>
+	);
+}
+
+const useStyles2 = makeStyles({
+	table: {
+		minWidth: 500,
+	}
+})
+
+const WorkGroup = (props: { council: ElectedCouncil | undefined}) => {
+  const { council } = props;
+  const { workingGroups, loading, error } = useWorkingGroups({ council });
+
+	const classes = useStyles2();
+	const [page, setPage] = React.useState(0);
+	const [rowsPerPage, setRowsPerPage] = React.useState(4);
+	
+  const handleChangePage = (event: unknown, newPage: number) => {
+		console.log(`handlechangepage : ${newPage}`)
+    setPage(newPage);
+  };
+
+  const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
+		console.log(`handleChangeRowsPerPage: ${parseInt(event.target.value, 10)}`)
+    setRowsPerPage(parseInt(event.target.value, 10));
+    setPage(0);
+  };
+
+	return (
+    <SubBlock title="WorkGroup" stretch={true}>
+      { !loading && (
+				<>
+					<TableContainer>
+						<Table className={classes.table} aria-label="working-group table">
+							<TableHead>
+								<TableRow>
+									<TableCell>Working Groups</TableCell>
+									<TableCell>Workers</TableCell>
+									<TableCell>Minted Tokens during Term</TableCell>
+									<TableCell>Budget/Debt at end of Term</TableCell>
+								</TableRow>
+							</TableHead>
+							{workingGroups && ( <>
+								<TableBody>
+									{ /*rowsPerPage > 0 ? 
+											workingGroups.slice(page * rowsPerPage, page *rowsPerPage + rowsPerPage)
+												.map((workingGroup) => <GroupWorkers key={workingGroup.id} council={council} workingGroup={workingGroup} />)
+										:*/ workingGroups.map((workingGroup) => <GroupWorkers key={workingGroup.id} council={council} workingGroup={workingGroup} />)
+									}
+								</TableBody>
+							</>)}
+							{/* <TableFooter>
+								<TableRow>
+									<TablePagination
+										rowsPerPageOptions={[4]}
+										colSpan={3}
+										count={workingGroups ? workingGroups.length : 0}
+										rowsPerPage={rowsPerPage}
+										page={page}
+										onPageChange={handleChangePage}
+										SelectProps={{
+											inputProps: { 'aria-label': 'rows per page' },
+											native: true,
+										}}
+										onRowsPerPageChange={handleChangeRowsPerPage}
+										ActionsComponent={TablePaginationActions}
+									/>
+								</TableRow>
+							</TableFooter> */}
+						</Table>
+					</TableContainer>
+				</>
+      )}
+    </SubBlock>
+  );
+};
+
+export default WorkGroup;

+ 7 - 4
src/components/Dashboard/index.tsx

@@ -7,10 +7,12 @@ import Videos from "./Videos";
 import Forum from "./Forum";
 import Election from "./Election";
 import Validation from "./Validation";
-import SubBlock from "./ui/SubBlock";
+import WorkGroup from './WorkGroup';
+
 import Banner from "./ui/Banner";
 import { useElectedCouncils } from '@/hooks';
 import { ElectedCouncil } from "@/types";
+import Proposals from "./Proposals";
 
 
 interface IProps extends IState {}
@@ -23,7 +25,8 @@ const Dashboard = (props: IProps) => {
 	const council: ElectedCouncil | undefined = data && data[0]
 
 	useEffect(() => {
-		if (!council) return
+		if (!council) 
+      return
 
 		setDescription1(
 			"Round: " + council.electionCycleId + 
@@ -54,10 +57,10 @@ const Dashboard = (props: IProps) => {
           />
         </Grid>
         <Grid container spacing={3}>
-          <Banner title="WG" description={description2}/>
+          <WorkGroup council={council}/>
         </Grid>
         <Grid container spacing={3}>
-          <Banner title="Proposals" description={description2}/>
+          <Proposals council={council} />
         </Grid>
       </Container>
     </div>

+ 5 - 3
src/components/Dashboard/ui/SubBlock.tsx

@@ -32,16 +32,18 @@ const useStyles = makeStyles((theme: Theme) =>
 
 const SubBlock = (props: {
   title: string,
+  stretch: boolean,
   children: any
-}) => {
+}) => { 
   const { 
     title,
-    children
+    stretch,
+    children,
   } = props;
   const classes = useStyles();
 
   return (
-    <Grid className={classes.grid} item xs={4} md={4} sm={12}>
+    <Grid className={classes.grid} item xs={stretch == true? 12: 4} md={stretch == true? 12: 4} sm={12}>
       <Paper className={classes.paper}>
         { title && 
           <AppBar className={classes.root} position="static">

+ 3 - 3
src/hooks/useWorker.ts

@@ -1,13 +1,13 @@
 import { useEffect, useMemo } from 'react';
 
-import { useGetTerminatedWorkdersLazyQuery, useGetWorkedExitedLazyQuery, useGetOpeningFilledLazyQuery } from '@/queries';
+import { useGetTerminatedWorkderLazyQuery, useGetWorkerExitedLazyQuery, useGetOpeningFilledLazyQuery } from '@/queries';
 import { asWorkingGroup } from '@/types';
 
 import { ForSelectedCouncil } from './types';
 
 export function useWorker({ council }: ForSelectedCouncil) {
-  const [fetchTerminated, terminatedQuery] = useGetTerminatedWorkdersLazyQuery();
-  const [fetchExited, exitedQuery] = useGetWorkedExitedLazyQuery();
+  const [fetchTerminated, terminatedQuery] = useGetTerminatedWorkderLazyQuery();
+  const [fetchExited, exitedQuery] = useGetWorkerExitedLazyQuery();
   const [fetchFilled, filledQuery] = useGetOpeningFilledLazyQuery();
 
   useEffect(() => {

+ 3 - 3
src/hooks/useWorkingGroups.ts

@@ -1,6 +1,6 @@
 import { useEffect, useMemo } from 'react';
 
-import { useGetWorkingGroupsLazyQuery, useGetWorkingGroupTokenLazyQuery, useGetRewardsLazyQuery } from '@/queries';
+import { useGetWorkingGroupsLazyQuery, useWorkingGroupTokenLazyQuery, useGetRewardsLazyQuery } from '@/queries';
 import { asWorkingGroup } from '@/types';
 
 import { ForSelectedCouncil } from './types';
@@ -8,8 +8,8 @@ import { ForSelectedCouncil } from './types';
 export function useWorkingGroups({ council }: ForSelectedCouncil) {
 
   const [fetch, query] = useGetWorkingGroupsLazyQuery();
-  const [fetchToken, tokenQuery] = useGetWorkingGroupTokenLazyQuery();
-  const [fetchTokenReward, tokenQueryReward] = useGetWorkingGroupTokenLazyQuery();
+  const [fetchToken, tokenQuery] = useWorkingGroupTokenLazyQuery();
+  const [fetchTokenReward, tokenQueryReward] = useWorkingGroupTokenLazyQuery();
   const [fetchReward, tokenReward] = useGetRewardsLazyQuery();
 
   useEffect(() => {

文件差異過大導致無法顯示
+ 1 - 0
src/queries/__generated__/proposals.generated.tsx


+ 3 - 0
src/queries/proposals.gql

@@ -26,6 +26,9 @@ fragment ProposalFields on Proposal {
   details {
     __typename
   }
+  creator {
+    ...MemberFields
+  }
   createdAt
   councilApprovals
   exactExecutionBlock

+ 1 - 1
src/types/Member.ts

@@ -66,7 +66,7 @@ export interface MemberWithDetails extends Member {
 }
 
 export const asMember = (data: Omit<MemberFieldsFragment, '__typename'>): Member => ({
-  id: data.id,
+  id: data?.id,
   handle: data.handle,
   // name: data.metadata?.name ?? undefined,
   // avatar: "", /*castQueryResult(data.metadata.avatar, 'AvatarUri')?.avatarUri ?? "",*/

+ 9 - 2
yarn.lock

@@ -1096,13 +1096,20 @@
   resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
   integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.19.0", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.8", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.19.0", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
   version "7.20.13"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b"
   integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==
   dependencies:
     regenerator-runtime "^0.13.11"
 
+"@babel/runtime@^7.14.8":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
+  integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
+  dependencies:
+    regenerator-runtime "^0.13.11"
+
 "@babel/template@^7.10.4", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
@@ -2017,7 +2024,7 @@
     react-is "^16.8.0 || ^17.0.0"
     react-transition-group "^4.4.0"
 
-"@material-ui/data-grid@^4.0.0-alpha.35":
+"@material-ui/data-grid@^4.0.0-alpha.37":
   version "4.0.0-alpha.37"
   resolved "https://registry.yarnpkg.com/@material-ui/data-grid/-/data-grid-4.0.0-alpha.37.tgz#89d907c4e94e6a0db4e89e4f59160f7811546ca2"
   integrity sha512-3T2AG31aad/lWLMLwn1XUP4mUf3H9YZES17dGuYByzkRLCXbBZHBTPEnCctWukajzwm+v0KGg3QpwitGoiDAjA==

部分文件因文件數量過多而無法顯示