Merge pull request #5 from Fayorg/dev

Release 1.1.0
This commit is contained in:
Elie Baier 2023-12-18 10:05:20 +01:00 committed by GitHub
commit 937c5e51a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 458 additions and 46 deletions

View File

@ -32,7 +32,7 @@ npm run start
## Credits
Made with love by [Elie Baier](https://github.com/fayorg) & [Tim Haller](https://github.com/timhaller).
Designed & made with love by [Elie Baier](https://github.com/fayorg), [Tim Haller](https://github.com/timhaller) & [Michal Polka](https://github.com/michalpolka).
We'd like to thanks Ms. Tixhon for the good idea and the great gifts!

12
actions/mangeTest.ts Normal file
View File

@ -0,0 +1,12 @@
"use server";
import prisma from "@/lib/prisma";
export async function setTestActive(id: number, active: boolean) {
const users = await prisma.users.findFirst();
console.log(users);
return true;
}

View File

@ -0,0 +1,22 @@
'use client';
import { setTestActive } from '@/actions/mangeTest';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
export default function ActiveCard() {
return (
<AlertDialog>
<AlertDialogTrigger>Open</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Etes-vous sur de vouloir terminer ce test?</AlertDialogTitle>
<AlertDialogDescription>Les votations ne seront plus ouverte pour ce test. Vous pouvez cependant le réactiver dans le dashboard.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => setTestActive(1, true)}>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}

25
app/dashboard/page.tsx Normal file
View File

@ -0,0 +1,25 @@
import prisma from '@/lib/prisma';
import ActiveCard from './ActiveCard';
export default async function Dashboard() {
const tests = await prisma.test.findMany({ select: { isActive: true, isPassed: true, id: true, testOf: { select: { id: true, firstName: true, lastName: true, isTeacher: true } } } });
const activeTests = tests.filter((test) => test.isActive);
const passedTests = tests.filter((test) => test.isPassed);
return (
<div>
<h1>Dashboard</h1>
<div className="border-2 border-white rounded-2xl bg-red-500 p-2">
<h2>Test(s) Actif(s) :</h2>
<ul>
{activeTests.map((test) => (
<li key={test.id}>
{test.testOf.firstName + ' ' + test.testOf.lastName} <ActiveCard />
</li>
))}
</ul>
</div>
</div>
);
}

View File

@ -1,12 +1,19 @@
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import '@styles/global.css';
const inter = Inter({ subsets: ['latin'] });
import type { Metadata } from "next";
export const metadata: Metadata = {
title: 'Calendrier de l\'avent',
description: 'Calendrier de l\'avent de maths de la 3M03',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head><title></title><meta name="viewport" content="viewport-fit=cover" /></head>
<body className={inter.className}>{children}</body>
</html>
);

View File

@ -1,42 +1,46 @@
"use server";
import {Chart} from "@components/custom/chart";
import logo from "@images/logo.svg";
import Image from "next/image";
import Prisma from '@lib/prisma'
'use server';
import { Chart } from '@components/custom/chart';
import logo from '@images/logo.svg';
import Image from 'next/image';
import Prisma from '@lib/prisma';
interface data {
name: string
total: number
name: string;
total: number;
}
export default async function Page({ params }: { params: { id: string } }) {
const teacherGrade = await Prisma.grade.findFirst({ where: { testId: parseInt(params.id), user: { isTeacher: true } } });
const grades = await Prisma.grade.findMany({where: {testId: parseInt(params.id), user: {isTeacher: false}}});
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);
const grades = await Prisma.grade.findMany({ where: { testId: parseInt(params.id) }})
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())]++
gradeOccurences[allGrades.indexOf(gradeList[i].toString())]++;
}
let data: data[] = []
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);
total: gradeOccurences[i],
});
}
return (
<div className={"p-4 md:p-12 w-full h-screen bg-black flex flex-col justify-between gap-y-4"}>
<Image src={logo} alt={"Logo"} width={100} height={200} className={"mx-auto w-full md:w-[400px]"}/>
<Chart data={data}/>
<div className={'p-4 md:p-12 w-full h-screen bg-black flex flex-col justify-between gap-y-4'}>
<Image src={logo} alt={'Logo'} width={100} height={200} className={'mx-auto w-full md:w-[400px]'} />
{teacherGrade && (
<div className={'flex flex-col gap-y-2'}>
<p className="text-white">Mme Tixhon :</p>
<h2 className={'text-4xl font-bold text-white'}>{teacherGrade.grade}</h2>
</div>
)
)}
<Chart data={data} />
</div>
);
}

BIN
assets/end.mp3 Normal file

Binary file not shown.

View File

@ -11,7 +11,8 @@ interface ChartProps {
export function Chart({...props}: ChartProps) {
return (
<div className={"text-[#F0F0F0]"}>
<div className={"text-[#F0F0F0] flex-1 flex items-end"}>
<div className={"w-full"}>
<div className={"flex flex-row justify-between w-full items-end"}>
{props.data.map((item, index) => (
<Santa height={item.total} key={index}/>
@ -26,5 +27,6 @@ export function Chart({...props}: ChartProps) {
))}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,43 @@
"use client";
import React from 'react';
import { useTimer } from 'react-timer-hook';
import { Button } from "@components/ui/button";
import { PauseIcon, Play} from "lucide-react";
interface MyTimerProps {
expiryTimestamp: Date;
onEnd?: () => void;
}
export default function MyTimer({ expiryTimestamp, onEnd } : MyTimerProps) {
const {
seconds,
minutes,
isRunning,
pause,
resume,
} = useTimer({
expiryTimestamp,
onExpire: () => onEnd && onEnd()
})
return (
<div className={"text-white text-center flex flex-col"}>
<div className={"font-bold text-8xl"}>
<span>{String(minutes).padStart(2, "0")}</span>:
<span>{String(seconds).padStart(2, "0")}</span>
</div>
<p>{isRunning ? 'Running' : 'Not running'}</p>
<div className={"self-center flex gap-4"}>
<Button onClick={pause} disabled={!isRunning}>
<PauseIcon/>
</Button>
<Button onClick={resume} disabled={isRunning}>
<Play/>
</Button>
</div>
</div>
);
}

View File

@ -0,0 +1,141 @@
"use client"
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref}
/>
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
/>
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
AlertDialogHeader.displayName = "AlertDialogHeader"
const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
AlertDialogFooter.displayName = "AlertDialogFooter"
const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold", className)}
{...props}
/>
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName
const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0",
className
)}
{...props}
/>
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}

56
components/ui/button.tsx Normal file
View File

@ -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<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

74
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@prisma/client": "^5.6.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
@ -22,6 +23,7 @@
"react": "^18",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18",
"react-timer-hook": "^3.0.7",
"socket.io": "^4.7.2",
"socket.io-client": "^4.7.2",
"swr": "^2.2.4",
@ -471,6 +473,34 @@
"@babel/runtime": "^7.13.10"
}
},
"node_modules/@radix-ui/react-alert-dialog": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz",
"integrity": "sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-dialog": "1.0.5",
"@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-arrow": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
@ -554,6 +584,42 @@
}
}
},
"node_modules/@radix-ui/react-dialog": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
"integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@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-focus-guards": "1.0.1",
"@radix-ui/react-focus-scope": "1.0.4",
"@radix-ui/react-id": "1.0.1",
"@radix-ui/react-portal": "1.0.4",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-slot": "1.0.2",
"@radix-ui/react-use-controllable-state": "1.0.1",
"aria-hidden": "^1.1.1",
"react-remove-scroll": "2.5.5"
},
"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-direction": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
@ -4370,6 +4436,14 @@
}
}
},
"node_modules/react-timer-hook": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/react-timer-hook/-/react-timer-hook-3.0.7.tgz",
"integrity": "sha512-ATpNcU+PQRxxfNBPVqce2+REtjGAlwmfoNQfcEBMZFxPj0r3GYdKhyPHdStvqrejejEi0QvqaJZjy2lBlFvAsA==",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",

View File

@ -10,6 +10,7 @@
},
"dependencies": {
"@prisma/client": "^5.6.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
@ -23,6 +24,7 @@
"react": "^18",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18",
"react-timer-hook": "^3.0.7",
"socket.io": "^4.7.2",
"socket.io-client": "^4.7.2",
"swr": "^2.2.4",

View File

@ -47,4 +47,23 @@
--input: 0 0% 80%;
--ring: 348 71% 60%;
}
html {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
background-color: black;
}
body {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
background-color: black;
}
/* If using React, set height on the root div as well */
#root {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
background-color: black;
}
}

View File

@ -25,8 +25,9 @@
"@styles/*": ["/styles/*"],
"@santa/*": ["./images/santa/*"],
"@images/*": ["./images/*"],
"@assets/*": ["./assets/*"],
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "**/*.d.ts"],
"exclude": ["node_modules"]
}

4
types/sounds.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.mp3' {
const content: string;
export default content;
}