From f9aef69cfaf4bcfbcccada0983b469fbcb543eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi?= Date: Fri, 3 Jan 2025 02:19:20 +0100 Subject: [PATCH] feat: add Room component and refactor Conference components, implement custom scrollbar styles and smooth --- .prettierrc | 5 + src/app/components/Conference/List.tsx | 24 --- src/app/components/Header/index.tsx | 109 ++++++------- .../components/{Conference => Room}/Card.tsx | 148 ++++++++--------- src/app/components/Room/List.tsx | 57 +++++++ .../Conference.d.ts => Room/Room.d.ts} | 32 ++-- .../ThemeSwitcher/ThemeSwitcher.tsx | 24 ++- src/app/globals.css | 18 ++- src/app/page.tsx | 151 ++++++++++-------- 9 files changed, 322 insertions(+), 246 deletions(-) create mode 100644 .prettierrc delete mode 100644 src/app/components/Conference/List.tsx rename src/app/components/{Conference => Room}/Card.tsx (89%) create mode 100644 src/app/components/Room/List.tsx rename src/app/components/{Conference/Conference.d.ts => Room/Room.d.ts} (93%) diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1035938 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 4, + "useTabs": true, + "semi": true +} diff --git a/src/app/components/Conference/List.tsx b/src/app/components/Conference/List.tsx deleted file mode 100644 index c7dca2d..0000000 --- a/src/app/components/Conference/List.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client" -import { ConferenceCard } from "./Card"; -import { Room } from "./Conference"; - -export const ConferenceList = ({ - rooms, -}: { - rooms: Room[]; -}) => { - return ( -
- {rooms.map((room) => ( - - ))} -
- ); -}; diff --git a/src/app/components/Header/index.tsx b/src/app/components/Header/index.tsx index 14f61fa..2847409 100644 --- a/src/app/components/Header/index.tsx +++ b/src/app/components/Header/index.tsx @@ -1,3 +1,4 @@ +'use client' import { Avatar, Button, @@ -8,85 +9,79 @@ import { Navbar, NavbarBrand, NavbarContent, - NavbarItem, -} from "@nextui-org/react"; -import { useSession } from "next-auth/react"; -import { ThemeSwitcher } from "../ThemeSwitcher/ThemeSwitcher"; -import { axiosInstance } from "@/app/lib/axios"; -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; + NavbarItem +} from '@nextui-org/react' +import { useSession } from 'next-auth/react' +import { ThemeSwitcher } from '../ThemeSwitcher/ThemeSwitcher' +import { axiosInstance } from '@/app/lib/axios' +import { useEffect, useState } from 'react' +import { useRouter } from 'next/navigation' const getInitials = (name: string) => { - if (!name) return ""; + if (!name) return '' - const nameParts = name.split(" "); + const nameParts = name.split(' ') if (nameParts.length === 1) { - return name; + return name } - const firstInitial = nameParts[0]?.[0] || ""; - const secondInitial = nameParts[1]?.[0] || nameParts[0]?.[1] || ""; + const firstInitial = nameParts[0]?.[0] || '' + const secondInitial = nameParts[1]?.[0] || nameParts[0]?.[1] || '' - return firstInitial + secondInitial; -}; + return firstInitial + secondInitial +} export const Header = () => { - const { data: session } = useSession(); - const router = useRouter(); + const { data: session } = useSession() + const router = useRouter() const [userProfile, setUserProfile] = useState<{ - id: string; - username: string; - role: "ADMIN" | "STUDENT"; - }>(); + id: string + username: string + role: 'ADMIN' | 'STUDENT' + }>() - const initials = session?.user?.name ? getInitials(session.user.name) : ""; + const initials = session?.user?.name ? getInitials(session.user.name) : '' const fetchUserProfile = async () => { return await axiosInstance<{ - id: string; - username: string; - role: "ADMIN" | "STUDENT"; - }>("/@me"); + id: string + username: string + role: 'ADMIN' | 'STUDENT' + }>('/@me') } useEffect(() => { fetchUserProfile().then(r => { - setUserProfile(r.data); - }); - }, []); + setUserProfile(r.data) + }) + }, []) return ( - + -

Toogether

+

Toogether

- - + + - - + +

Signed in as

-

- {session?.user?.name} -

+

{session?.user?.name}

- Settings - + Settings + Logout
@@ -94,18 +89,12 @@ export const Header = () => { - { - userProfile?.role === "ADMIN" ? ( - - - - ) : null - } + {userProfile?.role === 'ADMIN' ? ( + + + + ) : null}
- ); -}; + ) +} diff --git a/src/app/components/Conference/Card.tsx b/src/app/components/Room/Card.tsx similarity index 89% rename from src/app/components/Conference/Card.tsx rename to src/app/components/Room/Card.tsx index 0387559..1863f78 100644 --- a/src/app/components/Conference/Card.tsx +++ b/src/app/components/Room/Card.tsx @@ -1,74 +1,74 @@ -"use client"; - -import { parseDate, Time } from "@internationalized/date"; - -import { - Button, - Card, - CardBody, - CardHeader, - DateInput, - Divider, - TimeInput, -} from "@nextui-org/react"; -import { Room } from "./Conference"; -import moment from "moment"; -import { useRouter } from "next/navigation"; - -export const ConferenceCard = ({ - id, - name, - date, - Times, - Presentator -}: Room) => { - const router = useRouter(); - - return ( - - -
-

{name}

-

{Presentator.username}

-
-
- - - {Times.map((time) => ( -
- -
- - - - -
-
- ))} -
-
- -
-
- ); -}; +"use client"; + +import { parseDate, Time } from "@internationalized/date"; + +import { + Button, + Card, + CardBody, + CardHeader, + DateInput, + Divider, + TimeInput, +} from "@nextui-org/react"; +import { Room } from "./Room"; +import moment from "moment"; +import { useRouter } from "next/navigation"; + +export const RoomCard = ({ + id, + name, + date, + Times, + Presentator +}: Room) => { + const router = useRouter(); + + return ( + + +
+

{name}

+

{Presentator.username}

+
+
+ + + {Times.map((time) => ( +
+ +
+ + - + +
+
+ ))} +
+
+ +
+
+ ); +}; diff --git a/src/app/components/Room/List.tsx b/src/app/components/Room/List.tsx new file mode 100644 index 0000000..5c3966d --- /dev/null +++ b/src/app/components/Room/List.tsx @@ -0,0 +1,57 @@ +'use client'; +import { useEffect, useRef } from 'react'; +import { RoomCard } from './Card'; +import { Room } from './Room'; + +export const RoomList = ({ rooms }: { rooms: Room[] }) => { + const scrollContainerRef = useRef(null); + + const handleWheel = (event: WheelEvent) => { + if (event.deltaY === 0) return; + event.preventDefault(); + const scrollContainer = scrollContainerRef.current; + if (!scrollContainer) return; + + const scrollAmount = 10; + const direction = event.deltaY > 0 ? 1 : -1; + let scrollCount = 0; + + const interval = setInterval(() => { + scrollContainer.scrollLeft += scrollAmount * direction; + scrollCount += scrollAmount; + if (scrollCount >= 100) { + clearInterval(interval); + } + }, 10); + + }; + + useEffect(() => { + const scrollContainer = scrollContainerRef.current; + if (!scrollContainer) return; + + scrollContainer.addEventListener('wheel', handleWheel); + + return () => { + scrollContainer.removeEventListener('wheel', handleWheel); + }; + }, []); + + return ( +
+
    + {rooms.map(room => ( +
  • + +
  • + ))} +
+
+ ); +}; diff --git a/src/app/components/Conference/Conference.d.ts b/src/app/components/Room/Room.d.ts similarity index 93% rename from src/app/components/Conference/Conference.d.ts rename to src/app/components/Room/Room.d.ts index e60551f..2d76fbb 100644 --- a/src/app/components/Conference/Conference.d.ts +++ b/src/app/components/Room/Room.d.ts @@ -1,17 +1,17 @@ -export interface Room { - id: string; - name: string; - date: string; - Times: { - id: number; - startTime: string; - endTime: string; - roomId: string; - }[]; - Presentator: { - id: string; - username: string; - role: string; - createdAt: string; - } +export interface Room { + id: string; + name: string; + date: string; + Times: { + id: number; + startTime: string; + endTime: string; + roomId: string; + }[]; + Presentator: { + id: string; + username: string; + role: string; + createdAt: string; + } } \ No newline at end of file diff --git a/src/app/components/ThemeSwitcher/ThemeSwitcher.tsx b/src/app/components/ThemeSwitcher/ThemeSwitcher.tsx index bc8efe1..d57459c 100644 --- a/src/app/components/ThemeSwitcher/ThemeSwitcher.tsx +++ b/src/app/components/ThemeSwitcher/ThemeSwitcher.tsx @@ -1,13 +1,21 @@ -"use client"; -import { Button } from "@nextui-org/react"; -import { useTheme } from "next-themes"; +'use client' +import { Button } from '@nextui-org/react' +import { useTheme } from 'next-themes' +import { useEffect, useState } from 'react' export const ThemeSwitcher = () => { - const { theme, setTheme } = useTheme(); + const [mounted, setMounted] = useState(false) + const { theme, setTheme } = useTheme() + + useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) return null return ( - - ); -}; + ) +} diff --git a/src/app/globals.css b/src/app/globals.css index bd6213e..145073e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,3 +1,19 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +@layer utilities { + .scrollbar::-webkit-scrollbar { + height: 10px; + } + + .scrollbar::-webkit-scrollbar-track { + border-radius: 100vh; + background: #5a5a5a54; + } + + .scrollbar::-webkit-scrollbar-thumb { + background: #414141; + border-radius: 100vh; + } +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 2ee7112..28b7df9 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,37 +1,39 @@ -"use client"; -import { Card, Divider, Skeleton } from "@nextui-org/react"; -import moment from "moment"; -import { useEffect, useState } from "react"; -import { Room } from "./components/Conference/Conference"; -import { ConferenceList } from "./components/Conference/List"; -import { Header } from "./components/Header"; -import { axiosInstance } from "./lib/axios"; +'use client'; +import { Card, Divider, Skeleton } from '@nextui-org/react'; +import moment from 'moment'; +import { useEffect, useState } from 'react'; +import { Room } from './components/Room/Room'; +import { RoomList } from './components/Room/List'; +import { Header } from './components/Header'; +import { axiosInstance } from './lib/axios'; const SkeletonCard = () => { - return -
- -
+ return ( + +
+ +
+ + +
+ +
+ + +
- -
- -
- - -
- -
- -
- - - - -
- -
- -} +
+ +
+ + - + +
+ +
+ + ); +}; const HomePage = () => { const [roomsLoading, setRoomsLoading] = useState(true); @@ -46,70 +48,93 @@ const HomePage = () => { }); useEffect(() => { - axiosInstance.get<{ id: string, name: string, createdAt: string }[]>("/@me/class") - .then((classResponse) => { + axiosInstance + .get<{ id: string; name: string; createdAt: string }[]>( + '/@me/class' + ) + .then(classResponse => { if (classResponse.data.length) - axiosInstance.get(`/@me/class/${classResponse.data[0].id}/rooms`) + axiosInstance + .get( + `/@me/class/${classResponse.data[0].id}/rooms` + ) .then(classes => { // Filter rooms by date, get future, actual and past rooms - const future = classes.data.filter(room => moment(room.date).isAfter(moment(), "day")); - const actual = classes.data.filter(room => moment(room.date).isSame(moment(), "day")); - const past = classes.data.filter(room => moment(room.date).isBefore(moment())); + const future = classes.data.filter(room => + moment(room.date).isAfter(moment(), 'day') + ); + const actual = classes.data.filter(room => + moment(room.date).isSame(moment(), 'day') + ); + const past = classes.data.filter(room => + moment(room.date).isBefore(moment()) + ); setRooms({ future, actual, past }); setRoomsLoading(false); }); - }) - + }); }, []); return ( <>
-
- {roomsLoading - ? <> -
-

Cours a venir

-
+
+ {roomsLoading ? ( + <> +
+

+ Cours a venir +

+
-
-

Cours actuels

-
+
+

+ Cours actuels +

+
-
-

Cours passés

-
+
+

+ Cours passés +

+
- : <> -
-

Cours a venir

- + ) : ( + <> +
+

+ Cours a venir +

+
-
-

Cours actuels

- +
+

+ Cours actuels +

+
-
-

Cours passés

- +
+

+ Cours passés +

+
- - } + )}
);