diff --git a/README.md b/README.md index b1c3f91..b341baf 100644 --- a/README.md +++ b/README.md @@ -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! diff --git a/actions/mangeTest.ts b/actions/mangeTest.ts new file mode 100644 index 0000000..74822da --- /dev/null +++ b/actions/mangeTest.ts @@ -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; + +} \ No newline at end of file diff --git a/app/dashboard/ActiveCard.tsx b/app/dashboard/ActiveCard.tsx new file mode 100644 index 0000000..4152d49 --- /dev/null +++ b/app/dashboard/ActiveCard.tsx @@ -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 ( + + Open + + + Etes-vous sur de vouloir terminer ce test? + Les votations ne seront plus ouverte pour ce test. Vous pouvez cependant le réactiver dans le dashboard. + + + Cancel + setTestActive(1, true)}>Continue + + + + ); +} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..7cf6c54 --- /dev/null +++ b/app/dashboard/page.tsx @@ -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 ( +
+

Dashboard

+
+

Test(s) Actif(s) :

+ +
+
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx index dd3b95c..027c8dc 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -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 ( + {children} ); diff --git a/app/results/[id]/page.tsx b/app/results/[id]/page.tsx index dc1412c..021e298 100644 --- a/app/results/[id]/page.tsx +++ b/app/results/[id]/page.tsx @@ -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) }}) - 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), user: {isTeacher: false}}}); - for (let i = 0; i < gradeList.length; i++) { - gradeOccurences[allGrades.indexOf(gradeList[i].toString())]++ - } + 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); - 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); - } + for (let i = 0; i < gradeList.length; i++) { + gradeOccurences[allGrades.indexOf(gradeList[i].toString())]++; + } - return ( -
- {"Logo"} - -
- ) + let data: data[] = []; + for (let i = 0; i < gradeOccurences.length; i++) { + data.push({ + name: allGrades[i], + total: gradeOccurences[i], + }); + } + return ( +
+ {'Logo'} + {teacherGrade && ( +
+

Mme Tixhon :

+

{teacherGrade.grade}

+
+ )} + +
+ ); } diff --git a/assets/end.mp3 b/assets/end.mp3 new file mode 100644 index 0000000..f66643c Binary files /dev/null and b/assets/end.mp3 differ diff --git a/components/custom/chart.tsx b/components/custom/chart.tsx index b40690c..a271d8f 100644 --- a/components/custom/chart.tsx +++ b/components/custom/chart.tsx @@ -11,19 +11,21 @@ interface ChartProps { export function Chart({...props}: ChartProps) { return ( -
-
- {props.data.map((item, index) => ( - - ))} -
-
- {props.data.map((item, index) => ( -
-
- {item.name} -
- ))} +
+
+
+ {props.data.map((item, index) => ( + + ))} +
+
+ {props.data.map((item, index) => ( +
+
+ {item.name} +
+ ))} +
) diff --git a/components/custom/countdown.tsx b/components/custom/countdown.tsx new file mode 100644 index 0000000..a60571f --- /dev/null +++ b/components/custom/countdown.tsx @@ -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 ( +
+
+ {String(minutes).padStart(2, "0")}: + {String(seconds).padStart(2, "0")} +
+

{isRunning ? 'Running' : 'Not running'}

+
+ + +
+
+ ); +} diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..ce104c2 --- /dev/null +++ b/components/ui/alert-dialog.tsx @@ -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, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} 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/package-lock.json b/package-lock.json index 22a96ec..f976ea6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 4b194bb..826bdc1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/styles/global.css b/styles/global.css index fb1ff9a..fe6db4b 100644 --- a/styles/global.css +++ b/styles/global.css @@ -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; + } } diff --git a/tsconfig.json b/tsconfig.json index 709a6e8..66d7499 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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"] } diff --git a/types/sounds.d.ts b/types/sounds.d.ts new file mode 100644 index 0000000..3f6ebdd --- /dev/null +++ b/types/sounds.d.ts @@ -0,0 +1,4 @@ +declare module '*.mp3' { + const content: string; + export default content; +}