Added CSRF & YouTube and dark mode

This commit is contained in:
cdricms
2025-01-22 17:39:03 +01:00
parent 48e761667f
commit 5a5846d853
29 changed files with 1186 additions and 280 deletions

View File

@@ -9,21 +9,19 @@ export default async function HistoryDetails({
params: Promise<{ blog_id: string }>;
}) {
const { blog_id } = await params;
let blog = {}
let blog = {};
try {
const res = await fetch('http://localhost:3001/blogs/' + blog_id, {method: "GET"})
blog = await res.json()
console.log(blog as Blog)
} catch(e) {
const res = await fetch("http://localhost:3001/blogs/" + blog_id, {
method: "GET",
});
blog = await res.json();
console.log(blog as Blog);
} catch (e) {
console.log(e);
}
if(blog == null) {
return(
<>
Error
</>
)
if (blog == null) {
return <>Error</>;
}
const blog_item_params: BlogItemParams = {

View File

@@ -4,8 +4,16 @@ import Features, { FeatureItem } from "@/components/features";
import Gallery from "@/components/gallery";
import Hero from "@/components/hero";
import Testimonial from "@/components/testimonial";
import { CarouselItem } from "@/components/ui/carousel";
import { IYoutube } from "@/interfaces/youtube";
export default async function Home() {
let videos: IYoutube | null = null;
if (process.env.YOUTUBE_API_KEY) {
const query = `https://www.googleapis.com/youtube/v3/search?key=${process.env.YOUTUBE_API_KEY}&channelId=UCzuFLl5I0WxSMqbeMaiq_FQ&part=snippet,id&order=date&maxResults=50`;
const res = await fetch(query);
videos = await res.json();
}
return (
<main>
<Hero />
@@ -20,10 +28,10 @@ export default async function Home() {
position="left"
image="https://shadcnblocks.com/images/block/placeholder-2.svg"
>
<ol className="list-decimal text-justify flex flex-col gap-4">
<ol className="flex list-decimal flex-col gap-4 text-justify">
<li>
Un Système Centré sur les Concepts{" "}
<ul className="list-disc list-inside">
<ul className="list-inside list-disc">
<li>
Étude et application des meilleurs
concepts et stratégies issus de
@@ -37,7 +45,7 @@ export default async function Home() {
</li>
<li>
Éducation au Mouvement et à lEfficacité
<ul className="list-disc list-inside">
<ul className="list-inside list-disc">
<li>
Plus quun enchaînement de techniques :
une véritable éducation aux mouvements
@@ -56,12 +64,12 @@ export default async function Home() {
position="right"
image="https://shadcnblocks.com/images/block/placeholder-2.svg"
>
<ol className="list-none text-justify flex flex-col gap-4">
<ol className="flex list-none flex-col gap-4 text-justify">
<li>
<span className="font-bold">
Les Premières Étapes
</span>
<ul className="list-disc list-inside">
<ul className="list-inside list-disc">
<li>
Initialement centré sur les techniques
et mouvements, le système sest montré
@@ -78,10 +86,10 @@ export default async function Home() {
<span className="font-bold">
La Découverte des Concepts Clés
</span>{" "}
<ul className="list-disc list-inside">
<ul className="list-inside list-disc">
<li>
Rôle central des concepts de combat :
<ul className="list-disc list-inside pl-4">
<ul className="list-inside list-disc pl-4">
<li>Puissance dans les frappes.</li>
<li>Blocage ferme.</li>
<li>Équilibre et attitude.</li>
@@ -103,7 +111,7 @@ export default async function Home() {
>
Latosa Escrima Concepts repose sur cinq concepts
fondamentaux :
<ul className="list-disc list-inside">
<ul className="list-inside list-disc">
<li>Équilibre</li>
<li>Vitesse (Timing et Distance)</li>
<li>Puissance</li>
@@ -112,8 +120,35 @@ export default async function Home() {
</ul>
</FeatureItem>
</Features>
<Gallery />
<Gallery />
<Gallery
tagLine="Tag Line"
cta="Book a demo"
ctaHref="#"
title="Gallery"
/>
{videos && (
<Gallery
tagLine=""
cta="Accéder à la chaîne"
ctaHref="https://youtube.com/@WingTsunPicardie"
title="Vidéos YouTube"
>
{videos.items.map((video) => {
return (
<CarouselItem
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>
</CarouselItem>
);
})}
</Gallery>
)}
<Testimonial />
</div>
</main>

View File

@@ -0,0 +1,199 @@
"use client";
import "@schedule-x/theme-shadcn/dist/index.css";
import { useNextCalendarApp, ScheduleXCalendar } from "@schedule-x/react";
import { createEventsServicePlugin } from "@schedule-x/events-service";
import {
CalendarEventExternal,
createViewDay,
createViewWeek,
} from "@schedule-x/calendar";
import { useEffect, useState } from "react";
import { format } from "date-fns";
import { Dialog } from "@radix-ui/react-dialog";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { CalendarIcon } from "lucide-react";
import { Calendar } from "@/components/ui/calendar";
import { cn } from "@/lib/utils";
const Planning = () => {
const plugins = [createEventsServicePlugin()];
const [eventSelected, setEventSelected] =
useState<CalendarEventExternal | null>(null);
const [events, setEvents] = useState<CalendarEventExternal[]>([
{
id: "1",
title: "Event 1",
start: format(new Date(Date.now()), "yyyy-MM-dd HH:mm"),
end: format(
new Date(Date.now() + 1 * 3600 * 1000),
"yyyy-MM-dd HH:mm",
),
},
]);
const calendar = useNextCalendarApp(
{
theme: "shadcn",
views: [createViewDay(), createViewWeek()],
defaultView: "week",
isDark: true,
isResponsive: true,
locale: "fr-FR",
dayBoundaries: {
start: "06:00",
end: "00:00",
},
events,
callbacks: {
onEventClick(event, e) {
setEventSelected(event);
},
},
},
plugins,
);
useEffect(() => {
// get all events
calendar?.events.getAll();
}, []);
return (
<div>
<div className="m-8">
<ScheduleXCalendar calendarApp={calendar} />
</div>
<Dialog
open={eventSelected !== null}
onOpenChange={(open) => {
setEventSelected((e) => (open ? e : null));
}}
>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{eventSelected?.title}</DialogTitle>
<DialogDescription>
{eventSelected?.description}
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="start" className="text-right">
Début
</Label>
{/*<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!eventSelected?.start &&
"text-muted-foreground",
)}
>
{eventSelected?.start ? (
format(eventSelected?.start, "PPP")
) : (
<span>Choisissez une date.</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"
align="start"
>
<Calendar
mode="single"
selected={
new Date(
eventSelected?.start ??
Date.now(),
)
}
// onSelect={field.onChange}
disabled={(date) =>
date > new Date() ||
date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover> */}
<Input
id="start"
value={eventSelected?.start}
onChange={(e) => {
const val = e.currentTarget.value;
console.log(val);
setEventSelected((ev) => {
if (ev)
return {
...ev,
start: val,
};
return ev;
});
}}
className="col-span-3"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="end" className="text-right">
Fin
</Label>
<Input
id="end"
value={eventSelected?.end}
onChange={(e) =>
setEventSelected((ev) => {
if (ev)
return {
...ev,
end: e.currentTarget.value,
};
return ev;
})
}
className="col-span-3"
/>
</div>
</div>
<DialogFooter>
<Button
onClick={() => {
setEvents((evs) => {
evs = evs.filter(
(e) => e.id !== eventSelected?.id,
);
evs.push(eventSelected!);
return evs;
});
}}
type="submit"
>
Mettre à jour
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
};
export default Planning;