feat: add ProvidersList component for authentication and update LoginPage
This commit is contained in:
parent
096a10b9c1
commit
38fa50e0c8
@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
import { authOptions } from "@/authOptions";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { Metadata } from "next";
|
||||
import { ProvidersList } from "./providersList";
|
||||
|
||||
const LoginPage = () => {
|
||||
const provider = authOptions.providers[0];
|
||||
export const metadata: Metadata = {
|
||||
title: "Toogether | Connexion",
|
||||
};
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className="flex flex-col h-screen items-center justify-center bg-gradient-to-br from-green-700 to-green-950 bg-[length:200%_200%] animate-gradient-x text-white">
|
||||
<div className="flex flex-col justify-center items-center gap-6 bg-black bg-opacity-40 rounded-md p-5 mx-5 w-2/3">
|
||||
@ -25,19 +26,8 @@ const LoginPage = () => {
|
||||
<div className="w-full border-t border-white"></div>
|
||||
|
||||
<h3 className="font-bold text-xl">Via</h3>
|
||||
<ul>
|
||||
<li key={provider.id}>
|
||||
<button
|
||||
className="bg-white text-black p-2 rounded-md"
|
||||
onClick={() => signIn(provider.id)}
|
||||
>
|
||||
{provider.name}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ProvidersList />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
}
|
||||
|
22
src/app/auth/login/providersList.tsx
Normal file
22
src/app/auth/login/providersList.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import { authOptions } from "@/authOptions";
|
||||
import { signIn } from "next-auth/react";
|
||||
|
||||
export const ProvidersList = () => {
|
||||
const providers = authOptions.providers;
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
{providers.map((provider) => (
|
||||
<button
|
||||
key={provider.id}
|
||||
className="bg-white text-black p-2 rounded-md"
|
||||
onClick={() => signIn(provider.id)}
|
||||
>
|
||||
{provider.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
'use client';
|
||||
"use client";
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
@ -9,24 +9,24 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
@ -38,39 +38,39 @@ export const Header = () => {
|
||||
const [userProfile, setUserProfile] = useState<{
|
||||
id: string;
|
||||
username: string;
|
||||
role: 'ADMIN' | 'STUDENT';
|
||||
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');
|
||||
role: "ADMIN" | "STUDENT";
|
||||
}>("/@me");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserProfile().then(r => {
|
||||
fetchUserProfile().then((r) => {
|
||||
setUserProfile(r.data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Navbar className='mb-2'>
|
||||
<Navbar className="mb-2">
|
||||
<NavbarBrand>
|
||||
<p className='font-bold text-inherit'>Toogether</p>
|
||||
<p className="font-bold text-inherit">Toogether</p>
|
||||
</NavbarBrand>
|
||||
|
||||
<NavbarContent as='div' justify='end'>
|
||||
{userProfile?.role === 'ADMIN' ? (
|
||||
<NavbarContent as="div" justify="end">
|
||||
{userProfile?.role === "ADMIN" ? (
|
||||
<NavbarItem>
|
||||
<Button
|
||||
size='sm'
|
||||
variant='flat'
|
||||
className='min-w-0'
|
||||
onPress={() => router.push('/admin')}
|
||||
size="sm"
|
||||
variant="flat"
|
||||
className="min-w-0"
|
||||
onPress={() => router.push("/admin")}
|
||||
>
|
||||
🔧
|
||||
</Button>
|
||||
@ -80,29 +80,29 @@ export const Header = () => {
|
||||
<ThemeSwitcher />
|
||||
</NavbarItem>
|
||||
|
||||
<Dropdown placement='bottom-end'>
|
||||
<Dropdown placement="bottom-end">
|
||||
<DropdownTrigger>
|
||||
<Avatar
|
||||
isBordered
|
||||
as='button'
|
||||
className='transition-transform'
|
||||
color='secondary'
|
||||
as="button"
|
||||
className="transition-transform"
|
||||
color="secondary"
|
||||
name={initials}
|
||||
size='sm'
|
||||
size="sm"
|
||||
/>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label='Profile Actions' variant='flat'>
|
||||
<DropdownItem key='profile' className='h-14 gap-2'>
|
||||
<DropdownMenu aria-label="Profile Actions" variant="flat">
|
||||
<DropdownItem key="profile" className="h-14 gap-2">
|
||||
<p>Signed in as</p>
|
||||
<p className='font-semibold'>
|
||||
<p className="font-semibold">
|
||||
{session?.user?.name}
|
||||
</p>
|
||||
</DropdownItem>
|
||||
<DropdownItem key='settings'>Settings</DropdownItem>
|
||||
<DropdownItem key="settings">Settings</DropdownItem>
|
||||
<DropdownItem
|
||||
key='logout'
|
||||
color='danger'
|
||||
href='/auth/logout'
|
||||
key="logout"
|
||||
color="danger"
|
||||
href="/auth/logout"
|
||||
>
|
||||
Logout
|
||||
</DropdownItem>
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { parseDate, Time } from '@internationalized/date';
|
||||
import { parseDate, Time } from "@internationalized/date";
|
||||
|
||||
import {
|
||||
Button,
|
||||
@ -9,56 +9,56 @@ import {
|
||||
CardHeader,
|
||||
DateInput,
|
||||
Divider,
|
||||
TimeInput
|
||||
} from '@nextui-org/react';
|
||||
import { Room } from './Room';
|
||||
import moment from 'moment';
|
||||
import { useRouter } from 'next/navigation';
|
||||
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 (
|
||||
<Card className='w-[300px]'>
|
||||
<Card className="w-[300px]">
|
||||
<CardHeader>
|
||||
<div className='flex flex-col min-h-20'>
|
||||
<p className='text-md'>{name}</p>
|
||||
<p className='text-small text-default-500'>
|
||||
<div className="flex flex-col min-h-20">
|
||||
<p className="text-md">{name}</p>
|
||||
<p className="text-small text-default-500">
|
||||
{Presentator.username}
|
||||
</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<Divider />
|
||||
<CardBody>
|
||||
<div className='flex flex-col gap-2' key={`times.${id}`}>
|
||||
<div className="flex flex-col gap-2" key={`times.${id}`}>
|
||||
<DateInput
|
||||
isReadOnly
|
||||
label='Date'
|
||||
value={parseDate(moment(date).format('YYYY-MM-DD'))}
|
||||
label="Date"
|
||||
value={parseDate(moment(date).format("YYYY-MM-DD"))}
|
||||
/>
|
||||
{Times.map(time => (
|
||||
{Times.map((time) => (
|
||||
<div key={`time.${time.id}`}>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className="flex items-center gap-2">
|
||||
<TimeInput
|
||||
isReadOnly
|
||||
label='Start'
|
||||
label="Start"
|
||||
hourCycle={24}
|
||||
value={
|
||||
new Time(
|
||||
moment(time.startTime).hours(),
|
||||
moment(time.startTime).minutes()
|
||||
moment(time.startTime).minutes(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<span>-</span>
|
||||
<TimeInput
|
||||
isReadOnly
|
||||
label='End'
|
||||
label="End"
|
||||
hourCycle={24}
|
||||
value={
|
||||
new Time(
|
||||
moment(time.endTime).hours(),
|
||||
moment(time.endTime).minutes()
|
||||
moment(time.endTime).minutes(),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@ -68,13 +68,13 @@ export const RoomCard = ({ id, name, date, Times, Presentator }: Room) => {
|
||||
</div>
|
||||
</CardBody>
|
||||
{moment(date).dayOfYear() === moment().dayOfYear() && (
|
||||
<div className='flex p-2'>
|
||||
<div className="flex p-2">
|
||||
<Button
|
||||
className={''}
|
||||
color='primary'
|
||||
radius='full'
|
||||
size='sm'
|
||||
variant={'flat'}
|
||||
className={""}
|
||||
color="primary"
|
||||
radius="full"
|
||||
size="sm"
|
||||
variant={"flat"}
|
||||
onPress={() => {
|
||||
router.push(`/room/${id}`);
|
||||
}}
|
||||
|
@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { RoomCard } from './Card';
|
||||
import { Room } from './Room';
|
||||
import { SkeletonRoomCard } from './SkeletonRoomCard';
|
||||
"use client";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { RoomCard } from "./Card";
|
||||
import { Room } from "./Room";
|
||||
import { SkeletonRoomCard } from "./SkeletonRoomCard";
|
||||
|
||||
export const RoomList = ({ rooms }: { rooms: Room[] }) => {
|
||||
const scrollContainerRef = useRef<HTMLUListElement>(null);
|
||||
@ -18,7 +18,7 @@ export const RoomList = ({ rooms }: { rooms: Room[] }) => {
|
||||
const isEnd = goLeft
|
||||
? scrollContainer.scrollLeft === 0
|
||||
: scrollContainer.scrollLeft + scrollContainer.clientWidth >=
|
||||
scrollContainer.scrollWidth;
|
||||
scrollContainer.scrollWidth;
|
||||
if (isEnd) return;
|
||||
|
||||
event.preventDefault();
|
||||
@ -40,20 +40,20 @@ export const RoomList = ({ rooms }: { rooms: Room[] }) => {
|
||||
const scrollContainer = scrollContainerRef.current;
|
||||
if (!scrollContainer) return;
|
||||
|
||||
scrollContainer.addEventListener('wheel', handleWheel);
|
||||
scrollContainer.addEventListener("wheel", handleWheel);
|
||||
|
||||
return () => {
|
||||
scrollContainer.removeEventListener('wheel', handleWheel);
|
||||
scrollContainer.removeEventListener("wheel", handleWheel);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ul
|
||||
ref={scrollContainerRef}
|
||||
className='flex gap-2 overflow-x-auto scrollbar-hide rounded-xl bg-default-100 p-1'
|
||||
className="flex gap-2 overflow-x-auto scrollbar-hide rounded-xl bg-default-100 p-1"
|
||||
>
|
||||
{rooms.length > 0 ? (
|
||||
rooms.map(room => (
|
||||
rooms.map((room) => (
|
||||
<li key={room.id}>
|
||||
<RoomCard
|
||||
id={room.id}
|
||||
|
@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
import { axiosInstance } from '@/app/lib/axios';
|
||||
import moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { RoomList } from './List';
|
||||
import { Room } from './Room';
|
||||
"use client";
|
||||
import { axiosInstance } from "@/app/lib/axios";
|
||||
import moment from "moment";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RoomList } from "./List";
|
||||
import { Room } from "./Room";
|
||||
|
||||
export const RoomTable = () => {
|
||||
const [rooms, setRooms] = useState<{
|
||||
@ -13,30 +13,30 @@ export const RoomTable = () => {
|
||||
}>({
|
||||
future: [],
|
||||
actual: [],
|
||||
past: []
|
||||
past: [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
axiosInstance
|
||||
.get<{ id: string; name: string; createdAt: string }[]>(
|
||||
'/@me/class'
|
||||
)
|
||||
.then(classResponse => {
|
||||
.get<
|
||||
{ id: string; name: string; createdAt: string }[]
|
||||
>("/@me/class")
|
||||
.then((classResponse) => {
|
||||
if (classResponse.data.length)
|
||||
axiosInstance
|
||||
.get<Room[]>(
|
||||
`/@me/class/${classResponse.data[0].id}/rooms`
|
||||
)
|
||||
.then(classes => {
|
||||
.get<
|
||||
Room[]
|
||||
>(`/@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 future = classes.data.filter((room) =>
|
||||
moment(room.date).isAfter(moment(), "day"),
|
||||
);
|
||||
const actual = classes.data.filter(room =>
|
||||
moment(room.date).isSame(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 past = classes.data.filter((room) =>
|
||||
moment(room.date).isBefore(moment()),
|
||||
);
|
||||
setRooms({ future, actual, past });
|
||||
});
|
||||
@ -44,17 +44,17 @@ export const RoomTable = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<section className='flex flex-col gap-2'>
|
||||
<h2 className='font-semibold text-lg'>Upcoming</h2>
|
||||
<div className="flex flex-col gap-4">
|
||||
<section className="flex flex-col gap-2">
|
||||
<h2 className="font-semibold text-lg">Upcoming</h2>
|
||||
<RoomList rooms={rooms.future} />
|
||||
</section>
|
||||
<section className='flex flex-col gap-2'>
|
||||
<h2 className='font-semibold text-lg'>Today</h2>
|
||||
<section className="flex flex-col gap-2">
|
||||
<h2 className="font-semibold text-lg">Today</h2>
|
||||
<RoomList rooms={rooms.actual} />
|
||||
</section>
|
||||
<section className='flex flex-col gap-2'>
|
||||
<h2 className='font-semibold text-lg'>Past</h2>
|
||||
<section className="flex flex-col gap-2">
|
||||
<h2 className="font-semibold text-lg">Past</h2>
|
||||
<RoomList rooms={rooms.past} />
|
||||
</section>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { Button } from '@nextui-org/react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect, useState } from 'react';
|
||||
"use client";
|
||||
import { Button } from "@nextui-org/react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const ThemeSwitcher = () => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@ -15,12 +15,12 @@ export const ThemeSwitcher = () => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
size='sm'
|
||||
variant='flat'
|
||||
className='min-w-0'
|
||||
onPress={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
||||
size="sm"
|
||||
variant="flat"
|
||||
className="min-w-0"
|
||||
onPress={() => setTheme(theme === "light" ? "dark" : "light")}
|
||||
>
|
||||
{theme === 'light' ? '🌑' : '☀️'}
|
||||
{theme === "light" ? "🌑" : "☀️"}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { Metadata } from 'next';
|
||||
import { Header } from './components/Header';
|
||||
import { RoomTable } from './components/Room/Table';
|
||||
import { Metadata } from "next";
|
||||
import { Header } from "./components/Header";
|
||||
import { RoomTable } from "./components/Room/Table";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Toogether | Home',
|
||||
title: "Toogether | Home",
|
||||
description:
|
||||
'Toogether is a platform that allows you to create and join rooms to study together.'
|
||||
"Toogether is a platform that allows you to create and join rooms to study together.",
|
||||
};
|
||||
|
||||
export default function HomePage () {
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className='flex flex-col gap-8 p-4'>
|
||||
<main className="flex flex-col gap-8 p-4">
|
||||
<RoomTable />
|
||||
</main>
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user