From 2ab339523bcc23d95fa7bf966d7e425e5af0f665 Mon Sep 17 00:00:00 2001 From: Elie Baier <100542666+Fayorg@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:04:10 +0100 Subject: [PATCH 01/25] add: gh action publish package --- .github/workflows/docker-publish.yml | 98 ++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/docker-publish.yml diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..69855d2 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,98 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '26 22 * * *' + push: + branches: [ "master" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "master" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: fayorg/calendrier-avant + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 + with: + cosign-release: 'v2.1.1' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From 1e3345d45e9137a7215cbd5ca36576947be4a273 Mon Sep 17 00:00:00 2001 From: Elie Baier <100542666+Fayorg@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:11:41 +0100 Subject: [PATCH 02/25] fix: removed pr & cron action --- .github/workflows/docker-publish.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 69855d2..8905ead 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -6,14 +6,10 @@ name: Docker # documentation. on: - schedule: - - cron: '26 22 * * *' push: branches: [ "master" ] # Publish semver tags as releases. tags: [ 'v*.*.*' ] - pull_request: - branches: [ "master" ] env: # Use docker.io for Docker Hub if empty From 53a9b54b5ed66752c3e2c0dbc01f07cf48081ad2 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 21:32:50 +0100 Subject: [PATCH 03/25] fix: removed dayCard component --- components/custom/dayCard.tsx | 48 ----------------------------------- 1 file changed, 48 deletions(-) diff --git a/components/custom/dayCard.tsx b/components/custom/dayCard.tsx index 47b0d92..e69de29 100644 --- a/components/custom/dayCard.tsx +++ b/components/custom/dayCard.tsx @@ -1,48 +0,0 @@ -"use client"; - -import { BellRing, Check } from "lucide-react" - -import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" - -type CardProps = React.ComponentProps - -interface DayCardProps extends CardProps { - day: number, - name: string, - enabled: boolean -} - -export function DayCard({ className, ...props }: DayCardProps) { - return ( - - - {props.enabled ? -
-

1

-
-

Michal Polka

-

Description

-
-
- : -
-

1

-
-

Michal Polka

-

Description

-
-
- } -
-
- ) -} From 9df430fa72f93c7b31896b3681f61367d15b969b Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 21:36:14 +0100 Subject: [PATCH 04/25] fix: prisma import --- app/api/grade/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/grade/route.ts b/app/api/grade/route.ts index 667d120..915438e 100644 --- a/app/api/grade/route.ts +++ b/app/api/grade/route.ts @@ -1,6 +1,6 @@ import {NextResponse} from "next/server"; -import prisma from '@lib/prisma'; +import prisma from "@/lib/prisma"; interface IBody { key: string grade: number From 80ef4007f70cb7052a7af322d0b131f0fa613f7e Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 21:37:47 +0100 Subject: [PATCH 05/25] fix: removed api --- app/api/grade/route.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/api/grade/route.ts b/app/api/grade/route.ts index 915438e..5a491fb 100644 --- a/app/api/grade/route.ts +++ b/app/api/grade/route.ts @@ -7,12 +7,5 @@ interface IBody { } export async function POST(req: Request){ - const body = await req.json() as IBody - const grade = await prisma.grade.findFirst({ select: { id: true}, where: { oral: 1, key: { key: body.key } } }); - if(grade) return NextResponse.json({message: 'Deja vote'}, {status: 403}); - - const gradeCreated = await prisma.grade.create({ data: { key: { connect: { key: body.key } }, grade: body.grade, oral: 1 } }); - if(gradeCreated) return NextResponse.json({message: 'Vote enregistré'}, {status: 200}); - return NextResponse.json({message: 'Server error'}, {status: 500}) } From 76d9e12de22ed6c8f9ba54d69d2692fc7fa93abd Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 21:25:29 +0100 Subject: [PATCH 06/25] fix: merge issues --- app/results/page.tsx | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/app/results/page.tsx b/app/results/page.tsx index e278635..d9bfe5d 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -1,11 +1,41 @@ -"use client"; +'use client'; +import { Bar } from 'react-chartjs-2'; -import {DayCard} from "@components/custom/dayCard"; +import prisma from '@lib/prisma'; + +import Head from 'next/head'; + +import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler } from 'chart.js'; + +ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Filler, Title, Tooltip, Legend); + +interface IPower { + date: string; + value: number; +} + +interface IChart { + labels: string[]; + datasets: [ + { + label: string; + data: number[]; + fill: true; + backgroundColor: string; + borderColor: string; + } + ]; +} export default function Results() { - return ( -
- -
- ) + return ( + <> + + Solar + + + + + + ); } From 20d9c0f11a2b240fd3d65b15b99c8af76c08f7c1 Mon Sep 17 00:00:00 2001 From: timhaller <67918170+timhaller@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:43:55 +0100 Subject: [PATCH 07/25] everything --- app/results/page.tsx | 15 +++++++++++++-- components/custom/dayCard.tsx | 28 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/results/page.tsx b/app/results/page.tsx index e278635..336b980 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -4,8 +4,19 @@ import {DayCard} from "@components/custom/dayCard"; export default function Results() { return ( -
- +
+
+ + + + + + + + + + +
) } diff --git a/components/custom/dayCard.tsx b/components/custom/dayCard.tsx index 47b0d92..75a4fcb 100644 --- a/components/custom/dayCard.tsx +++ b/components/custom/dayCard.tsx @@ -1,6 +1,6 @@ "use client"; -import { BellRing, Check } from "lucide-react" +import { Lock } from "lucide-react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" @@ -18,28 +18,28 @@ type CardProps = React.ComponentProps interface DayCardProps extends CardProps { day: number, name: string, - enabled: boolean + enabled?: boolean | false, + oral?: boolean | true, +} + +function say() { + alert("Hello") } export function DayCard({ className, ...props }: DayCardProps) { return ( - - + + {props.enabled ? -
-

1

-
+
+

1

+

Michal Polka

-

Description

: -
-

1

-
-

Michal Polka

-

Description

-
+
+
} From 43a7f49c2da2518234bbaa6155843400294ea27d Mon Sep 17 00:00:00 2001 From: timhaller <67918170+timhaller@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:44:33 +0100 Subject: [PATCH 08/25] everything --- .idea/.gitignore | 5 +++ .idea/calendrier-avant.iml | 12 ++++++ .idea/modules.xml | 8 ++++ .idea/vcs.xml | 6 +++ components.json | 16 ++++++++ components/ui/button.tsx | 56 +++++++++++++++++++++++++++ components/ui/card.tsx | 79 ++++++++++++++++++++++++++++++++++++++ components/ui/input.tsx | 25 ++++++++++++ lib/utils.ts | 6 +++ tailwind.config.js | 76 ++++++++++++++++++++++++++++++++++++ 10 files changed, 289 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/calendrier-avant.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 components.json create mode 100644 components/ui/button.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/input.tsx create mode 100644 lib/utils.ts create mode 100644 tailwind.config.js diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/calendrier-avant.iml b/.idea/calendrier-avant.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/calendrier-avant.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7ea0a9f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/components.json b/components.json new file mode 100644 index 0000000..48c34e4 --- /dev/null +++ b/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "app/globals.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/components/ui/button.tsx b/components/ui/button.tsx new file mode 100644 index 0000000..0ba4277 --- /dev/null +++ b/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..afa13ec --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/components/ui/input.tsx b/components/ui/input.tsx new file mode 100644 index 0000000..677d05f --- /dev/null +++ b/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..ec79801 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..0377ea1 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,76 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: 0 }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: 0 }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} \ No newline at end of file From c2d3081a14303592e25cacaf6b0224614ba1f6f7 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 21:47:08 +0100 Subject: [PATCH 09/25] fix: removed .idea --- .gitignore | 1 + .idea/.gitignore | 5 ----- .idea/calendrier-avant.iml | 12 ------------ .idea/modules.xml | 8 -------- .idea/vcs.xml | 6 ------ 5 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/calendrier-avant.iml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 12526e8..42a2804 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ yarn-error.log* next-env.d.ts env +/.idea/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/calendrier-avant.iml b/.idea/calendrier-avant.iml deleted file mode 100644 index 24643cc..0000000 --- a/.idea/calendrier-avant.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 7ea0a9f..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From ff44beb61bb8c426bbfb2a54eea0bf12186ebfff Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:02:29 +0100 Subject: [PATCH 10/25] add: route to get user by key --- app/api/me/route.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 app/api/me/route.ts diff --git a/app/api/me/route.ts b/app/api/me/route.ts new file mode 100644 index 0000000..fe54c15 --- /dev/null +++ b/app/api/me/route.ts @@ -0,0 +1,24 @@ +import {NextRequest, NextResponse} from "next/server"; +import prisma from "@/lib/prisma"; + +export async function GET(req: NextRequest){ + const key = req.nextUrl.searchParams.get("key"); + + if(!key) return NextResponse.json({error: "No key provided"}, {status: 400}); + + const user = await prisma.users.findUnique({ + where: { + key + } + }); + + if(!user) return NextResponse.json({error: "Key not found"}, {status: 404}); + + return NextResponse.json({ + id: user.id, + firstName: user.firstName, + lastName: user.lastName, + isTeacher: user.isTeacher, + }); + +} From a1fb215aa4bf138972d133f95b84d0280decba61 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:05:03 +0100 Subject: [PATCH 11/25] fix: text --- app/page.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 99e9c00..0882eef 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,12 +1,18 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; export default function Home() { const [password, setPassword] = useState(''); const router = useRouter(); + useEffect(() => { + if (localStorage.getItem('@password')) { + router.push('/play'); + } + }, [router]); + function handleSubmit(event: React.FormEvent) { event.preventDefault(); @@ -15,9 +21,9 @@ export default function Home() { } return ( -
-

Calendrier-avent

-

Please enter the password :

+
+

Calendrier-avent

+

Merci d'entrer votre clé :

setPassword(e.target.value)}> From d9e1661a8f61b9213a9cb583d9948afdd7b73b91 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:09:36 +0100 Subject: [PATCH 12/25] fix: duplicated entry (testOf in Users and Test) --- prisma/schema.prisma | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a88e8f3..8a69b4e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -9,15 +9,14 @@ generator client { } model Users { - id Int @id @default(autoincrement()) - key String @unique + id Int @id @default(autoincrement()) + key String @unique grades Grade[] firstName String lastName String - testOn DateTime? @db.Date - isTeacher Boolean @default(false) + isTeacher Boolean @default(false) test Test? - createdAt DateTime @default(now()) + createdAt DateTime @default(now()) } model Test { From cd681b095f7f4e4a35189c03727a90bbf6a8af60 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:13:10 +0100 Subject: [PATCH 13/25] fix: change testOf from DateTime to Date --- prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8a69b4e..9cd3a4f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -23,7 +23,7 @@ model Test { id Int @id @default(autoincrement()) testOf Users @relation(fields: [testOfId], references: [id]) testOfId Int @unique - testOn DateTime @default(now()) + testOn DateTime @db.Date createdAt DateTime @default(now()) } From 8e78611aabc63dd593c58a9aff6a9b3cb4973597 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:16:41 +0100 Subject: [PATCH 14/25] fix: me endpoint now return the test date if exist --- app/api/me/route.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/api/me/route.ts b/app/api/me/route.ts index fe54c15..ef79a32 100644 --- a/app/api/me/route.ts +++ b/app/api/me/route.ts @@ -7,6 +7,17 @@ export async function GET(req: NextRequest){ if(!key) return NextResponse.json({error: "No key provided"}, {status: 400}); const user = await prisma.users.findUnique({ + select: { + id: true, + firstName: true, + lastName: true, + isTeacher: true, + test: { + select: { + testOn: true + } + } + }, where: { key } @@ -19,6 +30,7 @@ export async function GET(req: NextRequest){ firstName: user.firstName, lastName: user.lastName, isTeacher: user.isTeacher, + testOn: user.test?.testOn }); } From 2fdbb9648890d6081587293cef3a2b871b1bde42 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:23:34 +0100 Subject: [PATCH 15/25] add: route to get today's test --- app/api/test/route.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 app/api/test/route.ts diff --git a/app/api/test/route.ts b/app/api/test/route.ts new file mode 100644 index 0000000..ebff42d --- /dev/null +++ b/app/api/test/route.ts @@ -0,0 +1,38 @@ +import {NextRequest, NextResponse} from "next/server"; +import prisma from "@/lib/prisma"; + +export async function GET(req: NextRequest){ + const date = req.nextUrl.searchParams.get("date"); + + const test = await prisma.test.findFirst({ + select: { + id: true, + testOn: true, + testOf: { + select: { + id: true, + firstName: true, + lastName: true, + isTeacher: true + } + } + }, + where: { + testOn: (date ? new Date(date) : new Date()) + } + }); + + if(!test){ + return NextResponse.json({error: "Test not found"}, {status: 404}); + } + + return NextResponse.json({ + id: test.id, + testOn: test.testOn, + testOf: { + firstName: test.testOf.firstName, + lastName: test.testOf.lastName, + isTeacher: test.testOf.isTeacher + } + }); +} From cecdc164f58a161309ff2fdab794d212a4faaa99 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 22:31:51 +0100 Subject: [PATCH 16/25] add: added swr --- package-lock.json | 21 +++++++++++++++++++++ package.json | 1 + 2 files changed, 22 insertions(+) diff --git a/package-lock.json b/package-lock.json index bdbb10a..dd86cdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "react-dom": "^18", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", + "swr": "^2.2.4", "tailwind-merge": "^2.1.0", "tailwindcss-animate": "^1.0.7" }, @@ -4199,6 +4200,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.4.tgz", + "integrity": "sha512-njiZ/4RiIhoOlAaLYDqwz5qH/KZXVilRLvomrx83HjzCWTfa+InyfAjv05PSFxnmLzZkNO9ZfvgoqzAaEI4sGQ==", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/tailwind-merge": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.1.0.tgz", @@ -4495,6 +4508,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 3f1848b..4a8f613 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react-dom": "^18", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", + "swr": "^2.2.4", "tailwind-merge": "^2.1.0", "tailwindcss-animate": "^1.0.7" }, From 2d28916803aa9d09d7892570225e771dbf6bb914 Mon Sep 17 00:00:00 2001 From: timhaller <67918170+timhaller@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:23:36 +0100 Subject: [PATCH 17/25] prisma: added grade relation to test --- prisma/schema.prisma | 54 +++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a88e8f3..9aa88bf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,38 +1,40 @@ -datasource db { - provider = "mysql" - url = env("DATABASE_URL") - relationMode = "prisma" +generator client { + provider = "prisma-client-js" } -generator client { - provider = "prisma-client-js" +datasource db { + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "prisma" } model Users { - id Int @id @default(autoincrement()) - key String @unique - grades Grade[] - firstName String - lastName String - testOn DateTime? @db.Date - isTeacher Boolean @default(false) - test Test? - createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + key String @unique + firstName String + lastName String + isTeacher Boolean @default(false) + createdAt DateTime @default(now()) + grades Grade[] + test Test? } model Test { - id Int @id @default(autoincrement()) - testOf Users @relation(fields: [testOfId], references: [id]) - testOfId Int @unique - testOn DateTime @default(now()) - createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + testOfId Int @unique + testOn DateTime @db.Date + createdAt DateTime @default(now()) + testOf Users @relation(fields: [testOfId], references: [id]) + grades Grade[] } model Grade { - id Int @id @default(autoincrement()) - user Users @relation(fields: [userId], references: [id]) - userId Int - grade Float - note String? - createdAt DateTime @default(now()) + id Int @id @default(autoincrement()) + userId Int + note String? + createdAt DateTime @default(now()) + grade Float + testId Int + test Test @relation(fields: [testId], references: [id]) + user Users @relation(fields: [userId], references: [id]) } From 0c207bea3c6e0114d9bc9d480d6f9e83955197e1 Mon Sep 17 00:00:00 2001 From: timhaller <67918170+timhaller@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:26:05 +0100 Subject: [PATCH 18/25] work in progress : view results --- app/results/[id]/page.tsx | 21 ++ app/results/page.tsx | 30 +- components/custom/chart.tsx | 34 ++ components/custom/dayCard.tsx | 29 +- components/ui/toast.tsx | 127 ++++++++ components/ui/toaster.tsx | 35 +++ components/ui/use-toast.ts | 192 +++++++++++ package-lock.json | 577 +++++++++++++++++++++++++++++++++- package.json | 2 + tsconfig.json | 2 +- 10 files changed, 1021 insertions(+), 28 deletions(-) create mode 100644 app/results/[id]/page.tsx create mode 100644 components/custom/chart.tsx create mode 100644 components/ui/toast.tsx create mode 100644 components/ui/toaster.tsx create mode 100644 components/ui/use-toast.ts diff --git a/app/results/[id]/page.tsx b/app/results/[id]/page.tsx new file mode 100644 index 0000000..c3a16ea --- /dev/null +++ b/app/results/[id]/page.tsx @@ -0,0 +1,21 @@ +"use server"; +import {Chart} from "@components/custom/chart"; + +import Prisma from '@lib/prisma' + +export default async function Page({ params }: { params: { id: string } }) { + const grades = await Prisma.grade.findMany({ + where: { + testId: parseInt(params.id) + } + }) + + for (let grade in grades) { + console.log(grade); + } + + return
+ My Post: {params.id} + +
+} diff --git a/app/results/page.tsx b/app/results/page.tsx index 336b980..1afb638 100644 --- a/app/results/page.tsx +++ b/app/results/page.tsx @@ -1,21 +1,25 @@ -"use client"; +"use server"; import {DayCard} from "@components/custom/dayCard"; -export default function Results() { +import Prisma from '@lib/prisma' + +export default async function Results() { + + const users = await Prisma.users.findMany( + { + include: { + test: true + } + } + ) + return (
-
- - - - - - - - - - +
+ {users.map((user) => ( + + ))}
) diff --git a/components/custom/chart.tsx b/components/custom/chart.tsx new file mode 100644 index 0000000..dcab596 --- /dev/null +++ b/components/custom/chart.tsx @@ -0,0 +1,34 @@ +"use client" + +import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts" + +interface ChartProps { + data: { + name: string + total: number + }[] +} + +export function Chart({...props}: ChartProps) { + return ( + + + + `$${value}`} + /> + + + + ) +} diff --git a/components/custom/dayCard.tsx b/components/custom/dayCard.tsx index 75a4fcb..9334e63 100644 --- a/components/custom/dayCard.tsx +++ b/components/custom/dayCard.tsx @@ -15,26 +15,35 @@ import { type CardProps = React.ComponentProps +import { useRouter } from "next/navigation"; + + interface DayCardProps extends CardProps { day: number, name: string, - enabled?: boolean | false, - oral?: boolean | true, -} - -function say() { - alert("Hello") + enabled?: boolean, + oral?: boolean, } export function DayCard({ className, ...props }: DayCardProps) { + const router = useRouter(); + + function route(enabled: boolean, day: number) { + if (enabled) { + router.push("/results/" + day); + } else { + return; + } + } + return ( - + {route(props.enabled || false, props.day)}}> {props.enabled ? -
-

1

+
+

{props.day}

-

Michal Polka

+

{props.name}

: diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx new file mode 100644 index 0000000..a822477 --- /dev/null +++ b/components/ui/toast.tsx @@ -0,0 +1,127 @@ +import * as React from "react" +import * as ToastPrimitives from "@radix-ui/react-toast" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const ToastProvider = ToastPrimitives.Provider + +const ToastViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastViewport.displayName = ToastPrimitives.Viewport.displayName + +const toastVariants = cva( + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + { + variants: { + variant: { + default: "border bg-background text-foreground", + destructive: + "destructive group border-destructive bg-destructive text-destructive-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Toast = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => { + return ( + + ) +}) +Toast.displayName = ToastPrimitives.Root.displayName + +const ToastAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastAction.displayName = ToastPrimitives.Action.displayName + +const ToastClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +ToastClose.displayName = ToastPrimitives.Close.displayName + +const ToastTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastTitle.displayName = ToastPrimitives.Title.displayName + +const ToastDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastDescription.displayName = ToastPrimitives.Description.displayName + +type ToastProps = React.ComponentPropsWithoutRef + +type ToastActionElement = React.ReactElement + +export { + type ToastProps, + type ToastActionElement, + ToastProvider, + ToastViewport, + Toast, + ToastTitle, + ToastDescription, + ToastClose, + ToastAction, +} diff --git a/components/ui/toaster.tsx b/components/ui/toaster.tsx new file mode 100644 index 0000000..e223385 --- /dev/null +++ b/components/ui/toaster.tsx @@ -0,0 +1,35 @@ +"use client" + +import { + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, +} from "@/components/ui/toast" +import { useToast } from "@/components/ui/use-toast" + +export function Toaster() { + const { toasts } = useToast() + + return ( + + {toasts.map(function ({ id, title, description, action, ...props }) { + return ( + +
+ {title && {title}} + {description && ( + {description} + )} +
+ {action} + +
+ ) + })} + +
+ ) +} diff --git a/components/ui/use-toast.ts b/components/ui/use-toast.ts new file mode 100644 index 0000000..1671307 --- /dev/null +++ b/components/ui/use-toast.ts @@ -0,0 +1,192 @@ +// Inspired by react-hot-toast library +import * as React from "react" + +import type { + ToastActionElement, + ToastProps, +} from "@/components/ui/toast" + +const TOAST_LIMIT = 1 +const TOAST_REMOVE_DELAY = 1000000 + +type ToasterToast = ToastProps & { + id: string + title?: React.ReactNode + description?: React.ReactNode + action?: ToastActionElement +} + +const actionTypes = { + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", +} as const + +let count = 0 + +function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER + return count.toString() +} + +type ActionType = typeof actionTypes + +type Action = + | { + type: ActionType["ADD_TOAST"] + toast: ToasterToast + } + | { + type: ActionType["UPDATE_TOAST"] + toast: Partial + } + | { + type: ActionType["DISMISS_TOAST"] + toastId?: ToasterToast["id"] + } + | { + type: ActionType["REMOVE_TOAST"] + toastId?: ToasterToast["id"] + } + +interface State { + toasts: ToasterToast[] +} + +const toastTimeouts = new Map>() + +const addToRemoveQueue = (toastId: string) => { + if (toastTimeouts.has(toastId)) { + return + } + + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId) + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }) + }, TOAST_REMOVE_DELAY) + + toastTimeouts.set(toastId, timeout) +} + +export const reducer = (state: State, action: Action): State => { + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + } + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t + ), + } + + case "DISMISS_TOAST": { + const { toastId } = action + + // ! Side effects ! - This could be extracted into a dismissToast() action, + // but I'll keep it here for simplicity + if (toastId) { + addToRemoveQueue(toastId) + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id) + }) + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { + ...t, + open: false, + } + : t + ), + } + } + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + } + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + } + } +} + +const listeners: Array<(state: State) => void> = [] + +let memoryState: State = { toasts: [] } + +function dispatch(action: Action) { + memoryState = reducer(memoryState, action) + listeners.forEach((listener) => { + listener(memoryState) + }) +} + +type Toast = Omit + +function toast({ ...props }: Toast) { + const id = genId() + + const update = (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props, id }, + }) + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) + + dispatch({ + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss() + }, + }, + }) + + return { + id: id, + dismiss, + update, + } +} + +function useToast() { + const [state, setState] = React.useState(memoryState) + + React.useEffect(() => { + listeners.push(setState) + return () => { + const index = listeners.indexOf(setState) + if (index > -1) { + listeners.splice(index, 1) + } + } + }, [state]) + + return { + ...state, + toast, + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), + } +} + +export { useToast, toast } diff --git a/package-lock.json b/package-lock.json index dd86cdf..e689f92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@prisma/client": "^5.6.0", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-toast": "^1.1.5", "chart.js": "^4.4.0", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", @@ -19,6 +20,7 @@ "react": "^18", "react-chartjs-2": "^5.2.0", "react-dom": "^18", + "recharts": "^2.10.3", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", "swr": "^2.2.4", @@ -417,6 +419,40 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz", "integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==" }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", @@ -434,6 +470,120 @@ } } }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -452,6 +602,133 @@ } } }, + "node_modules/@radix-ui/react-toast": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz", + "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.6.0.tgz", @@ -484,6 +761,60 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -519,7 +850,7 @@ "version": "18.2.17", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.17.tgz", "integrity": "sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -1264,6 +1595,116 @@ "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", "devOptional": true }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -1286,6 +1727,11 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1366,6 +1812,14 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.601", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.601.tgz", @@ -1977,12 +2431,25 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -2457,6 +2924,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -2941,6 +3416,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3608,7 +4088,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3678,8 +4157,41 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-smooth": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.5.tgz", + "integrity": "sha512-BMP2Ad42tD60h0JW6BFaib+RJuV5dsXJK9Baxiv/HlNFjvRLqA9xrNKxVWnUIZPQfzUwGXIlU/dSYLU+54YGQA==", + "dependencies": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "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" + } + }, + "node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } }, "node_modules/read-cache": { "version": "1.0.0", @@ -3700,6 +4212,37 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.10.3.tgz", + "integrity": "sha512-G4J96fKTZdfFQd6aQnZjo2nVNdXhp+uuLb00+cBTGLo85pChvm1+E67K3wBOHDE/77spcYb2Cy9gYWVqiZvQCg==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-smooth": "^2.0.5", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -4302,6 +4845,11 @@ "node": ">=0.8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4529,6 +5077,27 @@ "node": ">= 0.8" } }, + "node_modules/victory-vendor": { + "version": "36.7.0", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.7.0.tgz", + "integrity": "sha512-nqYuTkLSdTTeACyXcCLbL7rl0y6jpzLPtTNGOtSnajdR+xxMxBdjMxDjfNJNlhR+ZU8vbXz+QejntcbY7h9/ZA==", + "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" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 4a8f613..b72653d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@prisma/client": "^5.6.0", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-toast": "^1.1.5", "chart.js": "^4.4.0", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", @@ -20,6 +21,7 @@ "react": "^18", "react-chartjs-2": "^5.2.0", "react-dom": "^18", + "recharts": "^2.10.3", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", "swr": "^2.2.4", diff --git a/tsconfig.json b/tsconfig.json index fbc8f6b..bef319b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,7 @@ "paths": { "@/*": ["./*"], "@components/*": ["./components/*"], - "@lib/*": ["/lib/*"], + "@lib/*": ["lib/*"], "@styles/*": ["/styles/*"], } }, From 2a6480e2f1da51b6bf624966ef339ac5a3e9bba7 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 23:51:41 +0100 Subject: [PATCH 19/25] add: added fetcher for swr --- lib/fetcher.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/fetcher.ts diff --git a/lib/fetcher.ts b/lib/fetcher.ts new file mode 100644 index 0000000..04852d0 --- /dev/null +++ b/lib/fetcher.ts @@ -0,0 +1,3 @@ +import { stat } from "fs"; + +export const fetcher = (input: URL | RequestInfo, init?: RequestInit | undefined) => fetch(input, init).then(res => res.json().then(data => ({...data, status: res.status}))); \ No newline at end of file From fcdcc14b85d9e58bbd77e12a42a5afe3e074daaa Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 23:52:41 +0100 Subject: [PATCH 20/25] fix: created components for voting page --- components/custom/gradingForm.tsx | 76 +++++++++++++++++++++++++++++++ components/custom/index.ts | 3 ++ components/custom/testCard.tsx | 32 +++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 components/custom/gradingForm.tsx create mode 100644 components/custom/index.ts create mode 100644 components/custom/testCard.tsx diff --git a/components/custom/gradingForm.tsx b/components/custom/gradingForm.tsx new file mode 100644 index 0000000..a59e47f --- /dev/null +++ b/components/custom/gradingForm.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; + +export function GradingForm({ password, data }: { password: string; data: any }) { + const [grade, setGrade] = useState(0); + const [hasVoted, setHasVoted] = useState(false); + const router = useRouter(); + + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + + const response = await fetch('/api/grade', { + method: 'POST', + body: JSON.stringify({ grade, key: password, testId: data.id }), + }); + const json = await response.json(); + if (response.status == 200) { + setHasVoted(true); + } else { + alert(json.message); + } + } + + if (data.vote.hasVoted || hasVoted) { + return ( +
+

Vous avez déjà voté, merci !

+

{'Vous avez mis ' + (data.vote.hasVoted ? data.vote.grade : grade) + ' !'}

+
+ ); + } + + return ( +
+

Cliquer sur la note à la fin de la présentation :

+ + setGrade(1)}> + +

+ setGrade(1.5)}> + +

+ setGrade(2)}> + +

+ setGrade(2.5)}> + +

+ setGrade(3)}> + +

+ setGrade(3.5)}> + +

+ setGrade(4)}> + +

+ setGrade(4.5)}> + +

+ setGrade(5)}> + +

+ setGrade(5.5)}> + +

+ setGrade(6)}> + +

+ + +
+ ); +} diff --git a/components/custom/index.ts b/components/custom/index.ts new file mode 100644 index 0000000..778de2a --- /dev/null +++ b/components/custom/index.ts @@ -0,0 +1,3 @@ +export { DayCard as DayCard } from './dayCard'; +export { TestCard as TestCard } from './testCard'; +export { GradingForm } from "./gradingForm"; \ No newline at end of file diff --git a/components/custom/testCard.tsx b/components/custom/testCard.tsx new file mode 100644 index 0000000..84e2b3e --- /dev/null +++ b/components/custom/testCard.tsx @@ -0,0 +1,32 @@ +'use client'; + +export function TestCard({ data, error, isLoading }: { data: any; error: any; isLoading: boolean }) { + if (isLoading) + return ( +
+

Aujourd'hui

+

Chargement...

+
+ ); + if (error) + return ( +
+

Aujourd'hui

+

Une erreur est survenue, merci d'essayer dans quelques minutes

+
+ ); + if (data.status == 404) + return ( +
+

{"Aujourd'hui " + new Date().toLocaleDateString()}

+

Pas de test prévu pour aujourd'hui

+
+ ); + + return ( +
+

{"Aujourd'hui " + new Date(data.testOn).toLocaleDateString()}

+

{data.testOf.firstName + ' ' + data.testOf.lastName}

+
+ ); +} From 771c28fa32fd21e61708aace63770387e5496442 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 23:53:07 +0100 Subject: [PATCH 21/25] fix: changed to use components --- app/play/page.tsx | 70 ++++++++++++----------------------------------- 1 file changed, 17 insertions(+), 53 deletions(-) diff --git a/app/play/page.tsx b/app/play/page.tsx index cb8c2f9..574835f 100644 --- a/app/play/page.tsx +++ b/app/play/page.tsx @@ -1,12 +1,17 @@ 'use client'; +import { GradingForm, TestCard } from '@/components/custom'; import { useRouter } from 'next/navigation'; import { useState, useEffect } from 'react'; +import useSWR from 'swr'; +import { fetcher } from '@/lib/fetcher'; export default function Play() { - const [grade, setGrade] = useState(0); const router = useRouter(); const [password, setPassword] = useState(''); + + const { data, error, isLoading } = useSWR('/api/test?key=' + password, fetcher); + useEffect(() => { const pass = localStorage.getItem('@password'); setPassword(pass); @@ -15,62 +20,21 @@ export default function Play() { } }, [router]); - async function handleSubmit(event: React.FormEvent) { - event.preventDefault(); - - const response = await fetch('/api/grade', { - method: 'POST', - body: JSON.stringify({ grade, key: password }), - }); - const data = await response.json(); - if (response.status == 200) { - localStorage.setItem('@voted', new Date().toString()); - } else { - alert(data.message); - } - } - return (

Calendrier-avant

+ + {data && console.log(data)} + {data && data.status == 200 && password && }
-

Cliquer sur la note à la fin de la présentation :

-
- setGrade(1)}> - -

- setGrade(1.5)}> - -

- setGrade(2)}> - -

- setGrade(2.5)}> - -

- setGrade(3)}> - -

- setGrade(3.5)}> - -

- setGrade(4)}> - -

- setGrade(4.5)}> - -

- setGrade(5)}> - -

- setGrade(5.5)}> - -

- setGrade(6)}> - -

- -
+
); From fe914efa4120907f9fe61703c07f261b2cfc8fef Mon Sep 17 00:00:00 2001 From: Fayorg Date: Tue, 5 Dec 2023 23:53:23 +0100 Subject: [PATCH 22/25] add: api routes --- app/api/grade/route.ts | 74 +++++++++++++++++++++++++++++++++++++++++- app/api/test/route.ts | 19 +++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/app/api/grade/route.ts b/app/api/grade/route.ts index 5a491fb..233c945 100644 --- a/app/api/grade/route.ts +++ b/app/api/grade/route.ts @@ -4,8 +4,80 @@ import prisma from "@/lib/prisma"; interface IBody { key: string grade: number + testId: number } export async function POST(req: Request){ - return NextResponse.json({message: 'Server error'}, {status: 500}) + const body: IBody = await req.json(); + + const test = await prisma.test.findFirst({ + select: { + id: true, + testOn: true, + testOf: { + select: { + id: true, + firstName: true, + lastName: true, + isTeacher: true + } + }, + grades: { + select: { + note: true, + grade: true, + createdAt: true, + }, + where: { + user: { + key: body.key + } + } + } + }, + where: { + id: body.testId + } + }); + + if(!test){ + return NextResponse.json({error: "Test not found"}, {status: 404}); + } + + if(test.grades.length > 0){ + return NextResponse.json({error: "You have already voted"}, {status: 403}); + } + + const grade = await prisma.grade.create({ + data: { + note: "", + grade: body.grade, + user: { + connect: { + key: body.key + } + }, + test: { + connect: { + id: test.id + } + } + } + }); + + return NextResponse.json({ + id: test.id, + testOn: test.testOn, + testOf: { + firstName: test.testOf.firstName, + lastName: test.testOf.lastName, + isTeacher: test.testOf.isTeacher + }, + vote: { + hasVoted: test.grades?.length > 0, + grade: grade.grade, + note: grade.note, + createdAt: grade.createdAt + } + }); } diff --git a/app/api/test/route.ts b/app/api/test/route.ts index ebff42d..0d7b03d 100644 --- a/app/api/test/route.ts +++ b/app/api/test/route.ts @@ -3,6 +3,7 @@ import prisma from "@/lib/prisma"; export async function GET(req: NextRequest){ const date = req.nextUrl.searchParams.get("date"); + const key = req.nextUrl.searchParams.get("key"); const test = await prisma.test.findFirst({ select: { @@ -15,6 +16,18 @@ export async function GET(req: NextRequest){ lastName: true, isTeacher: true } + }, + grades: { + select: { + note: true, + grade: true, + createdAt: true, + }, + where: { + user: { + key: key || "" + } + } } }, where: { @@ -33,6 +46,12 @@ export async function GET(req: NextRequest){ firstName: test.testOf.firstName, lastName: test.testOf.lastName, isTeacher: test.testOf.isTeacher + }, + vote: { + hasVoted: test.grades?.length > 0, + grade: test.grades[0]?.grade, + note: test.grades[0]?.note, + createdAt: test.grades[0]?.createdAt } }); } From b9b0c079d8052c2a00a6eba18607a522ac097f35 Mon Sep 17 00:00:00 2001 From: Fayorg Date: Wed, 6 Dec 2023 00:09:24 +0100 Subject: [PATCH 23/25] fix: get test works without a provided date --- app/api/test/route.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/api/test/route.ts b/app/api/test/route.ts index 0d7b03d..2a8d7fe 100644 --- a/app/api/test/route.ts +++ b/app/api/test/route.ts @@ -4,6 +4,8 @@ import prisma from "@/lib/prisma"; export async function GET(req: NextRequest){ const date = req.nextUrl.searchParams.get("date"); const key = req.nextUrl.searchParams.get("key"); + + const usableDate = new Date(date || new Date()); const test = await prisma.test.findFirst({ select: { @@ -31,7 +33,7 @@ export async function GET(req: NextRequest){ } }, where: { - testOn: (date ? new Date(date) : new Date()) + testOn: new Date(usableDate.getFullYear() + "-" + (usableDate.getMonth() + 1) + "-" + (usableDate.getDate().toString().length === 1 ? "0" + usableDate.getDate() : usableDate.getDate())) } }); From 2b05397b55041c582615382b8f7b655d4ff77214 Mon Sep 17 00:00:00 2001 From: timhaller <67918170+timhaller@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:11:24 +0100 Subject: [PATCH 24/25] added results page --- app/results/[id]/page.tsx | 24 ++++++++++++++++++++++-- components/custom/chart.tsx | 3 ++- components/custom/dayCard.tsx | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/results/[id]/page.tsx b/app/results/[id]/page.tsx index c3a16ea..93967e5 100644 --- a/app/results/[id]/page.tsx +++ b/app/results/[id]/page.tsx @@ -3,6 +3,11 @@ import {Chart} from "@components/custom/chart"; import Prisma from '@lib/prisma' +interface data { + name: string + total: number +} + export default async function Page({ params }: { params: { id: string } }) { const grades = await Prisma.grade.findMany({ where: { @@ -10,12 +15,27 @@ export default async function Page({ params }: { params: { id: string } }) { } }) + const allGrades = ['1', '1.5', '2', '2.5', '3', '3.5', '4', '4.5', '5', '5.5', '6'] + let gradeOccurences = new Array(allGrades.length).fill(0) + const gradeList = grades.map((grade) => grade.grade) + + for (let i = 0; i < gradeList.length; i++) { + gradeOccurences[allGrades.indexOf(gradeList[i].toString())]++ + } + + let data: data[] = [] + for (let i = 0; i < gradeOccurences.length; i++) { + data.push({ + name: allGrades[i], + total: gradeOccurences[i] + }) + } + for (let grade in grades) { console.log(grade); } return
- My Post: {params.id} - +
} diff --git a/components/custom/chart.tsx b/components/custom/chart.tsx index dcab596..50bd8fb 100644 --- a/components/custom/chart.tsx +++ b/components/custom/chart.tsx @@ -25,7 +25,8 @@ export function Chart({...props}: ChartProps) { fontSize={12} tickLine={false} axisLine={false} - tickFormatter={(value) => `$${value}`} + allowDecimals={false} + tickFormatter={(value) => `${value}`} /> diff --git a/components/custom/dayCard.tsx b/components/custom/dayCard.tsx index 9334e63..e0faea9 100644 --- a/components/custom/dayCard.tsx +++ b/components/custom/dayCard.tsx @@ -37,7 +37,7 @@ export function DayCard({ className, ...props }: DayCardProps) { } return ( - {route(props.enabled || false, props.day)}}> + {route(props.enabled || false, props.day)}}> {props.enabled ?
From ff7a33a01ecf8f9a3676df1d87f5a875acd0244d Mon Sep 17 00:00:00 2001 From: Fayorg Date: Wed, 6 Dec 2023 00:12:50 +0100 Subject: [PATCH 25/25] fix: change ' to ' --- app/page.tsx | 2 +- components/custom/testCard.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 0882eef..08cd2da 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -23,7 +23,7 @@ export default function Home() { return (

Calendrier-avent

-

Merci d'entrer votre clé :

+

Merci d'entrer votre clé :

setPassword(e.target.value)}> diff --git a/components/custom/testCard.tsx b/components/custom/testCard.tsx index 84e2b3e..306897f 100644 --- a/components/custom/testCard.tsx +++ b/components/custom/testCard.tsx @@ -4,22 +4,22 @@ export function TestCard({ data, error, isLoading }: { data: any; error: any; is if (isLoading) return (
-

Aujourd'hui

+

Aujourd'hui

Chargement...

); if (error) return (
-

Aujourd'hui

-

Une erreur est survenue, merci d'essayer dans quelques minutes

+

Aujourd'hui

+

Une erreur est survenue, merci d'essayer dans quelques minutes

); if (data.status == 404) return (

{"Aujourd'hui " + new Date().toLocaleDateString()}

-

Pas de test prévu pour aujourd'hui

+

Pas de test prévu pour aujourd'hui

);