From a617ef8721288f38c8b6d5ee0c4e7610a40cdb1f Mon Sep 17 00:00:00 2001 From: Imken Luo Date: Tue, 6 Aug 2024 15:38:44 +0800 Subject: [PATCH] add snapshot timeline --- packages/archive/package.json | 1 + packages/archive/src/lib/activity.ts | 4 +- packages/archive/src/lib/judgement.ts | 4 +- packages/archive/src/lib/list.ts | 6 +- packages/archive/src/lib/paste.ts | 4 +- packages/archive/src/lib/post.ts | 6 +- packages/archive/src/lib/user.ts | 2 +- packages/viewer/package.json | 1 + .../src/app/user/[uid]/SnapshotTimeline.tsx | 97 +++++++++ packages/viewer/src/app/user/[uid]/layout.tsx | 5 + .../src/app/user/[uid]/snapshots/route.ts | 26 +++ packages/viewer/src/components/UserInfo.tsx | 14 +- pnpm-lock.yaml | 195 ++++++++++++++++++ 13 files changed, 353 insertions(+), 12 deletions(-) create mode 100644 packages/viewer/src/app/user/[uid]/SnapshotTimeline.tsx create mode 100644 packages/viewer/src/app/user/[uid]/snapshots/route.ts diff --git a/packages/archive/package.json b/packages/archive/package.json index c9fd7c3..2dcdc49 100644 --- a/packages/archive/package.json +++ b/packages/archive/package.json @@ -11,6 +11,7 @@ "main": "dist/server.js", "scripts": { "build": "tsc", + "start": "node dist/server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { diff --git a/packages/archive/src/lib/activity.ts b/packages/archive/src/lib/activity.ts index 5504921..ecf72b7 100644 --- a/packages/archive/src/lib/activity.ts +++ b/packages/archive/src/lib/activity.ts @@ -4,7 +4,7 @@ import type { BaseLogger } from "pino"; import type { PrismaClient, PrismaPromise } from "@prisma/client"; import { getResponse } from "./parser"; import type { UserSummary } from "./user"; -import { upsertUserSnapshotHook } from "./user"; +import { upsertUserSnapshot } from "./user"; export interface Activity { content: string; @@ -37,7 +37,7 @@ export async function saveActivityPage( // eslint-disable-next-line no-restricted-syntax for (const { user } of res.feeds.result) { // eslint-disable-next-line no-await-in-loop - await upsertUserSnapshotHook(prisma, user); + await upsertUserSnapshot(prisma, user); } res.feeds.result.forEach((activity) => { diff --git a/packages/archive/src/lib/judgement.ts b/packages/archive/src/lib/judgement.ts index eb06513..e5bfaa3 100644 --- a/packages/archive/src/lib/judgement.ts +++ b/packages/archive/src/lib/judgement.ts @@ -1,7 +1,7 @@ import type { PrismaClient, PrismaPromise } from "@prisma/client"; import type { BaseLogger } from "pino"; import { getResponse } from "./parser"; -import { UserSummary, upsertUserSnapshotHook } from "./user"; +import { UserSummary, upsertUserSnapshot } from "./user"; interface JudgementBody { user: UserSummary; @@ -43,7 +43,7 @@ export default async function saveJudgements( // eslint-disable-next-line no-restricted-syntax for (const judgement of judgements) { // eslint-disable-next-line no-await-in-loop - await upsertUserSnapshotHook(prisma, judgement.user); + await upsertUserSnapshot(prisma, judgement.user); if (new Date(judgement.time * 1000) <= latestJudgement.time) break; operations.push( prisma.judgement.upsert({ diff --git a/packages/archive/src/lib/list.ts b/packages/archive/src/lib/list.ts index 9c35ad2..a5bea75 100644 --- a/packages/archive/src/lib/list.ts +++ b/packages/archive/src/lib/list.ts @@ -53,7 +53,11 @@ export default async function getPostList( await prisma.post.findMany({ select: { id: true, - replies: { select: { id: true }, orderBy: { id: "desc" }, take: 1 }, + replies: { + select: { id: true }, + orderBy: { id: "desc" }, + take: 1, + }, }, where: { id: { diff --git a/packages/archive/src/lib/paste.ts b/packages/archive/src/lib/paste.ts index 4cde22b..7860352 100644 --- a/packages/archive/src/lib/paste.ts +++ b/packages/archive/src/lib/paste.ts @@ -2,7 +2,7 @@ import type { BaseLogger } from "pino"; import type { PrismaClient } from "@prisma/client"; import { getResponse } from "./parser"; import { type UserSummary } from "./user"; -import { upsertUserSnapshotHook } from "./user"; +import { upsertUserSnapshot } from "./user"; interface Paste { data: string; @@ -49,7 +49,7 @@ export default async function savePaste( } if (json.code !== 200) throw Error(json.currentData.errorMessage); const { paste } = json.currentData; - await upsertUserSnapshotHook(prisma, paste.user); + await upsertUserSnapshot(prisma, paste.user); await prisma.$transaction([ prisma.paste.upsert({ where: { id: paste.id }, diff --git a/packages/archive/src/lib/post.ts b/packages/archive/src/lib/post.ts index 3cddda8..86e72e6 100644 --- a/packages/archive/src/lib/post.ts +++ b/packages/archive/src/lib/post.ts @@ -4,7 +4,7 @@ import type { BroadcastOperator } from "socket.io"; import type { PostSnapshot, PrismaClient } from "@prisma/client"; import { getResponse } from "./parser"; import type { ServerToClientEvents } from "../plugins/socket.io"; -import { UserSummary, upsertUserSnapshotHook } from "./user"; +import { UserSummary, upsertUserSnapshot } from "./user"; const PAGES_PER_SAVE = parseInt(process.env.PAGES_PER_SAVE ?? "64", 10); export const emitters: Record = {}; @@ -73,7 +73,7 @@ export async function savePost( // eslint-disable-next-line no-restricted-syntax for (const { author } of replies) { // eslint-disable-next-line no-await-in-loop - await upsertUserSnapshotHook(prisma, author); + await upsertUserSnapshot(prisma, author); } allReplies = [...allReplies, ...replies]; }; @@ -129,7 +129,7 @@ export async function savePost( const { post, replies, forum } = (await fetchPage(1)).currentData; const postTime = new Date(post.time * 1000); - await upsertUserSnapshotHook(prisma, post.author); + await upsertUserSnapshot(prisma, post.author); await prisma.$transaction( async (tx) => { diff --git a/packages/archive/src/lib/user.ts b/packages/archive/src/lib/user.ts index 147a798..ce6d5fe 100644 --- a/packages/archive/src/lib/user.ts +++ b/packages/archive/src/lib/user.ts @@ -11,7 +11,7 @@ export interface UserSummary { isRoot?: true; } -export const upsertUserSnapshotHook = async ( +export const upsertUserSnapshot = async ( prisma: PrismaClient, user: UserSummary, ) => { diff --git a/packages/viewer/package.json b/packages/viewer/package.json index 3a51dde..4c6b05f 100644 --- a/packages/viewer/package.json +++ b/packages/viewer/package.json @@ -26,6 +26,7 @@ "rehype-katex": "^7.0.0", "remark-luogu-flavor": "^1.0.0", "remark-math": "^6.0.0", + "rsuite": "^5.68.1", "socket.io-client": "^4.7.5", "swr": "^2.2.5" }, diff --git a/packages/viewer/src/app/user/[uid]/SnapshotTimeline.tsx b/packages/viewer/src/app/user/[uid]/SnapshotTimeline.tsx new file mode 100644 index 0000000..0c3f371 --- /dev/null +++ b/packages/viewer/src/app/user/[uid]/SnapshotTimeline.tsx @@ -0,0 +1,97 @@ +"use client"; + +import Spinner from "@/components/Spinner"; +import UserInfo from "@/components/UserInfo"; +import fetcher from "@/lib/fetcher"; +import { UserSnapshot } from "@prisma/client"; +import Timeline from "rsuite/Timeline"; +import useSWRInfinite from "swr/infinite"; +import "rsuite/Timeline/styles/index.css"; +import { useState } from "react"; +import { BsChevronDown, BsChevronUp, BsThreeDots } from "react-icons/bs"; + +const PER_PAGE = 15; + +interface PageData { + snapshots: UserSnapshot[]; + nextCursor: string; +} + +export default function SnapshotTimeline({ uid }: { uid: number }) { + const [open, setOpen] = useState(false); + + const { data, size, setSize, isValidating } = useSWRInfinite( + (_pageIndex, prev: PageData | null) => { + if (prev && !prev.nextCursor) return null; + let res = `/user/${uid}/snapshots?limit=${PER_PAGE}`; + if (prev) res += `&offset=${prev.nextCursor}`; + return res; + }, + fetcher, + ); + + const timeline = ( + <> + + {data?.map((dat) => + dat.snapshots?.map((snapshot) => ( + + +
+ 截至 {new Date(snapshot.until).toLocaleString()} +
+ )), + )} +
+ {isValidating && } + {!isValidating && + data && + data[data.length - 1].snapshots.length === PER_PAGE && ( + + )} + + ); + + return ( + <> +
+ + + {open && ( + <> + {/* 对于缺失空白的一个并不优雅的解决方案 */} +
+ {timeline} + + )} +
+
{timeline}
+ + ); +} diff --git a/packages/viewer/src/app/user/[uid]/layout.tsx b/packages/viewer/src/app/user/[uid]/layout.tsx index 8c48557..3e2dc88 100644 --- a/packages/viewer/src/app/user/[uid]/layout.tsx +++ b/packages/viewer/src/app/user/[uid]/layout.tsx @@ -7,6 +7,7 @@ import "@/components/markdown.css"; import { selectUser } from "@/lib/user"; import TabNavigation from "./TabNavigation"; import UserStatistics from "./UserStatistics"; +import SnapshotTimeline from "./SnapshotTimeline"; export async function generateMetadata({ params, @@ -65,6 +66,10 @@ export default async function Layout({
+ +
+ +
diff --git a/packages/viewer/src/app/user/[uid]/snapshots/route.ts b/packages/viewer/src/app/user/[uid]/snapshots/route.ts new file mode 100644 index 0000000..bd3c280 --- /dev/null +++ b/packages/viewer/src/app/user/[uid]/snapshots/route.ts @@ -0,0 +1,26 @@ +import prisma from "@/lib/prisma"; +import { NextRequest, NextResponse } from "next/server"; + +export async function GET( + request: NextRequest, + { params }: { params: { uid: string } }, +) { + const userId = +params.uid; + const limit = request.nextUrl.searchParams.get("limit") ?? 10; + const offset = request.nextUrl.searchParams.get("offset"); + + // 按照时间降序排序 跳过 offset 个 + const snapshots = await prisma.userSnapshot.findMany({ + where: { + userId, + time: { lt: offset ? new Date(offset) : undefined }, + }, + orderBy: { time: "desc" }, + take: limit ? +limit : undefined, + }); + + return NextResponse.json({ + snapshots, + nextCursor: snapshots[snapshots.length - 1]?.time ?? null, + }); +} diff --git a/packages/viewer/src/components/UserInfo.tsx b/packages/viewer/src/components/UserInfo.tsx index af61b35..28b3fc9 100644 --- a/packages/viewer/src/components/UserInfo.tsx +++ b/packages/viewer/src/components/UserInfo.tsx @@ -11,17 +11,29 @@ export default function UserInfo({ user, snapshotId, href, + noHref, }: { user: LatestUser; // eslint-disable-next-line react/require-default-props snapshotId?: number; // eslint-disable-next-line react/require-default-props href?: string; + // eslint-disable-next-line react/require-default-props + noHref?: boolean; }) { const snapshot = user.userSnapshots[snapshotId ?? 0]; return ( - {href === undefined ? ( + {/* eslint-disable-next-line no-nested-ternary */} + {noHref ? ( + + {snapshot.name} + + ) : href === undefined ? ( =16.8.0' + react-dom: '>=16.8.0' + '@rushstack/eslint-patch@1.10.3': resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} @@ -508,6 +523,9 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/lodash@4.17.7': + resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -523,6 +541,9 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-window@1.8.8': + resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==} + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} @@ -951,6 +972,9 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1057,6 +1081,10 @@ packages: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -1142,6 +1170,9 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dom-lib@3.3.1: + resolution: {integrity: sha512-N2mpo8qQmB9wIMZJVjER+BSh4GJiZZ7S6EjnMtyETcXo90hpITUDXpUhqOcfXZ2ZefytuYYKTZMp3CGR2X+tDA==} + domexception@4.0.0: resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} engines: {node: '>=12'} @@ -1560,6 +1591,10 @@ packages: get-tsconfig@4.7.5: resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} + get-value@3.0.1: + resolution: {integrity: sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==} + engines: {node: '>=6.0'} + git-revision-webpack-plugin@5.0.0: resolution: {integrity: sha512-RptQN/4UKcEPkCBmRy8kLPo5i8MnF8+XfAgFYN9gbwmKLTLx4YHsQw726H+C5+sIGDixDkmGL3IxPA2gKo+u4w==} engines: {node: '>=10'} @@ -1757,6 +1792,9 @@ packages: inline-style-parser@0.2.3: resolution: {integrity: sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==} + insert-css@2.0.0: + resolution: {integrity: sha512-xGq5ISgcUP5cvGkS2MMFLtPDBtrtQPSFfC6gA6U8wHKqfjTIMZLZNxOItQnoSjdOzlXOLU/yD32RKC4SvjNbtA==} + internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -1869,6 +1907,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -1876,6 +1918,10 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-primitive@3.0.1: + resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} + engines: {node: '>=0.10.0'} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -1921,6 +1967,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} @@ -2019,6 +2069,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-update@6.0.0: resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} engines: {node: '>=18'} @@ -2082,6 +2135,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -2525,12 +2581,27 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-markdown@9.0.1: resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} peerDependencies: '@types/react': '>=18' react: '>=18' + react-use-set@1.0.0: + resolution: {integrity: sha512-6BBbOcWc/tOKuwd9gDtdunvOr/g40S0SkCBYvrSJvpI0upzNlHmLoeDvylnoP8PrjQXItClAFxseVGGhEkk7kw==} + peerDependencies: + react: '>=16.8.0' + + react-window@1.8.10: + resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -2625,6 +2696,19 @@ packages: rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + rsuite-table@5.18.3: + resolution: {integrity: sha512-Rua79XndYY+UdCUpBuH1Ew5qa54y6zLZ0RNRnudKgamksrV1j+rUhcCsA03a5ZY+b8DXTwct4V/Q6K9q/cJT5w==} + peerDependencies: + prop-types: ^15.7.2 + react: '>=16.8.0' + react-dom: '>=16.8.0' + + rsuite@5.68.1: + resolution: {integrity: sha512-1MZvutCSAgS7YocZTr580zcfyKP0COWA3e9OeokpUxoSHTmEiFTGqbJMUP92lQOWEgLNJ/epA8/c9Wk7eNfPEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2661,6 +2745,9 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + schema-typed@2.2.2: + resolution: {integrity: sha512-hRmqKr5V6UyhmZ0FixRVetgxvudRPjDynVZZRNq6t4EZHii7U33vmqd9uap3s4aqBcDg1JtubMNvCEmsZTpm3Q==} + schema-utils@3.3.0: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} @@ -2695,6 +2782,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-value@4.1.0: + resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==} + engines: {node: '>=11.0'} + sharp@0.32.6: resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} engines: {node: '>=14.15.0'} @@ -3371,6 +3462,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@juggle/resize-observer@3.4.0': {} + '@logtail/core@0.4.21': dependencies: '@logtail/tools': 0.4.21 @@ -3493,6 +3586,18 @@ snapshots: dependencies: '@prisma/debug': 5.15.0 + '@rsuite/icon-font@4.0.0': {} + + '@rsuite/icons@1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.24.7 + '@rsuite/icon-font': 4.0.0 + classnames: 2.5.1 + insert-css: 2.0.0 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@rushstack/eslint-patch@1.10.3': {} '@socket.io/admin-ui@0.5.1(socket.io@4.7.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))': @@ -3569,6 +3674,8 @@ snapshots: '@types/katex@0.16.7': {} + '@types/lodash@4.17.7': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.2 @@ -3585,6 +3692,10 @@ snapshots: dependencies: '@types/react': 18.3.3 + '@types/react-window@1.8.8': + dependencies: + '@types/react': 18.3.3 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 @@ -4102,6 +4213,8 @@ snapshots: chrome-trace-event@1.0.4: {} + classnames@2.5.1: {} + cli-cursor@4.0.0: dependencies: restore-cursor: 4.0.0 @@ -4208,6 +4321,10 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.24.7 + debounce@1.2.1: {} debug@3.2.7: @@ -4279,6 +4396,10 @@ snapshots: dependencies: esutils: 2.0.3 + dom-lib@3.3.1: + dependencies: + '@babel/runtime': 7.24.7 + domexception@4.0.0: dependencies: webidl-conversions: 7.0.0 @@ -5023,6 +5144,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-value@3.0.1: + dependencies: + isobject: 3.0.1 + git-revision-webpack-plugin@5.0.0(webpack@5.92.0): dependencies: webpack: 5.92.0 @@ -5269,6 +5394,8 @@ snapshots: inline-style-parser@0.2.3: {} + insert-css@2.0.0: {} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 @@ -5365,10 +5492,16 @@ snapshots: is-plain-obj@4.1.0: {} + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + is-plain-object@5.0.0: {} is-potential-custom-element-name@1.0.1: {} + is-primitive@3.0.1: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -5409,6 +5542,8 @@ snapshots: isexe@2.0.0: {} + isobject@3.0.1: {} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 @@ -5549,6 +5684,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash@4.17.21: {} + log-update@6.0.0: dependencies: ansi-escapes: 6.2.1 @@ -5717,6 +5854,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + memoize-one@5.2.1: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -6267,6 +6406,8 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-markdown@9.0.1(@types/react@18.3.3)(react@18.3.1): dependencies: '@types/hast': 3.0.4 @@ -6284,6 +6425,17 @@ snapshots: transitivePeerDependencies: - supports-color + react-use-set@1.0.0(react@18.3.1): + dependencies: + react: 18.3.1 + + react-window@1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.7 + memoize-one: 5.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -6423,6 +6575,39 @@ snapshots: rrweb-cssom@0.6.0: {} + rsuite-table@5.18.3(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.7 + '@juggle/resize-observer': 3.4.0 + '@rsuite/icons': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + dom-lib: 3.3.1 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 17.0.2 + + rsuite@5.68.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.7 + '@juggle/resize-observer': 3.4.0 + '@rsuite/icons': 1.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/lodash': 4.17.7 + '@types/prop-types': 15.7.12 + '@types/react-window': 1.8.8 + classnames: 2.5.1 + date-fns: 2.30.0 + dom-lib: 3.3.1 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-use-set: 1.0.0(react@18.3.1) + react-window: 1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rsuite-table: 5.18.3(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + schema-typed: 2.2.2 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -6464,6 +6649,11 @@ snapshots: dependencies: loose-envify: 1.4.0 + schema-typed@2.2.2: + dependencies: + get-value: 3.0.1 + set-value: 4.1.0 + schema-utils@3.3.0: dependencies: '@types/json-schema': 7.0.15 @@ -6502,6 +6692,11 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-value@4.1.0: + dependencies: + is-plain-object: 2.0.4 + is-primitive: 3.0.1 + sharp@0.32.6: dependencies: color: 4.2.3