Added theme switcher + optimized youtube loading + about changes

This commit is contained in:
cdricms
2025-01-31 17:28:13 +01:00
parent f7dd0c60d6
commit 90945b161d
8 changed files with 306 additions and 267 deletions

View File

@@ -23,22 +23,18 @@ export default async function About() {
);
return (
<>
<div className="px-4 py-8 lg:px-24 lg:py-32">
<div className="
flex flex-col lg:flex-row gap-4
justify-center w-full pb-24">
<div className="
flex flex-col gap-4 w-full
lg:w-3/5 justify-center">
<Card className="py-5 sm:mx-10 max-h-fit">
<div className="">
<div className="flex flex-col lg:flex-row gap-4 justify-between w-full p-12">
<div className="flex flex-col lg:w-1/2 xl:w-full gap-4 w-full justify-center">
<Card className="py-5 max-h-fit">
<CardHeader className="text-center p-2">
<CardTitle className="m-5">
Entraîneur depuis 60 ans
<CardTitle className="text-5xl">
Nicolas GORUK
</CardTitle>
<span className="font-bold text-4xl
sm:text-4xl">
Robert Louis Jean Jacke
</span>
<CardDescription>
Président l'association française de Latosa
Escrima
</CardDescription>
</CardHeader>
<CardContent className="px-8 sm:px-10 py-14">
<div className="flex flex-col gap-4 justify-center">
@@ -47,74 +43,30 @@ export default async function About() {
</h2>
<p className="blog-paragraph text-muted-foreground">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Debitis accusamus illum,
nam nemo quod delectus velit repellat odio
dolorum sapiente soluta, aliquam atque
praesentium ea placeat ad, neque eveniet
adipisci?
adipisicing elit. Debitis accusamus
illum, nam nemo quod delectus velit
repellat odio dolorum sapiente soluta,
aliquam atque praesentium ea placeat ad,
neque eveniet adipisci?
</p>
<h2 className="text-pretty text-center text-xl font-semibold md:mb-0.5 lg:mb-1 lg:max-w-3xl sm:text-3xl">
Lorem ipsum, dolor sit amet
</h2>
<p className="blog-paragraph text-muted-foreground">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Debitis accusamus illum,
nam nemo quod delectus velit repellat odio
dolorum sapiente soluta, aliquam atque
praesentium ea placeat ad, neque eveniet
adipisci?
adipisicing elit. Debitis accusamus
illum, nam nemo quod delectus velit
repellat odio dolorum sapiente soluta,
aliquam atque praesentium ea placeat ad,
neque eveniet adipisci?
</p>
</div>
</CardContent>
</Card>
<div className="max-w-full my-8 text-center lg:hidden" >
<h2 className="text-pretty text-xl font-semibold mb-4 sm:mb-8 sm:text-3xl lg:mb-1 lg:max-w-3xl">
Mes associés
</h2>
<div className="relative flex flex-col lg:flex-row items-center justify-center overflow-y-visible gap-6 w-full">
<Avatar className="flex flex-row gap-9 w-20 h-20 m-1">
<AvatarImage className="rounded-full z-50 ld:md:z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="
md:absolute border self-center rounded-xl font-bold
text-center
py-4 px-8 lg:md:px-5 w-fit opacity-100 lg:md:top-20
md:left-[calc(50%-11em)] translate-x-[-50%] lg:left-[calc(50% -4em)]
sm:translate-x-[100%] lg:translate-x-[50%] z-40 lg:md:z-50">
Richard Vagneur
</div>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="flex flex-row translate-x-[-8em] gap-9 w-20 h-20 m-1">
<AvatarImage className="rounded-full z-50 ld:md:z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="
md:absolute border rounded-xl font-bold text-center
py-4 px-8 lg:md:px-5 w-fit opacity-100 lg:md:top-20
md:left-[calc(50%)] translate-x-[-50%] z-40 lg:md:z-50
sm:translate-x-[-50%] lg:translate-x-[-50%] self-center">
Robert Lewis
</div>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="flex flex-row gap-9 w-20 h-20 m-1">
<AvatarImage className="rounded-full z-50 ld:md:z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="
md:absolute border rounded-xl font-bold text-center
py-4 px-8 lg:md:px-5 w-fit opacity-100 lg:md:top-20
md:left-[calc(50%-11em)] translate-x-[-50%] z-40
lg:md:z-50 sm:translate-x-[125%] sm:self-center
lg:translate-x-[-100%]">
Marria Caré
</div>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</div>
</div>
</div>
<div className="w-full lg:w-2/5 sm:self-center sm:w-3/5 h-fit border rounded">
<div className="w-full lg:w-1/2 border rounded">
<img
className="w-full"
className="w-full aspect-square"
src="https://shadcnblocks.com/images/block/placeholder-dark-1.svg"
alt="president profile image"
/>
@@ -129,7 +81,8 @@ export default async function About() {
l'état" possible.
</p>
<p className="mt-1 text-muted-foreground">
equipement (gants, casque) pris en compte. Prévoir une tenue sportive adaptée.
equipement (gants, casque) pris en compte. Prévoir une
tenue sportive adaptée.
</p>
</div>
<div className="mt-12 flex flex-col sm:flex-row px-12 justify-center gap-6 lg:items-center">

View File

@@ -5,6 +5,7 @@ import Gallery from "@/components/gallery";
import Hero from "@/components/hero";
import Testimonial from "@/components/testimonial";
import { CarouselItem } from "@/components/ui/carousel";
import YouTubeEmbed from "@/components/youtube-embed";
import { IYoutube } from "@/interfaces/youtube";
export default async function Home() {
@@ -17,6 +18,15 @@ export default async function Home() {
return (
<main>
<Hero />
<div className="p-12">
<YouTubeEmbed
loadIframe
width="full"
height="full"
video="cbxWxPDXgNM"
autoPlay={false}
/>
</div>
<div className="flex flex-col p-12">
<Features
title="DÉVELOPPEMENT DU SYSTÈME"
@@ -139,11 +149,7 @@ export default async function Home() {
key={video.id.videoId}
className="pl-[20px] md:max-w-[452px]"
>
<iframe
width="424"
height="238"
src={`https://www.youtube.com/embed/${video.id.videoId}`}
></iframe>
<YouTubeEmbed video={video} />
</CarouselItem>
);
})}

View File

@@ -36,7 +36,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="fr">
<html lang="fr" suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>

View File

@@ -21,6 +21,7 @@ import {
import Link from "next/link";
import { deleteCookie, getCookie } from "cookies-next";
import { useEffect, useState } from "react";
import { ThemeSwitcher } from "./theme-switcher";
const subMenuItemsOne = [
{
@@ -142,6 +143,7 @@ const Navbar = () => {
</div>
</div>
<div className="flex gap-2 animate-in ease-in-out">
<ThemeSwitcher />
<Button variant="outline">
{cookie ? (
<Link href="/dashboard">Compte</Link>
@@ -175,6 +177,8 @@ const Navbar = () => {
Latosa-Escrima
</span>
</div>
<div className="flex gap-2">
<ThemeSwitcher />
<Sheet>
<SheetTrigger asChild>
<Button variant="outline" size="icon">
@@ -197,87 +201,30 @@ const Navbar = () => {
</SheetTitle>
</SheetHeader>
<div className="mb-8 mt-8 flex flex-col gap-4">
<Link href="/" className="font-semibold">
<Link
href="/"
className="font-semibold"
>
Accueil
</Link>
<Accordion
type="single"
collapsible
className="w-full"
<Link
href="/planning"
className="font-semibold"
>
<AccordionItem
value="products"
className="border-b-0"
Planning
</Link>
<Link
href="/about"
className="font-semibold"
>
<AccordionTrigger className="mb-4 py-0 font-semibold hover:no-underline">
Products
</AccordionTrigger>
<AccordionContent className="mt-2">
{subMenuItemsOne.map(
(item, idx) => (
<a
key={idx}
className={cn(
"flex select-none gap-4 rounded-md p-3 leading-none outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
)}
href="#"
À propos
</Link>
<Link
href="/blog"
className="font-semibold"
>
{item.icon}
<div>
<div className="text-sm font-semibold">
{item.title}
</div>
<p className="text-sm leading-snug text-muted-foreground">
{
item.description
}
</p>
</div>
</a>
),
)}
</AccordionContent>
</AccordionItem>
<AccordionItem
value="resources"
className="border-b-0"
>
<AccordionTrigger className="py-0 font-semibold hover:no-underline">
Resources
</AccordionTrigger>
<AccordionContent className="mt-2">
{subMenuItemsTwo.map(
(item, idx) => (
<a
key={idx}
className={cn(
"flex select-none gap-4 rounded-md p-3 leading-none outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
)}
href="#"
>
{item.icon}
<div>
<div className="text-sm font-semibold">
{item.title}
</div>
<p className="text-sm leading-snug text-muted-foreground">
{
item.description
}
</p>
</div>
</a>
),
)}
</AccordionContent>
</AccordionItem>
</Accordion>
<a href="#" className="font-semibold">
Pricing
</a>
<a href="#" className="font-semibold">
Blog
</a>
</Link>
</div>
<div className="border-t pt-4">
<div className="grid grid-cols-2 justify-start">
@@ -362,6 +309,7 @@ const Navbar = () => {
</div>
</div>
</div>
</div>
</section>
);
};

View File

@@ -1,6 +1,6 @@
"use client";
import { ApiResponse, request, useApi } from "@/hooks/use-api";
import { ApiResponse, request } from "@/hooks/use-api";
import "@schedule-x/theme-shadcn/dist/index.css";
import { useNextCalendarApp, ScheduleXCalendar } from "@schedule-x/react";
import { createEventsServicePlugin } from "@schedule-x/events-service";
@@ -26,17 +26,24 @@ import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { KeyedMutator } from "swr";
import { getCookie } from "cookies-next";
import { useTheme } from "next-themes";
const Planning: React.FC<{
events: CalendarEventExternal[];
mutate?: KeyedMutator<ApiResponse<CalendarEventExternal[]>>;
}> = ({ events, mutate }) => {
const plugins = [
const { resolvedTheme } = useTheme();
console.log(resolvedTheme);
const isConnected = getCookie("auth_token");
const plugins = isConnected
? [
createEventsServicePlugin(),
createDragAndDropPlugin(),
createResizePlugin(),
createEventRecurrencePlugin(),
];
]
: [];
const [eventSelected, setEventSelected] =
useState<CalendarEventExternal | null>(null);
const [newEvent, setNewEvent] = useState<Omit<
@@ -70,7 +77,7 @@ const Planning: React.FC<{
theme: "shadcn",
views: [createViewDay(), createViewWeek()],
defaultView: "week",
isDark: true,
isDark: resolvedTheme === "dark" ? true : false,
isResponsive: true,
locale: "fr-FR",
dayBoundaries: {
@@ -102,6 +109,10 @@ const Planning: React.FC<{
calendar?.events.getAll();
}, []);
useEffect(() => {
calendar?.setTheme(resolvedTheme === "dark" ? "dark" : "light");
}, [resolvedTheme]);
const AddButton: React.FC = () => (
<Button onClick={() => setNewEvent({})} variant="outline">
Nouveau

View File

@@ -0,0 +1,40 @@
"use client";
import * as React from "react";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
export function ThemeSwitcher() {
const { setTheme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Changer le thème</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
Clair
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Sombre
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
Système
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@@ -0,0 +1,77 @@
"use client";
import { IYoutubeItem } from "@/interfaces/youtube";
import Image from "next/image";
import { useState } from "react";
export default function YouTubeEmbed({
video,
width = 424,
height = 238,
loadIframe = false,
autoPlay = true,
}: {
video: IYoutubeItem | string;
width?: number | "full";
height?: number | "full";
loadIframe?: boolean;
autoPlay?: boolean;
}) {
const [isIframeLoaded, setIframeLoaded] = useState(loadIframe);
const isString = typeof video === "string";
const _loadIframe = () => setIframeLoaded(true);
return (
<div
className={`relative w-${width === "full" ? width : "[" + width + "px]"} h-${height === "full" ? height : "[" + height + "px]"} cursor-pointer`}
onClick={_loadIframe}
>
{isIframeLoaded ? (
<iframe
className="rounded-md shadow-current aspect-video"
width={width === "full" ? "100%" : width}
height={height === "full" ? "100%" : height}
src={`https://www.youtube-nocookie.com/embed/${isString ? video : video.id.videoId}?rel=0&modestbranding=1&autoplay=${autoPlay ? 1 : 0}`}
title={
isString ? "YouTube video player" : video.snippet.title
}
allow="accelerometer; autoplay; encrypted-media; gyroscope"
allowFullScreen
/>
) : (
<>
{width === "full" || height === "full" ? (
<img
width="100%"
height="100%"
className="w-full h-full object-cover rounded-md shadow-current"
src={`https://img.youtube.com/vi/${isString ? video : video.id.videoId}/hqdefault.jpg`}
alt={
isString
? "YouTube video player"
: video.snippet.title
}
/>
) : (
<Image
width={width}
height={height}
className="w-full h-full object-cover rounded-md shadow-current"
src={`https://img.youtube.com/vi/${isString ? video : video.id.videoId}/hqdefault.jpg`}
alt={
isString
? "YouTube video player"
: video.snippet.title
}
/>
)}
<button className="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[50%] text-white text-8xl cursor-pointer">
</button>
</>
)}
</div>
);
}

View File

@@ -10,6 +10,10 @@ const nextConfig: NextConfig = {
output: "standalone",
images: {
remotePatterns: [
{
protocol: "https",
hostname: "img.youtube.com",
},
{
protocol: "https",
hostname: "avatar.vercel.sh",