Gallery
Events still not working
This commit is contained in:
@@ -16,6 +16,8 @@ export default function ShortcodesPage() {
|
|||||||
success,
|
success,
|
||||||
} = useApi<IShortcode[]>("/shortcodes", undefined, true);
|
} = useApi<IShortcode[]>("/shortcodes", undefined, true);
|
||||||
|
|
||||||
|
console.log(shortcodes);
|
||||||
|
|
||||||
const handleUpdate = async (updatedShortcode: IShortcode) => {
|
const handleUpdate = async (updatedShortcode: IShortcode) => {
|
||||||
const res = await request<IShortcode>(
|
const res = await request<IShortcode>(
|
||||||
`/shortcodes/${updatedShortcode.code}/update`,
|
`/shortcodes/${updatedShortcode.code}/update`,
|
||||||
|
|||||||
106
frontend/app/(main)/gallery/page.tsx
Normal file
106
frontend/app/(main)/gallery/page.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import {
|
||||||
|
Pagination,
|
||||||
|
PaginationContent,
|
||||||
|
PaginationItem,
|
||||||
|
PaginationLink,
|
||||||
|
PaginationNext,
|
||||||
|
PaginationPrevious,
|
||||||
|
} from "@/components/ui/pagination";
|
||||||
|
import useMedia from "@/hooks/use-media";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
|
||||||
|
export default function PhotoGallery() {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
error: mediaError,
|
||||||
|
isLoading,
|
||||||
|
success,
|
||||||
|
setPage,
|
||||||
|
setLimit,
|
||||||
|
mutate,
|
||||||
|
} = useMedia();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="flex justify-between items-center mb-8">
|
||||||
|
<h1 className="text-3xl font-bold">Gallerie Photo</h1>
|
||||||
|
</div>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="flex w-full h-full justify-center">
|
||||||
|
<Loader2 className="animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||||
|
{data?.items.map((photo) => (
|
||||||
|
<div
|
||||||
|
key={photo.id}
|
||||||
|
className="aspect-square overflow-hidden rounded-lg shadow-md cursor-pointer"
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={photo.url || "/placeholder.svg"}
|
||||||
|
alt={photo.alt}
|
||||||
|
width={300}
|
||||||
|
height={300}
|
||||||
|
unoptimized
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
)
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Pagination className="mt-8">
|
||||||
|
<PaginationContent>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationPrevious
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setPage((prev) => Math.max(prev - 1, 1));
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
data?.page === 1
|
||||||
|
? "pointer-events-none opacity-50"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
{[...Array(data?.totalPages)].map((_, i) => (
|
||||||
|
<PaginationItem key={i + 1}>
|
||||||
|
<PaginationLink
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setPage(i + 1);
|
||||||
|
}}
|
||||||
|
isActive={data?.page === i + 1}
|
||||||
|
>
|
||||||
|
{i + 1}
|
||||||
|
</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
))}
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationNext
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setPage((prev) =>
|
||||||
|
Math.min(prev + 1, data?.totalPages ?? 1),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
data?.page === data?.totalPages
|
||||||
|
? "pointer-events-none opacity-50"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
</PaginationContent>
|
||||||
|
</Pagination>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ export const dynamic = "force-dynamic"; // Prevents static rendering
|
|||||||
import Features, { FeatureItem } from "@/components/features";
|
import Features, { FeatureItem } from "@/components/features";
|
||||||
import Gallery from "@/components/gallery";
|
import Gallery from "@/components/gallery";
|
||||||
import Hero from "@/components/hero";
|
import Hero from "@/components/hero";
|
||||||
|
import HomepageGalleryItems from "@/components/homepage-gallery";
|
||||||
import Testimonial from "@/components/testimonial";
|
import Testimonial from "@/components/testimonial";
|
||||||
import { CarouselItem } from "@/components/ui/carousel";
|
import { CarouselItem } from "@/components/ui/carousel";
|
||||||
import YouTubeEmbed from "@/components/youtube-embed";
|
import YouTubeEmbed from "@/components/youtube-embed";
|
||||||
@@ -149,11 +150,13 @@ export default async function Home() {
|
|||||||
</FeatureItem>
|
</FeatureItem>
|
||||||
</Features>
|
</Features>
|
||||||
<Gallery
|
<Gallery
|
||||||
tagLine="Tag Line"
|
tagLine=""
|
||||||
cta="Book a demo"
|
cta="Voir toutes les photos"
|
||||||
ctaHref="#"
|
ctaHref="/gallery"
|
||||||
title="Gallery"
|
title="Gallerie"
|
||||||
/>
|
>
|
||||||
|
<HomepageGalleryItems />
|
||||||
|
</Gallery>
|
||||||
{videos && (
|
{videos && (
|
||||||
<Gallery
|
<Gallery
|
||||||
tagLine=""
|
tagLine=""
|
||||||
|
|||||||
@@ -1,23 +1,44 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import { CalendarIcon } from "lucide-react"
|
import { CalendarIcon } from "lucide-react";
|
||||||
import { format } from "date-fns"
|
import { format } from "date-fns";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import * as z from "zod"
|
import * as z from "zod";
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar"
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
|
|
||||||
import { Input } from "@/components/ui/input"
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
|
||||||
import { useForm } from "react-hook-form"
|
|
||||||
import {
|
import {
|
||||||
CalendarEventExternal,
|
Form,
|
||||||
} from "@schedule-x/calendar";
|
FormControl,
|
||||||
import ICalendarEvent from "@/interfaces/ICalendarEvent"
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import {
|
||||||
|
SubmitErrorHandler,
|
||||||
|
SubmitHandler,
|
||||||
|
useForm,
|
||||||
|
UseFormReturn,
|
||||||
|
} from "react-hook-form";
|
||||||
|
import { CalendarEventExternal } from "@schedule-x/calendar";
|
||||||
|
import ICalendarEvent from "@/interfaces/ICalendarEvent";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export const eventFormSchema = z.object({
|
export const eventFormSchema = z.object({
|
||||||
title: z.string().min(1, "Titre requis"),
|
title: z.string().min(1, "Titre requis"),
|
||||||
@@ -33,59 +54,93 @@ export const eventFormSchema = z.object({
|
|||||||
frequency: z.enum(["unique", "quotidien", "hebdomadaire", "mensuel"]),
|
frequency: z.enum(["unique", "quotidien", "hebdomadaire", "mensuel"]),
|
||||||
frequencyEndDate: z.date().optional(),
|
frequencyEndDate: z.date().optional(),
|
||||||
isVisible: z.boolean().default(true),
|
isVisible: z.boolean().default(true),
|
||||||
})
|
});
|
||||||
|
|
||||||
export type EventFormValues = z.infer<typeof eventFormSchema>
|
export type EventFormValues = z.infer<typeof eventFormSchema>;
|
||||||
|
|
||||||
const frequencies = [
|
const frequencies = [
|
||||||
{ label: "Unique", value: "unique" },
|
{ label: "Unique", value: "unique" },
|
||||||
{ label: "Quotidien", value: "quotidien" },
|
{ label: "Quotidien", value: "quotidien" },
|
||||||
{ label: "Hebdomadaire", value: "hebdomadaire" },
|
{ label: "Hebdomadaire", value: "hebdomadaire" },
|
||||||
{ label: "Mensuel", value: "mensuel" },
|
{ label: "Mensuel", value: "mensuel" },
|
||||||
]
|
];
|
||||||
|
|
||||||
const isCalendarEventExternal = (event: CalendarEventExternal | Omit<CalendarEventExternal, "id">): event is CalendarEventExternal => {
|
const isCalendarEventExternal = (
|
||||||
|
event: CalendarEventExternal | Omit<CalendarEventExternal, "id">,
|
||||||
|
): event is CalendarEventExternal => {
|
||||||
return (event as CalendarEventExternal).id !== undefined;
|
return (event as CalendarEventExternal).id !== undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EventForm: React.FC<
|
export const EventForm: React.FC<{
|
||||||
{
|
|
||||||
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
||||||
onSubmitEvent: (eventFormValues: EventFormValues) => void;
|
onSubmit: (data: EventFormValues) => any;
|
||||||
}
|
}> = ({ event, onSubmit }) => {
|
||||||
> = ({
|
const _start = new Date(event.start ?? Date.now());
|
||||||
event,
|
const _end = new Date(event.end ?? Date.now());
|
||||||
onSubmitEvent,
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const form = useForm<EventFormValues>({
|
const form = useForm<EventFormValues>({
|
||||||
resolver: zodResolver(eventFormSchema),
|
resolver: zodResolver(eventFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
title: event.title ? event.title : "",
|
title: event.title ? event.title : "",
|
||||||
startDate: new Date(), // event.start),
|
startDate: _start, // event.start),
|
||||||
startTime: `${new Date(event.start).getHours()}:${new Date(event.start).getMinutes()}`,
|
startTime: format(_start, "HH:mm"),
|
||||||
endDate: new Date(), // event.end),
|
endDate: _end, // event.end),
|
||||||
endTime: `${new Date(event.end).getHours()}:${new Date(event.end).getMinutes()}`,
|
endTime: format(_end, "HH:mm"),
|
||||||
fullDay: event.fullday,
|
fullDay: event.fullday,
|
||||||
frequency: "unique",
|
frequency: "unique",
|
||||||
isVisible: event.isVisible,
|
isVisible: event.isVisible,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const frequency = form.watch("frequency")
|
const frequency = form.watch("frequency");
|
||||||
|
|
||||||
async function onSubmit(data: EventFormValues) {
|
// async function onSubmit(data: EventFormValues) {
|
||||||
try {
|
// try {
|
||||||
const validatedData = eventFormSchema.parse(data)
|
// const validatedData = eventFormSchema.parse(data);
|
||||||
onSubmitEvent(validatedData)
|
// onSubmitEvent(validatedData);
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error("On submit error : ", error)
|
// console.error("On submit error : ", error);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// try {
|
||||||
|
// const validatedData = eventFormSchema.parse(form);
|
||||||
|
// setEvent((old) => {
|
||||||
|
// const rrule = mapFrequencyToRrule(
|
||||||
|
// validatedData.frequency,
|
||||||
|
// validatedData.frequencyEndDate,
|
||||||
|
// );
|
||||||
|
// const [sHours, sMinutes] = validatedData.startTime.split(":");
|
||||||
|
// validatedData.startDate.setHours(
|
||||||
|
// parseInt(sHours),
|
||||||
|
// parseInt(sMinutes),
|
||||||
|
// );
|
||||||
|
// const [eHours, eMinutes] = validatedData.endTime.split(":");
|
||||||
|
// validatedData.endDate.setHours(
|
||||||
|
// parseInt(eHours),
|
||||||
|
// parseInt(eMinutes),
|
||||||
|
// );
|
||||||
|
// return {
|
||||||
|
// ...old,
|
||||||
|
// start: validatedData.startDate.toISOString(),
|
||||||
|
// end: validatedData.endDate.toISOString(),
|
||||||
|
// title: `${validatedData.title}`,
|
||||||
|
// fullDay: validatedData.fullDay,
|
||||||
|
// rrule: rrule,
|
||||||
|
// isVisible: validatedData.isVisible,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// } catch (e) {
|
||||||
|
// console.log(e);
|
||||||
|
// }
|
||||||
|
// }, [form]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full max-w-md space-y-4">
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="w-full max-w-md space-y-4"
|
||||||
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="title"
|
name="title"
|
||||||
@@ -93,7 +148,10 @@ export const EventForm: React.FC<
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Titre</FormLabel>
|
<FormLabel>Titre</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="Ajouter un titre" {...field} />
|
<Input
|
||||||
|
placeholder="Ajouter un titre"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -112,15 +170,36 @@ export const EventForm: React.FC<
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground")}
|
className={cn(
|
||||||
|
"w-full pl-3 text-left font-normal",
|
||||||
|
!field.value &&
|
||||||
|
"text-muted-foreground",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{field.value ? format(field.value, "yyyy-mm-dd hh:mm") : <span>Choisis une date</span>}
|
{field.value ? (
|
||||||
|
format(
|
||||||
|
field.value,
|
||||||
|
"dd/MM/yyyy",
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
Choisis une date
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
<PopoverContent
|
||||||
<Calendar mode="single" selected={field.value} onSelect={field.onChange} initialFocus />
|
className="w-auto p-0"
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={field.onChange}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -134,7 +213,11 @@ export const EventForm: React.FC<
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="time" {...field} className="w-[120px]" />
|
<Input
|
||||||
|
type="time"
|
||||||
|
{...field}
|
||||||
|
className="w-[120px]"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -154,15 +237,36 @@ export const EventForm: React.FC<
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground")}
|
className={cn(
|
||||||
|
"w-full pl-3 text-left font-normal",
|
||||||
|
!field.value &&
|
||||||
|
"text-muted-foreground",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{field.value ? format(field.value, "yyyy-mm-dd hh:mm") : <span>Choisis une date</span>}
|
{field.value ? (
|
||||||
|
format(
|
||||||
|
field.value,
|
||||||
|
"dd/MM/yyyy",
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
Choisis une date
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
<PopoverContent
|
||||||
<Calendar mode="single" selected={field.value} onSelect={field.onChange} initialFocus />
|
className="w-auto p-0"
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={field.onChange}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -176,7 +280,11 @@ export const EventForm: React.FC<
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="time" {...field} className="w-[120px]" />
|
<Input
|
||||||
|
type="time"
|
||||||
|
{...field}
|
||||||
|
className="w-[120px]"
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@@ -190,7 +298,10 @@ export const EventForm: React.FC<
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox checked={field.value} onCheckedChange={field.onChange} />
|
<Checkbox
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormLabel>Journée complète</FormLabel>
|
<FormLabel>Journée complète</FormLabel>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -205,7 +316,10 @@ export const EventForm: React.FC<
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex-1">
|
<FormItem className="flex-1">
|
||||||
<FormLabel>Fréquence</FormLabel>
|
<FormLabel>Fréquence</FormLabel>
|
||||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Selectionner Fréquence" />
|
<SelectValue placeholder="Selectionner Fréquence" />
|
||||||
@@ -213,7 +327,10 @@ export const EventForm: React.FC<
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{frequencies.map((frequency) => (
|
{frequencies.map((frequency) => (
|
||||||
<SelectItem key={frequency.value} value={frequency.value}>
|
<SelectItem
|
||||||
|
key={frequency.value}
|
||||||
|
value={frequency.value}
|
||||||
|
>
|
||||||
{frequency.label}
|
{frequency.label}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -236,15 +353,36 @@ export const EventForm: React.FC<
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn("w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground")}
|
className={cn(
|
||||||
|
"w-full pl-3 text-left font-normal",
|
||||||
|
!field.value &&
|
||||||
|
"text-muted-foreground",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{field.value ? format(field.value, "MM/dd/yyyy") : <span>Choisis une date</span>}
|
{field.value ? (
|
||||||
|
format(
|
||||||
|
field.value,
|
||||||
|
"dd/MM/yyyy",
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
Choisis une date
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
<PopoverContent
|
||||||
<Calendar mode="single" selected={field.value} onSelect={field.onChange} initialFocus />
|
className="w-auto p-0"
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={field.value}
|
||||||
|
onSelect={field.onChange}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -259,9 +397,12 @@ export const EventForm: React.FC<
|
|||||||
name="isVisible"
|
name="isVisible"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex-1">
|
<FormItem className="flex-1">
|
||||||
<FormLabel className="align-sub">Evènement visible ?</FormLabel>
|
<FormLabel className="align-sub">
|
||||||
|
Evènement visible ?
|
||||||
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox className="m-3 align-top justify-center"
|
<Checkbox
|
||||||
|
className="m-3 align-top justify-center"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
@@ -270,17 +411,7 @@ export const EventForm: React.FC<
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end space-x-2">
|
|
||||||
<Button variant="outline" type="button">
|
|
||||||
Abandonner
|
|
||||||
</Button>
|
|
||||||
<Button type="submit" className="bg-[#6B4EFF] hover:bg-[#5B3FEF]">
|
|
||||||
Sauvegarder
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
43
frontend/components/homepage-gallery.tsx
Normal file
43
frontend/components/homepage-gallery.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import useMedia from "@/hooks/use-media";
|
||||||
|
import { CarouselItem } from "./ui/carousel";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
|
||||||
|
export default function HomepageGalleryItems() {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
mutate,
|
||||||
|
setPage,
|
||||||
|
success,
|
||||||
|
setLimit,
|
||||||
|
isLoading,
|
||||||
|
isValidating,
|
||||||
|
} = useMedia(20);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center w-full h-full">
|
||||||
|
<Loader2 className="animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data?.items.map((i) => (
|
||||||
|
<CarouselItem className="pl-[20px] md:max-w-[452px]">
|
||||||
|
<div className="w-full aspect-square" key={i.id}>
|
||||||
|
<img
|
||||||
|
src={i.url}
|
||||||
|
alt={i.alt}
|
||||||
|
className="inset-0 border rounded-sm w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -128,6 +128,18 @@ const Navbar = () => {
|
|||||||
>
|
>
|
||||||
A propos
|
A propos
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
className={cn(
|
||||||
|
"text-muted-foreground",
|
||||||
|
navigationMenuTriggerStyle,
|
||||||
|
buttonVariants({
|
||||||
|
variant: "ghost",
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
href="/gallery"
|
||||||
|
>
|
||||||
|
Gallerie
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-muted-foreground",
|
"text-muted-foreground",
|
||||||
@@ -144,13 +156,33 @@ const Navbar = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 animate-in ease-in-out">
|
<div className="flex gap-2 animate-in ease-in-out">
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
<Button variant="outline">
|
|
||||||
{cookie ? (
|
{cookie ? (
|
||||||
<Link href="/dashboard">Compte</Link>
|
<Link
|
||||||
) : (
|
className={cn(
|
||||||
<Link href="/login">Se connecter</Link>
|
"text-muted-foreground",
|
||||||
|
navigationMenuTriggerStyle,
|
||||||
|
buttonVariants({
|
||||||
|
variant: "outline",
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
href="/dashboard"
|
||||||
|
>
|
||||||
|
Compte
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
className={cn(
|
||||||
|
"text-muted-foreground",
|
||||||
|
navigationMenuTriggerStyle,
|
||||||
|
buttonVariants({
|
||||||
|
variant: "outline",
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
href="/login"
|
||||||
|
>
|
||||||
|
Se connecter
|
||||||
|
</Link>
|
||||||
)}
|
)}
|
||||||
</Button>
|
|
||||||
{cookie ? (
|
{cookie ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -219,6 +251,12 @@ const Navbar = () => {
|
|||||||
>
|
>
|
||||||
À propos
|
À propos
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/gallery"
|
||||||
|
className="font-semibold"
|
||||||
|
>
|
||||||
|
Gallerie
|
||||||
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/blog"
|
href="/blog"
|
||||||
className="font-semibold"
|
className="font-semibold"
|
||||||
|
|||||||
@@ -23,41 +23,10 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { KeyedMutator } from "swr";
|
import { KeyedMutator } from "swr";
|
||||||
import { getCookie } from "cookies-next";
|
import { getCookie } from "cookies-next";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { EventForm, EventFormValues } from "./event-dialog";
|
import { EventForm, eventFormSchema, EventFormValues } from "./event-dialog";
|
||||||
import ICalendarEvent from "@/interfaces/ICalendarEvent";
|
import ICalendarEvent from "@/interfaces/ICalendarEvent";
|
||||||
|
import { UseFormReturn } from "react-hook-form";
|
||||||
const mapFrequencyToRrule = (
|
import mapFrequencyToRrule from "@/lib/mapFrequencyToRrule";
|
||||||
frequency: "unique" | "quotidien" | "hebdomadaire" | "mensuel",
|
|
||||||
frequencyEndDate?: Date,
|
|
||||||
): string => {
|
|
||||||
let rrule = "";
|
|
||||||
|
|
||||||
switch (frequency) {
|
|
||||||
case "quotidien":
|
|
||||||
rrule = "FREQ=DAILY";
|
|
||||||
break;
|
|
||||||
case "hebdomadaire":
|
|
||||||
rrule = "FREQ=WEEKLY";
|
|
||||||
break;
|
|
||||||
case "mensuel":
|
|
||||||
rrule = "FREQ=MONTHLY";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frequencyEndDate) {
|
|
||||||
const until = frequencyEndDate.getTime();
|
|
||||||
const untilDate = new Date(until);
|
|
||||||
const epochDateString = untilDate
|
|
||||||
.toISOString()
|
|
||||||
.replace(/[-:]/g, "")
|
|
||||||
.split(".")[0]; // Format as YYYYMMDDTHHmmss
|
|
||||||
rrule += `;UNTIL=${epochDateString}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rrule;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Planning: React.FC<{
|
const Planning: React.FC<{
|
||||||
events: ICalendarEvent[];
|
events: ICalendarEvent[];
|
||||||
@@ -81,12 +50,14 @@ const Planning: React.FC<{
|
|||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleEventUpdate = async (eventSelected: ICalendarEvent) => {
|
const handleEventUpdate = async (
|
||||||
const event: ICalendarEvent = {
|
eventSelected: ICalendarEvent | Omit<ICalendarEvent, "id">,
|
||||||
|
) => {
|
||||||
|
const event = {
|
||||||
...eventSelected,
|
...eventSelected,
|
||||||
start: `${new Date(eventSelected.start).toISOString()}`,
|
start: `${new Date(eventSelected.start).toISOString()}`,
|
||||||
end: `${new Date(eventSelected.end).toISOString()}`,
|
end: `${new Date(eventSelected.end).toISOString()}`,
|
||||||
};
|
} as ICalendarEvent;
|
||||||
try {
|
try {
|
||||||
const res = await request<undefined>(`/events/${event.id}/update`, {
|
const res = await request<undefined>(`/events/${event.id}/update`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
@@ -157,31 +128,38 @@ const Planning: React.FC<{
|
|||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
setNewEvent((e) => (open ? e : null));
|
setNewEvent((e) => (open ? e : null));
|
||||||
}}
|
}}
|
||||||
event={newEvent}
|
onAdd={async (formValues) => {
|
||||||
onSubmitEvent={async (eventFormValues) => {
|
|
||||||
const rrule = mapFrequencyToRrule(
|
const rrule = mapFrequencyToRrule(
|
||||||
eventFormValues.frequency,
|
formValues.frequency,
|
||||||
eventFormValues.frequencyEndDate,
|
formValues.frequencyEndDate,
|
||||||
|
);
|
||||||
|
const [sHours, sMinutes] =
|
||||||
|
formValues.startTime.split(":");
|
||||||
|
formValues.startDate.setHours(
|
||||||
|
parseInt(sHours),
|
||||||
|
parseInt(sMinutes),
|
||||||
|
);
|
||||||
|
const [eHours, eMinutes] =
|
||||||
|
formValues.endTime.split(":");
|
||||||
|
formValues.endDate.setHours(
|
||||||
|
parseInt(eHours),
|
||||||
|
parseInt(eMinutes),
|
||||||
);
|
);
|
||||||
try {
|
|
||||||
const event: Omit<ICalendarEvent, "id"> = {
|
const event: Omit<ICalendarEvent, "id"> = {
|
||||||
...newEvent,
|
...newEvent,
|
||||||
start: `${eventFormValues.startDate} ${eventFormValues.startTime}`,
|
start: formValues.startDate.toISOString(),
|
||||||
end: `${eventFormValues.endDate} ${eventFormValues.endTime}`,
|
end: formValues.endDate.toISOString(),
|
||||||
title: `${eventFormValues.title}`,
|
title: `${formValues.title}`,
|
||||||
fullDay: eventFormValues.fullDay,
|
fullday: formValues.fullDay,
|
||||||
rrule: rrule,
|
rrule: rrule,
|
||||||
isVisible: eventFormValues.isVisible,
|
isVisible: formValues.isVisible,
|
||||||
};
|
};
|
||||||
const res = await request<undefined>(
|
const res = await request<undefined>(`/events/new`, {
|
||||||
`/events/new`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: event,
|
body: event,
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
csrfToken: false,
|
csrfToken: false,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
if (res.status === "Error") {
|
if (res.status === "Error") {
|
||||||
console.log("Error");
|
console.log("Error");
|
||||||
}
|
}
|
||||||
@@ -189,10 +167,8 @@ const Planning: React.FC<{
|
|||||||
mutate?.();
|
mutate?.();
|
||||||
console.log("Success");
|
console.log("Success");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
|
event={newEvent}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{eventSelected && (
|
{eventSelected && (
|
||||||
@@ -202,17 +178,13 @@ const Planning: React.FC<{
|
|||||||
setEventSelected((e) => (open ? e : null));
|
setEventSelected((e) => (open ? e : null));
|
||||||
}}
|
}}
|
||||||
event={eventSelected}
|
event={eventSelected}
|
||||||
onSubmitEvent={(eventForm) => {
|
onDelete={async (id) => {
|
||||||
console.log("Event form: " + eventForm);
|
calendar?.events?.remove(id);
|
||||||
}}
|
|
||||||
onDelete={async () => {
|
|
||||||
calendar?.events?.remove(eventSelected.id);
|
|
||||||
try {
|
try {
|
||||||
const res = await request<undefined>(
|
const res = await request<undefined>(
|
||||||
`/events/${eventSelected.id}/delete`,
|
`/events/${id}/delete`,
|
||||||
{
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
body: eventSelected,
|
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
csrfToken: false,
|
csrfToken: false,
|
||||||
},
|
},
|
||||||
@@ -228,8 +200,33 @@ const Planning: React.FC<{
|
|||||||
}
|
}
|
||||||
setEventSelected(null);
|
setEventSelected(null);
|
||||||
}}
|
}}
|
||||||
onUpdate={async () => {
|
onUpdate={async (formValues) => {
|
||||||
await handleEventUpdate(eventSelected);
|
const rrule = mapFrequencyToRrule(
|
||||||
|
formValues.frequency,
|
||||||
|
formValues.frequencyEndDate,
|
||||||
|
);
|
||||||
|
const [sHours, sMinutes] =
|
||||||
|
formValues.startTime.split(":");
|
||||||
|
formValues.startDate.setHours(
|
||||||
|
parseInt(sHours),
|
||||||
|
parseInt(sMinutes),
|
||||||
|
);
|
||||||
|
const [eHours, eMinutes] =
|
||||||
|
formValues.endTime.split(":");
|
||||||
|
formValues.endDate.setHours(
|
||||||
|
parseInt(eHours),
|
||||||
|
parseInt(eMinutes),
|
||||||
|
);
|
||||||
|
const event: ICalendarEvent = {
|
||||||
|
...eventSelected,
|
||||||
|
start: formValues.startDate.toISOString(),
|
||||||
|
end: formValues.endDate.toISOString(),
|
||||||
|
title: `${formValues.title}`,
|
||||||
|
fullday: formValues.fullDay,
|
||||||
|
rrule: rrule,
|
||||||
|
isVisible: formValues.isVisible,
|
||||||
|
};
|
||||||
|
await handleEventUpdate(event);
|
||||||
setEventSelected(null);
|
setEventSelected(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -240,21 +237,27 @@ const Planning: React.FC<{
|
|||||||
|
|
||||||
const EventDialog: React.FC<
|
const EventDialog: React.FC<
|
||||||
{
|
{
|
||||||
onSubmitEvent: (eventFormValues: EventFormValues) => void;
|
onDelete?: (id: string) => void;
|
||||||
onDelete?: () => void;
|
onUpdate?: (formValues: EventFormValues) => void;
|
||||||
onUpdate?: () => void;
|
onAdd?: (formValues: EventFormValues) => void;
|
||||||
onAdd?: () => void;
|
|
||||||
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
event: ICalendarEvent | Omit<ICalendarEvent, "id">;
|
||||||
} & DialogProps
|
} & DialogProps
|
||||||
> = ({
|
> = ({ open, onOpenChange, onDelete, onUpdate, onAdd, event }) => {
|
||||||
open,
|
const [form, setForm] = useState<UseFormReturn<EventFormValues>>();
|
||||||
onOpenChange,
|
|
||||||
onSubmitEvent,
|
const handleOnAdd = (data: EventFormValues) => {
|
||||||
onDelete,
|
onAdd?.(data);
|
||||||
onUpdate,
|
};
|
||||||
onAdd,
|
|
||||||
event,
|
const onSubmit = (data: EventFormValues) => {
|
||||||
}) => {
|
try {
|
||||||
|
const validatedData = eventFormSchema.parse(data);
|
||||||
|
handleOnAdd(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
@@ -262,12 +265,24 @@ const EventDialog: React.FC<
|
|||||||
<DialogTitle>{event.title}</DialogTitle>
|
<DialogTitle>{event.title}</DialogTitle>
|
||||||
<DialogDescription>{event.description}</DialogDescription>
|
<DialogDescription>{event.description}</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<EventForm event={event} onSubmitEvent={onSubmitEvent} />
|
<EventForm event={event} onSubmit={onSubmit} />
|
||||||
<DialogFooter className="flex flex-row justify-end">
|
<DialogFooter className="flex flex-row justify-end">
|
||||||
{onUpdate && (
|
{onUpdate && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => onUpdate()}
|
onClick={() => {
|
||||||
|
form?.handleSubmit((data) => {
|
||||||
|
console.log(data);
|
||||||
|
try {
|
||||||
|
const validatedData =
|
||||||
|
eventFormSchema.parse(data);
|
||||||
|
console.log(validatedData);
|
||||||
|
onUpdate(validatedData);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Actualiser
|
Actualiser
|
||||||
@@ -276,14 +291,17 @@ const EventDialog: React.FC<
|
|||||||
{onDelete && (
|
{onDelete && (
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => onDelete()}
|
onClick={() => onDelete(event.id)}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Supprimer
|
Supprimer
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{onAdd && !onUpdate && !onDelete && (
|
{onAdd && !onUpdate && !onDelete && (
|
||||||
<Button onClick={() => onAdd()} type="submit">
|
<Button
|
||||||
|
onClick={() => form?.handleSubmit((data) => {})}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
Créer
|
Créer
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export default function useMedia(_limit: number = 20) {
|
|||||||
const [limit, setLimit] = useState(_limit);
|
const [limit, setLimit] = useState(_limit);
|
||||||
const res = useApi<IPaginatedResponse<Media>>(
|
const res = useApi<IPaginatedResponse<Media>>(
|
||||||
`/media?page=${page}&limit=${limit}`,
|
`/media?page=${page}&limit=${limit}`,
|
||||||
|
{},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
...res,
|
...res,
|
||||||
|
|||||||
51
frontend/lib/mapFrequencyToRrule.ts
Normal file
51
frontend/lib/mapFrequencyToRrule.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// const mapRruleToFrequency = (rrule: string) => {
|
||||||
|
//
|
||||||
|
// switch (frequency) {
|
||||||
|
// case "quotidien":
|
||||||
|
// rrule = "FREQ=DAILY";
|
||||||
|
// break;
|
||||||
|
// case "hebdomadaire":
|
||||||
|
// rrule = "FREQ=WEEKLY";
|
||||||
|
// break;
|
||||||
|
// case "mensuel":
|
||||||
|
// rrule = "FREQ=MONTHLY";
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// return "";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
const mapFrequencyToRrule = (
|
||||||
|
frequency: "unique" | "quotidien" | "hebdomadaire" | "mensuel",
|
||||||
|
frequencyEndDate?: Date,
|
||||||
|
): string => {
|
||||||
|
let rrule = "";
|
||||||
|
|
||||||
|
switch (frequency) {
|
||||||
|
case "quotidien":
|
||||||
|
rrule = "FREQ=DAILY";
|
||||||
|
break;
|
||||||
|
case "hebdomadaire":
|
||||||
|
rrule = "FREQ=WEEKLY";
|
||||||
|
break;
|
||||||
|
case "mensuel":
|
||||||
|
rrule = "FREQ=MONTHLY";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frequencyEndDate) {
|
||||||
|
const until = frequencyEndDate.getTime();
|
||||||
|
const untilDate = new Date(until);
|
||||||
|
const epochDateString = untilDate
|
||||||
|
.toISOString()
|
||||||
|
.replace(/[-:]/g, "")
|
||||||
|
.split(".")[0]; // Format as YYYYMMDDTHHmmss
|
||||||
|
rrule += `;UNTIL=${epochDateString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rrule;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default mapFrequencyToRrule;
|
||||||
@@ -12,6 +12,7 @@ export default async function request<T>(
|
|||||||
cookies?: () => Promise<ReadonlyRequestCookies>;
|
cookies?: () => Promise<ReadonlyRequestCookies>;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<ApiResponse<T>> {
|
): Promise<ApiResponse<T>> {
|
||||||
|
console.log(API_URL, endpoint);
|
||||||
const { method = "GET", body, requiresAuth = true } = options;
|
const { method = "GET", body, requiresAuth = true } = options;
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|||||||
Reference in New Issue
Block a user