From ec76c557e4570c0824f3eed3d985efe0993a2560 Mon Sep 17 00:00:00 2001 From: biersoeckli Date: Thu, 5 Dec 2024 12:17:08 +0000 Subject: [PATCH] bugfixes --- src/app/layout.tsx | 3 +- .../app/[appId]/app-action-buttons.tsx | 1 + .../project/app/[appId]/general/actions.ts | 6 + .../app/[appId]/general/app-source.tsx | 2 +- .../project/app/[appId]/storage/actions.ts | 4 + .../[appId]/storage/storage-edit-overlay.tsx | 7 +- .../project/app/[appId]/storage/storages.tsx | 4 +- src/app/settings/cluster/nodeInfo.tsx | 4 +- src/components/ui/chart.tsx | 2 +- src/frontend/utils/toast.utils.ts | 4 +- src/server/services/deployment.service.ts | 26 +- src/server/services/svc.service.ts | 14 +- yarn.lock | 239 +++++++++++++++++- 13 files changed, 293 insertions(+), 23 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 391e531..cce383b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -29,7 +29,8 @@ export default async function RootLayout({ children: React.ReactNode; }>) { const cookieStore = await cookies() - const defaultOpen = cookieStore.get("sidebar:state")?.value === "true"; + const cookieSidebarState = cookieStore.get("sidebar:state")?.value ?? 'true'; + const defaultOpen = cookieSidebarState === "true"; const session = await getUserSession(); const userIsLoggedIn = !!session; diff --git a/src/app/project/app/[appId]/app-action-buttons.tsx b/src/app/project/app/[appId]/app-action-buttons.tsx index 706fa5f..9e73339 100644 --- a/src/app/project/app/[appId]/app-action-buttons.tsx +++ b/src/app/project/app/[appId]/app-action-buttons.tsx @@ -7,6 +7,7 @@ import { AppExtendedModel } from "@/shared/model/app-extended.model"; import { Toast } from "@/frontend/utils/toast.utils"; import AppStatus from "./app-status"; import { Hammer, Pause, Play, Rocket } from "lucide-react"; +import { toast } from "sonner"; export default function AppActionButtons({ app diff --git a/src/app/project/app/[appId]/general/actions.ts b/src/app/project/app/[appId]/general/actions.ts index 291d77f..ca06c2b 100644 --- a/src/app/project/app/[appId]/general/actions.ts +++ b/src/app/project/app/[appId]/general/actions.ts @@ -44,6 +44,12 @@ export const saveGeneralAppRateLimits = async (prevState: any, inputData: AppRat throw new ServiceException('Replica Count must be at least 1'); } await getAuthUserSession(); + + const extendedApp = await appService.getExtendedById(appId); + if (extendedApp.appVolumes.some(v => v.accessMode === 'ReadWriteOnce') && validatedData.replicas > 1) { + throw new ServiceException('Replica Count must be 1 because you have at least one volume with access mode ReadWriteOnce.'); + } + const existingApp = await appService.getById(appId); await appService.save({ ...existingApp, diff --git a/src/app/project/app/[appId]/general/app-source.tsx b/src/app/project/app/[appId]/general/app-source.tsx index dff8d59..9793132 100644 --- a/src/app/project/app/[appId]/general/app-source.tsx +++ b/src/app/project/app/[appId]/general/app-source.tsx @@ -152,7 +152,7 @@ export default function GeneralAppSource({ app }: { name="containerImageSource" render={({ field }) => ( - Docker Container Name + Docker Image Name diff --git a/src/app/project/app/[appId]/storage/actions.ts b/src/app/project/app/[appId]/storage/actions.ts index 8395016..838434b 100644 --- a/src/app/project/app/[appId]/storage/actions.ts +++ b/src/app/project/app/[appId]/storage/actions.ts @@ -16,10 +16,14 @@ const actionAppVolumeEditZodModel = appVolumeEditZodModel.merge(z.object({ export const saveVolume = async (prevState: any, inputData: z.infer) => saveFormAction(inputData, actionAppVolumeEditZodModel, async (validatedData) => { await getAuthUserSession(); + const existingApp = await appService.getExtendedById(validatedData.appId); const existingVolume = validatedData.id ? await appService.getVolumeById(validatedData.id) : undefined; if (existingVolume && existingVolume.size > validatedData.size) { throw new ServiceException('Volume size cannot be decreased'); } + if (existingApp.replicas > 1 && validatedData.accessMode === 'ReadWriteOnce') { + throw new ServiceException('Volume access mode must be ReadWriteMany because your app has more than one replica configured.'); + } await appService.saveVolume({ ...validatedData, id: validatedData.id ?? undefined, diff --git a/src/app/project/app/[appId]/storage/storage-edit-overlay.tsx b/src/app/project/app/[appId]/storage/storage-edit-overlay.tsx index d450cb0..2b0f7f0 100644 --- a/src/app/project/app/[appId]/storage/storage-edit-overlay.tsx +++ b/src/app/project/app/[appId]/storage/storage-edit-overlay.tsx @@ -38,13 +38,14 @@ import { saveVolume } from "./actions" import { toast } from "sonner" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { QuestionMarkCircledIcon } from "@radix-ui/react-icons" +import { AppExtendedModel } from "@/shared/model/app-extended.model" const accessModes = [ { label: "ReadWriteOnce", value: "ReadWriteOnce" }, { label: "ReadWriteMany", value: "ReadWriteMany" }, ] as const -export default function DialogEditDialog({ children, volume, appId }: { children: React.ReactNode; volume?: AppVolume; appId: string; }) { +export default function DialogEditDialog({ children, volume, app }: { children: React.ReactNode; volume?: AppVolume; app: AppExtendedModel; }) { const [isOpen, setIsOpen] = useState(false); @@ -53,14 +54,14 @@ export default function DialogEditDialog({ children, volume, appId }: { children resolver: zodResolver(appVolumeEditZodModel), defaultValues: { ...volume, - accessMode: volume?.accessMode ?? "ReadWriteOnce" + accessMode: volume?.accessMode ?? (app.replicas > 1 ? "ReadWriteMany" : "ReadWriteOnce") } }); const [state, formAction] = useFormState((state: ServerActionResult, payload: AppVolumeEditModel) => saveVolume(state, { ...payload, - appId, + appId: app.id, id: volume?.id }), FormUtils.getInitialFormState()); diff --git a/src/app/project/app/[appId]/storage/storages.tsx b/src/app/project/app/[appId]/storage/storages.tsx index 150b764..43bda33 100644 --- a/src/app/project/app/[appId]/storage/storages.tsx +++ b/src/app/project/app/[appId]/storage/storages.tsx @@ -106,7 +106,7 @@ export default function StorageList({ app }: { - + diff --git a/src/app/settings/cluster/nodeInfo.tsx b/src/app/settings/cluster/nodeInfo.tsx index c27140e..964fbe4 100644 --- a/src/app/settings/cluster/nodeInfo.tsx +++ b/src/app/settings/cluster/nodeInfo.tsx @@ -107,9 +107,9 @@ export default async function NodeInfo({ nodeInfos }: { nodeInfos: NodeInfoModel Kubelet Version: {nodeInfo.kubeletVersion} -
+ {index !== 0 &&
-
+
} ))} diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx index 8620baa..13b1ea4 100644 --- a/src/components/ui/chart.tsx +++ b/src/components/ui/chart.tsx @@ -3,7 +3,7 @@ import * as React from "react" import * as RechartsPrimitive from "recharts" -import { cn } from "@/lib/utils" +import { cn } from "@/frontend/utils/utils" // Format: { THEME_NAME: CSS_SELECTOR } const THEMES = { light: "", dark: ".dark" } as const diff --git a/src/frontend/utils/toast.utils.ts b/src/frontend/utils/toast.utils.ts index 3716533..f54f7cf 100644 --- a/src/frontend/utils/toast.utils.ts +++ b/src/frontend/utils/toast.utils.ts @@ -2,7 +2,7 @@ import { ServerActionResult } from "@/shared/model/server-action-error-return.mo import { toast } from "sonner"; export class Toast { - static async fromAction(action: () => Promise>) { + static async fromAction(action: () => Promise>, defaultSuccessMessage = 'Operation successful') { return new Promise>(async (resolve, reject) => { toast.promise(async () => { @@ -15,7 +15,7 @@ export class Toast { loading: 'loading...', success: (result: ServerActionResult) => { resolve(result); - return result.message ?? 'Operation successful'; + return result.message ?? defaultSuccessMessage; }, error: (error) => { reject(error); diff --git a/src/server/services/deployment.service.ts b/src/server/services/deployment.service.ts index 2196c3c..2110999 100644 --- a/src/server/services/deployment.service.ts +++ b/src/server/services/deployment.service.ts @@ -123,6 +123,30 @@ class DeploymentService { } } + if (app.cpuLimit || app.memoryLimit) { + body.spec!.template!.spec!.containers[0].resources = { + limits: {} + } + if (app.cpuLimit) { + body.spec!.template!.spec!.containers[0].resources!.limits!.cpu! = `${app.cpuLimit}m`; + } + if (app.memoryLimit) { + body.spec!.template!.spec!.containers[0].resources!.limits!.memory! = `${app.memoryLimit}M`; + } + } + + if (app.cpuReservation || app.memoryReservation) { + body.spec!.template!.spec!.containers[0].resources = { + requests: {} + } + if (app.cpuReservation) { + body.spec!.template!.spec!.containers[0].resources!.requests!.cpu! = `${app.cpuReservation}m`; + } + if (app.memoryReservation) { + body.spec!.template!.spec!.containers[0].resources!.requests!.memory! = `${app.memoryReservation}M`; + } + } + if (existingDeployment) { dlog(deploymentId, `Replacing existing deployment...`); const res = await k3s.apps.replaceNamespacedDeployment(app.id, app.projectId, body); @@ -134,7 +158,7 @@ class DeploymentService { await svcService.createOrUpdateService(deploymentId, app); dlog(deploymentId, `Updating ingress...`); await ingressService.createOrUpdateIngressForApp(deploymentId, app); - dlog(deploymentId, `Deployment finished.`); + dlog(deploymentId, `Deployment applied`); } private parseEnvVariables(app: AppExtendedModel) { diff --git a/src/server/services/svc.service.ts b/src/server/services/svc.service.ts index 73a9e46..cce1d62 100644 --- a/src/server/services/svc.service.ts +++ b/src/server/services/svc.service.ts @@ -19,7 +19,6 @@ class SvcService { return returnVal; } - async getService(projectId: string, appId: string) { const allServices = await k3s.core.listNamespacedService(projectId); if (allServices.body.items.some((item) => item.metadata?.name === KubeObjectNameUtils.toServiceName(appId))) { @@ -47,7 +46,16 @@ class SvcService { targetPort: port.port })), ].filter((port, index, self) => - index === self.findIndex((t) => (t.port === port.port && t.targetPort === port.targetPort))); + index === self.findIndex((t) => + (t.port === port.port && t.targetPort === port.targetPort))); + + if (ports.length === 0) { + dlog(deplyomentId, `No domain or internal port settings found, service (HTTP) will not be created or updated. The application will run, but will not be accessible via the internal network or the internet.`); + if (existingService) { + await this.deleteService(app.projectId, app.id); + } + return; + } const body = { metadata: { @@ -61,7 +69,7 @@ class SvcService { } }; - dlog(deplyomentId, `Updating service with ports ${ports.map(x => x.port).join(', ')}...`); + dlog(deplyomentId, `Updating service (HTTP) with ports ${ports.map(x => x.port).join(', ')}...`); if (existingService) { await k3s.core.replaceNamespacedService(KubeObjectNameUtils.toServiceName(app.id), app.projectId, body); } else { diff --git a/yarn.lock b/yarn.lock index 00b3088..fae5220 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== -"@babel/runtime@^7.13.10", "@babel/runtime@^7.20.13": +"@babel/runtime@^7.13.10", "@babel/runtime@^7.20.13", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -1085,6 +1085,57 @@ dependencies: "@types/node" "*" +"@types/d3-array@^3.0.3": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5" + integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg== + +"@types/d3-color@*": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== + +"@types/d3-ease@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b" + integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA== + +"@types/d3-interpolate@^3.0.1": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.0.tgz#2b907adce762a78e98828f0b438eaca339ae410a" + integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ== + +"@types/d3-scale@^4.0.2": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb" + integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^3.1.0": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72" + integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time@*", "@types/d3-time@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f" + integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== + +"@types/d3-timer@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70" + integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw== + "@types/debug@4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -1667,7 +1718,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clsx@^2.1.1: +clsx@^2.0.0, clsx@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== @@ -1782,6 +1833,77 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-ease@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +d3-timer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -1850,6 +1972,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== +decimal.js-light@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1920,6 +2047,14 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -2317,6 +2452,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +eventemitter3@^4.0.1: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -2337,6 +2477,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-equals@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d" + integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== + fast-glob@^3.2.7, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -2728,6 +2873,11 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -3132,6 +3282,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -3151,10 +3306,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lucide-react@^0.462.0: - version "0.462.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.462.0.tgz#cb95b1dde558be51b7ae247290f7bb5f197880bf" - integrity sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g== +lucide-react@^0.465.0: + version "0.465.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.465.0.tgz#3f98d40f7b7ac5266c055aaf582c303b07f84de2" + integrity sha512-uV7WEqbwaCcc+QjAxIhAvkAr3kgwkkYID3XptCHll72/F7NZlk6ONmJYpk+Xqx5Q0r/8wiOjz73H1BYbl8Z8iw== make-dir@^3.1.0: version "3.1.0" @@ -3718,7 +3873,7 @@ prisma@^5.21.1: optionalDependencies: fsevents "2.3.3" -prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -3781,6 +3936,11 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== + react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c" @@ -3811,6 +3971,15 @@ react-remove-scroll@2.6.0: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-smooth@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-4.0.3.tgz#add5c7f607445766bcd871b10b2170318a444454" + integrity sha512-PyxIrra8WZWrMRFcCiJsZ+JqFaxEINAt+v/w++wQKQlmO99Eh3+JTLeKApdTsLX2roBdWYXqPsaS8sO4UmdzIg== + dependencies: + fast-equals "^5.0.1" + prop-types "^15.8.1" + react-transition-group "^4.4.5" + react-style-singleton@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" @@ -3820,6 +3989,16 @@ react-style-singleton@^2.2.1: invariant "^2.2.4" tslib "^2.0.0" +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -3850,6 +4029,27 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +recharts-scale@^0.4.4: + version "0.4.5" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9" + integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^2.14.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.14.1.tgz#8b91bd1e53915b9bc03a262a3d8969663d3195b5" + integrity sha512-xtWulflkA+/xu4/QClBdtZYN30dbvTHjxjkh5XTMrH/CQ3WGDDPHHa/LLKCbgoqz0z3UaSH2/blV1i6VNMeh1g== + dependencies: + clsx "^2.0.0" + eventemitter3 "^4.0.1" + lodash "^4.17.21" + react-is "^18.3.1" + react-smooth "^4.0.0" + recharts-scale "^0.4.4" + tiny-invariant "^1.3.1" + victory-vendor "^36.6.8" + reflect-metadata@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" @@ -4407,6 +4607,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +tiny-invariant@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4631,6 +4836,26 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +victory-vendor@^36.6.8: + version "36.9.2" + resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801" + integrity sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ== + dependencies: + "@types/d3-array" "^3.0.3" + "@types/d3-ease" "^3.0.0" + "@types/d3-interpolate" "^3.0.1" + "@types/d3-scale" "^4.0.2" + "@types/d3-shape" "^3.1.0" + "@types/d3-time" "^3.0.0" + "@types/d3-timer" "^3.0.0" + d3-array "^3.1.6" + d3-ease "^3.0.1" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-shape "^3.1.0" + d3-time "^3.0.0" + d3-timer "^3.0.1" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"