From 4cf85981ebf8218a7744b47238b41263f3645387 Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Mon, 10 Mar 2025 16:25:12 +0100 Subject: [PATCH] Locations added --- backend/api/events/update.go | 3 - backend/api/locations/delete.go | 38 ++ backend/api/locations/location.go | 56 +++ backend/api/locations/locations.go | 46 +++ backend/api/locations/new.go | 38 ++ backend/api/locations/update.go | 41 ++ backend/api/locations_routes.go | 30 ++ .../migrations/20250307080744_update_event.go | 30 ++ backend/core/models/events.go | 2 + backend/core/models/locations.go | 16 + backend/core/permissions.go | 2 +- backend/core/schemas.go | 3 + backend/main.go | 3 +- .../(auth)/dashboard/locations/_locations.tsx | 89 +++++ .../app/(auth)/dashboard/locations/page.tsx | 21 + frontend/app/(main)/about/page.tsx | 367 +++++++++--------- frontend/components/app-sidebar.tsx | 13 +- frontend/components/event-dialog.tsx | 148 +++++-- .../components/locations/location-card.tsx | 197 ++++++++++ .../components/locations/location-dialog.tsx | 79 ++++ .../components/locations/location-form.tsx | 252 ++++++++++++ frontend/components/photo-viewer.tsx | 2 +- frontend/components/planning.tsx | 12 +- frontend/components/ui/alert.tsx | 62 +++ frontend/hooks/use-debounce.tsx | 31 ++ frontend/interfaces/ICalendarEvent.ts | 10 +- frontend/lib/formatLocation.ts | 8 + frontend/lib/openNavigationMap.ts | 28 ++ frontend/lib/osmEmbed.ts | 11 + frontend/package-lock.json | 51 +++ frontend/package.json | 3 + frontend/types/types.tsx | 39 +- 32 files changed, 1504 insertions(+), 227 deletions(-) create mode 100644 backend/api/locations/delete.go create mode 100644 backend/api/locations/location.go create mode 100644 backend/api/locations/locations.go create mode 100644 backend/api/locations/new.go create mode 100644 backend/api/locations/update.go create mode 100644 backend/api/locations_routes.go create mode 100644 backend/cmd/migrate/migrations/20250307080744_update_event.go create mode 100644 backend/core/models/locations.go create mode 100644 frontend/app/(auth)/dashboard/locations/_locations.tsx create mode 100644 frontend/app/(auth)/dashboard/locations/page.tsx create mode 100644 frontend/components/locations/location-card.tsx create mode 100644 frontend/components/locations/location-dialog.tsx create mode 100644 frontend/components/locations/location-form.tsx create mode 100644 frontend/components/ui/alert.tsx create mode 100644 frontend/hooks/use-debounce.tsx create mode 100644 frontend/lib/formatLocation.ts create mode 100644 frontend/lib/openNavigationMap.ts create mode 100644 frontend/lib/osmEmbed.ts diff --git a/backend/api/events/update.go b/backend/api/events/update.go index cac415f..09166be 100644 --- a/backend/api/events/update.go +++ b/backend/api/events/update.go @@ -3,7 +3,6 @@ package events import ( "context" "encoding/json" - "log" "net/http" "fr.latosa-escrima/core" @@ -32,8 +31,6 @@ func HandleUpdate(w http.ResponseWriter, r *http.Request) { return } - log.Println(event) - _, err = core.DB.NewUpdate(). Model(&event). OmitZero(). diff --git a/backend/api/locations/delete.go b/backend/api/locations/delete.go new file mode 100644 index 0000000..7ab2bde --- /dev/null +++ b/backend/api/locations/delete.go @@ -0,0 +1,38 @@ +package locations + +import ( + "context" + "encoding/json" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleDelete(w http.ResponseWriter, r *http.Request) { + var location models.Location + if err := json.NewDecoder(r.Body).Decode(&location); err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + if _, err := core.DB. + NewDelete(). + Model(&location). + WherePK(). + Exec(context.Background()); err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Location deleted.", + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/locations/location.go b/backend/api/locations/location.go new file mode 100644 index 0000000..2fab849 --- /dev/null +++ b/backend/api/locations/location.go @@ -0,0 +1,56 @@ +package locations + +import ( + "context" + "encoding/json" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleLocation(w http.ResponseWriter, r *http.Request) { + var location models.Location + if err := json.NewDecoder(r.Body).Decode(&location); err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + err := core.DB. + NewSelect(). + Model(&location). + Where("street = ? AND city = ? AND postal_code = ?", location.Street, location.City, location.PostalCode). + Scan(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + var events []models.Event + err = core.DB. + NewSelect(). + Model(&events). + Where("location = ? || ', ' || ? || ', ' || ?", location.Street, location.City, location.PostalCode). + Scan(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + location.Events = &events + + core.JSONSuccess{ + Status: core.Success, + Message: "Location retrieved.", + Data: location, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/locations/locations.go b/backend/api/locations/locations.go new file mode 100644 index 0000000..9e2b88b --- /dev/null +++ b/backend/api/locations/locations.go @@ -0,0 +1,46 @@ +package locations + +import ( + "context" + "fmt" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" + "fr.latosa-escrima/utils" +) + +func HandleLocations(w http.ResponseWriter, r *http.Request) { + var locations []*models.Location + if err := core.DB. + NewSelect(). + Model(&locations). + Scan(context.Background()); err != nil { + fmt.Println("Error") + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + locations = utils.Map(locations, func(l *models.Location) *models.Location { + var events []models.Event + err := core.DB. + NewSelect(). + Model(&events). + Where("location = ? || ', ' || ? || ', ' || ?", l.Street, l.City, l.PostalCode). + Scan(context.Background()) + if err != nil { + return nil + } + l.Events = &events + return l + }) + + core.JSONSuccess{ + Status: core.Success, + Message: "Locations retrieved.", + Data: locations, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/locations/new.go b/backend/api/locations/new.go new file mode 100644 index 0000000..adebc5e --- /dev/null +++ b/backend/api/locations/new.go @@ -0,0 +1,38 @@ +package locations + +import ( + "context" + "encoding/json" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleNew(w http.ResponseWriter, r *http.Request) { + var location models.Location + if err := json.NewDecoder(r.Body).Decode(&location); err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + if _, err := core.DB. + NewInsert(). + Model(&location). + Ignore(). + Exec(context.Background()); err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Location created.", + }.Respond(w, http.StatusCreated) +} diff --git a/backend/api/locations/update.go b/backend/api/locations/update.go new file mode 100644 index 0000000..3e1c453 --- /dev/null +++ b/backend/api/locations/update.go @@ -0,0 +1,41 @@ +package locations + +import ( + "context" + "encoding/json" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleUpdate(w http.ResponseWriter, r *http.Request) { + var location models.Location + if err := json.NewDecoder(r.Body).Decode(&location); err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + _, err := core.DB. + NewUpdate(). + Model(&location). + WherePK(). + OmitZero(). + Exec(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Location updated", + Data: location, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/locations_routes.go b/backend/api/locations_routes.go new file mode 100644 index 0000000..70d7f77 --- /dev/null +++ b/backend/api/locations_routes.go @@ -0,0 +1,30 @@ +package api + +import ( + "fr.latosa-escrima/api/locations" + "fr.latosa-escrima/core" +) + +var LocationsRoutes = map[string]core.Handler{ + "GET /locations/all": { + Handler: locations.HandleLocations, + Middlewares: []core.Middleware{Methods("GET")}, + }, + "/locations/new": { + Handler: locations.HandleNew, + Middlewares: []core.Middleware{Methods(("POST")), + HasPermissions("locations", "insert"), AuthJWT}}, + "GET /locations": { + Handler: locations.HandleLocation, + Middlewares: []core.Middleware{Methods("GET")}}, + "DELETE /locations": { + Handler: locations.HandleDelete, + Middlewares: []core.Middleware{Methods("DELETE"), + HasPermissions("locations", "delete"), AuthJWT}, + }, + "PATCH /locations": { + Handler: locations.HandleUpdate, + Middlewares: []core.Middleware{Methods("PATCH"), + HasPermissions("blogs", "update"), AuthJWT}, + }, +} diff --git a/backend/cmd/migrate/migrations/20250307080744_update_event.go b/backend/cmd/migrate/migrations/20250307080744_update_event.go new file mode 100644 index 0000000..21bc486 --- /dev/null +++ b/backend/cmd/migrate/migrations/20250307080744_update_event.go @@ -0,0 +1,30 @@ +package migrations + +import ( + "context" + "fmt" + + "fr.latosa-escrima/core/models" + "github.com/uptrace/bun" +) + +func init() { + Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { + fmt.Print(" [up migration] ") + _, err := db. + NewAddColumn(). + Model((*models.Event)(nil)). + ColumnExpr("description TEXT"). + Exec(ctx) + _, err = db. + NewAddColumn(). + Model((*models.Event)(nil)). + ColumnExpr("location TEXT"). + Exec(ctx) + + return err + }, func(ctx context.Context, db *bun.DB) error { + fmt.Print(" [down migration] ") + return nil + }) +} diff --git a/backend/core/models/events.go b/backend/core/models/events.go index ad3054b..ef2a61b 100644 --- a/backend/core/models/events.go +++ b/backend/core/models/events.go @@ -17,5 +17,7 @@ type Event struct { ScheduleEnd time.Time `bun:"schedule_end,notnull" json:"end"` FullDay bool `bun:"full_day,notnull,default:false" json:"fullDay"` IsVisible bool `bun:"is_visible,notnull,default:true" json:"isVisible"` + Description *string `bun:"description" json:"description,omitempty"` + Location *string `bun:"location" json:"location,omitempty"` Rrule string `bun:"rrule" json:"rrule"` } diff --git a/backend/core/models/locations.go b/backend/core/models/locations.go new file mode 100644 index 0000000..56de418 --- /dev/null +++ b/backend/core/models/locations.go @@ -0,0 +1,16 @@ +package models + +import "github.com/uptrace/bun" + +type Location struct { + bun.BaseModel `bun:"table:locations"` + + ID int `bun:"id,pk,autoincrement" json:"id"` + Street string `bun:"street,notnull,unique:location" json:"street"` + City string `bun:"city,notnull,unique:location" json:"city"` + PostalCode string `bun:"postal_code,notnull,unique:location" json:"postalCode"` + Latitude *float64 `bun:"latitude" json:"latitude,omitempty"` + Longitude *float64 `bun:"longitude" json:"longitude,omitempty"` + + Events *[]Event `bun:"-" json:"events,omitempty"` +} diff --git a/backend/core/permissions.go b/backend/core/permissions.go index e291fa0..04d0b70 100644 --- a/backend/core/permissions.go +++ b/backend/core/permissions.go @@ -10,7 +10,7 @@ import ( type Permissions []models.Permission func GetAllPermissions() Permissions { - resources := []string{"users", "roles", "media", "events", "permissions", "shortcodes", "blogs"} + resources := []string{"users", "roles", "media", "events", "permissions", "shortcodes", "blogs", "locations"} var perms Permissions for _, resource := range resources { perms = append(perms, Permissions{ diff --git a/backend/core/schemas.go b/backend/core/schemas.go index d94f7de..81d0378 100644 --- a/backend/core/schemas.go +++ b/backend/core/schemas.go @@ -72,6 +72,9 @@ func InitDatabase(dsn DSN) (*bun.DB, error) { _, err = db.NewCreateTable(). Model((*m.UserToRole)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable(). + Model((*m.Location)(nil)).IfNotExists().Exec(ctx) + if err != nil { return nil, err } diff --git a/backend/main.go b/backend/main.go index eafbf8d..dfda05a 100644 --- a/backend/main.go +++ b/backend/main.go @@ -81,7 +81,8 @@ func main() { api.MediaRoutes, api.PermissionsRoutes, api.RolesRoutes, - api.ShortcodesRoutes) + api.ShortcodesRoutes, + api.LocationsRoutes) core.HandleRoutes(mux, routes) fmt.Printf("Serving on port %s\n", port) diff --git a/frontend/app/(auth)/dashboard/locations/_locations.tsx b/frontend/app/(auth)/dashboard/locations/_locations.tsx new file mode 100644 index 0000000..6990be5 --- /dev/null +++ b/frontend/app/(auth)/dashboard/locations/_locations.tsx @@ -0,0 +1,89 @@ +"use client"; + +import LocationDialog from "@/components/locations/location-dialog"; +import IUser from "@/interfaces/IUser"; +import hasPermissions from "@/lib/hasPermissions"; +import { useState } from "react"; +import { Location } from "@/types/types"; +import request from "@/lib/request"; +import { useApi } from "@/hooks/use-api"; +import { LocationCard } from "@/components/locations/location-card"; + +export default function LocationsPage({ user }: { user: IUser }) { + const { locations: locationsPerm } = hasPermissions(user.roles, { + locations: ["update", "insert", "delete"], + } as const); + + const locations = useApi("/locations/all"); + + const onUpdate = async (l: Location) => { + try { + const res = await request("/locations", { + method: "PATCH", + body: l, + requiresAuth: true, + }); + if (res.status === "Success") { + locations.mutate(); + } else { + } + } catch (e) { + console.error(e); + } + }; + + const onDelete = async (l: Location) => { + try { + const res = await request("/locations", { + method: "DELETE", + body: l, + requiresAuth: true, + }); + + if (res.status === "Success") locations.mutate(); + else { + } + } catch (e) { + console.error(e); + } + }; + + return ( +
+ {locationsPerm.insert && ( +
+ { + try { + const res = await request("/locations/new", { + body: l, + method: "POST", + requiresAuth: true, + csrfToken: false, + }); + if (res.status === "Success") { + locations.mutate(); + } else { + } + } catch (e) {} + }} + /> +
+ )} +
+ {locations.data?.map((l) => { + return ( + + ); + })} +
+
+ ); +} diff --git a/frontend/app/(auth)/dashboard/locations/page.tsx b/frontend/app/(auth)/dashboard/locations/page.tsx new file mode 100644 index 0000000..1a62ada --- /dev/null +++ b/frontend/app/(auth)/dashboard/locations/page.tsx @@ -0,0 +1,21 @@ +"use server"; +import getMe from "@/lib/getMe"; +import hasPermissions from "@/lib/hasPermissions"; +import { redirect } from "next/navigation"; +import LocationsPage from "./_locations"; + +export default async function Page() { + const me = await getMe(); + if ( + !me || + me.status === "Error" || + !me.data || + !hasPermissions(me.data.roles, { + locations: ["get"], + } as const).all + ) { + redirect("/dashboard"); + } + + return ; +} diff --git a/frontend/app/(main)/about/page.tsx b/frontend/app/(main)/about/page.tsx index cf711e7..50634a1 100644 --- a/frontend/app/(main)/about/page.tsx +++ b/frontend/app/(main)/about/page.tsx @@ -1,6 +1,10 @@ export const dynamic = "force-dynamic"; // Prevents static rendering +import { LocationCard } from "@/components/locations/location-card"; import { Badge } from "@/components/ui/badge"; + +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Info } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, @@ -12,197 +16,202 @@ import { } from "@/components/ui/card"; import { SITE_NAME } from "@/lib/constants"; import getShortcode from "@/lib/getShortcode"; +import request from "@/lib/request"; +import { Location } from "@/types/types"; import { CheckIcon } from "lucide-react"; -export default async function About() { - const make_contact_div = ( - - - - ); +// Define props interface for PricingCard +interface PricingCardProps { + title: string; + price: number; + description: string; + features: string[]; + isPopular?: boolean; + ctaText?: string; + ctaLink?: string; +} +const UnderConstructionBanner = () => { + return ( + + +
+ Attention + + Cette page est encore en cours de construction et les + informations listées peuvent ne pas être exactes pour le + moment. + +
+
+ ); +}; + +// Reusable Pricing Card Component +const PricingCard: React.FC = ({ + title, + price, + description, + features, + isPopular = false, + ctaText = "Prendre contact", + ctaLink = "/contact", +}) => ( + + + {isPopular && ( + + Le plus populaire + + )} + {title} + {price}€ + + + {description} + + +
    + {features.map((feature, index) => ( +
  • + + {feature} +
  • + ))} +
+
+ + + + + +
+); + +export default async function About() { const profileImage = await getShortcode("profile_image"); + const locations = await request("/locations/all", { + requiresAuth: false, + }); return ( - <> -
-
- {/* Text Section - Takes 2/3 on large screens */} -
- - - - Nicolas GORUK - - - Président de l'association française de{" "} - {SITE_NAME} - - - -
-

- Lorem ipsum, dolor sit amet -

-

- 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? -

-

- Lorem ipsum, dolor sit amet -

-

- 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? -

-
-
-
-
- - {/* Image Section - Takes 1/3 on large screens */} -
- president profile image -
+
+ {/* Hero Section */} +
+ +
+
+ {/* Text Section - Takes 2/3 on large screens */} +
+ + + + Nicolas GORUK + + + Président de l'association française de{" "} + {SITE_NAME} + + + +
+

+ Notre mission +

+

+ Chez {SITE_NAME}, nous nous engageons à + promouvoir l'excellence. Nous offrons un + environnement dynamique pour tous nos + membres, avec des événements réguliers et + des opportunités uniques. +

+

+ Notre histoire +

+

+ Fondée en [année], notre association a + grandi pour devenir un acteur clé dans + [domaine]. Nous avons organisé [nombre] + événements et touché plus de [nombre] + personnes grâce à nos initiatives. +

+
+
+
-
-

+ + {/* Image Section - Takes 1/3 on large screens */} +
+ Portrait de Nicolas GORUK, président de l'association +
+

+ + {/* Locations Section */} + {locations.data && locations.data.length > 0 && ( +
+

+ Retrouvez-nous +

+
+ {locations.data.map((l: Location) => ( + + ))} +
+
+ )} + + {/* Pricing Section */} +
+
+

Tarifs

-

- License accessible à partir de 90€. Aide "une aide de - l'état" possible. +

+ Adhésion à partir de [prix]€.

-

- equipement (gants, casque) pris en compte. Prévoir une - tenue sportive adaptée. +

+ Équipement (gants, casque) fourni. Prévoir une tenue + sportive adaptée.

-
- - - - Most popular - - Startup - £39 - - - All the basics for starting a new business - - -
    -
  • - - - 2 user - -
  • -
  • - - - Plan features - -
  • -
  • - - - Product support - -
  • -
-
- - - - - -
- - - Team - £89 - - - Everything you need for a growing business - - -
    -
  • - - - 5 user - -
  • -
  • - - - Plan features - -
  • -
  • - - - Product support - -
  • -
-
- {make_contact_div} -
- - - Enterprise - 149 - - - Advanced features for scaling your business - - -
    -
  • - - - 10 user - -
  • -
  • - - - Plan features - -
  • -
  • - - - Product support - -
  • -
-
- {make_contact_div} -
+
+ +
-
- +
+
); } diff --git a/frontend/components/app-sidebar.tsx b/frontend/components/app-sidebar.tsx index 84747f3..f198f42 100644 --- a/frontend/components/app-sidebar.tsx +++ b/frontend/components/app-sidebar.tsx @@ -13,10 +13,10 @@ import { Loader2, Camera, UserRoundCog, + MapPin, } from "lucide-react"; import { NavMain } from "@/components/nav-main"; -import { NavProjects } from "@/components/nav-projects"; import { NavUser } from "@/components/nav-user"; import { TeamSwitcher } from "@/components/team-switcher"; import { @@ -55,6 +55,17 @@ const data = { }, ], }, + { + title: "Adresses", + url: "/dashboard/locations", + icon: MapPin, + items: [ + { + title: "Listes des adresses", + url: "/dashboard/locations", + }, + ], + }, { title: "Planning", icon: Calendar, diff --git a/frontend/components/event-dialog.tsx b/frontend/components/event-dialog.tsx index fb10634..7af9762 100644 --- a/frontend/components/event-dialog.tsx +++ b/frontend/components/event-dialog.tsx @@ -32,6 +32,20 @@ import { } from "@/components/ui/select"; import { useForm } from "react-hook-form"; import { useEffect } from "react"; +import ICalendarEvent from "@/interfaces/ICalendarEvent"; +import { + Command, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, +} from "@/components/ui/command"; +import { Textarea } from "@/components/ui/textarea"; +import { useApi } from "@/hooks/use-api"; +import { Location } from "@/types/types"; +import openNavigationApp from "@/lib/openNavigationMap"; +import formatLocation from "@/lib/formatLocation"; export const eventFormSchema = z.object({ title: z.string().min(1, "Titre requis"), @@ -43,6 +57,8 @@ export const eventFormSchema = z.object({ frequency: z.enum(["unique", "quotidien", "hebdomadaire", "mensuel"]), frequencyEndDate: z.date().optional(), isVisible: z.boolean().default(true), + description: z.string().optional(), + location: z.string().optional(), // Store as a formatted string }); export type EventFormValues = z.infer; @@ -55,13 +71,15 @@ const frequencies = [ ]; export const EventForm: React.FC<{ - event: any; + event: ICalendarEvent | Omit; setForm: React.Dispatch< React.SetStateAction< ReturnType> | undefined > >; }> = ({ event, setForm }) => { + const locations = useApi("/locations/all"); + const _start = new Date(event.start ?? Date.now()); const _end = new Date(event.end ?? Date.now()); @@ -76,6 +94,8 @@ export const EventForm: React.FC<{ fullDay: event.fullday ?? false, frequency: "unique", isVisible: event.isVisible ?? true, + location: event.location, + description: event.description, }, }); @@ -106,7 +126,7 @@ export const EventForm: React.FC<{ />
- {/* Simplified startDate without FormField */} + {/* Start Date */} Début @@ -135,25 +155,16 @@ export const EventForm: React.FC<{ align="start" >
- {/* Force interactivity */} { - console.log( - "Start date selected:", - date, - ); if (date) { form.setValue( "startDate", date, { shouldValidate: true }, ); - console.log( - "Updated startDate:", - form.getValues("startDate"), - ); } }} initialFocus @@ -183,7 +194,7 @@ export const EventForm: React.FC<{ Until - {/* Simplified endDate */} + {/* End Date */} Fin @@ -216,18 +227,10 @@ export const EventForm: React.FC<{ mode="single" selected={form.getValues("endDate")} onSelect={(date) => { - console.log( - "End date selected:", - date, - ); if (date) { form.setValue("endDate", date, { shouldValidate: true, }); - console.log( - "Updated endDate:", - form.getValues("endDate"), - ); } }} initialFocus @@ -286,7 +289,7 @@ export const EventForm: React.FC<{ > - + @@ -343,10 +346,6 @@ export const EventForm: React.FC<{ "frequencyEndDate", )} onSelect={(date) => { - console.log( - "Frequency end date selected:", - date, - ); if (date) { form.setValue( "frequencyEndDate", @@ -356,12 +355,6 @@ export const EventForm: React.FC<{ true, }, ); - console.log( - "Updated frequencyEndDate:", - form.getValues( - "frequencyEndDate", - ), - ); } }} initialFocus @@ -380,7 +373,7 @@ export const EventForm: React.FC<{ render={({ field }) => ( - Evènement visible ? + Évènement visible ? )} /> + + {/* Updated Location Field with Command */} + ( + + Lieu + + + + field.onChange(value) + } + /> + + {locations.isLoading && ( + + Chargement... + + )} + {!locations.isLoading && + !locations.data?.length && ( + + Aucun lieu trouvé. + + )} + {!locations.isLoading && + locations.data?.length && ( + + {locations.data + .filter((location) => + formatLocation( + location, + ) + .toLowerCase() + .includes( + ( + field.value || + "" + ).toLowerCase(), + ), + ) + .map((location) => ( + { + const formatted = + formatLocation( + location, + ); + field.onChange( + formatted, + ); + }} + > + {formatLocation( + location, + )} + + ))} + + )} + + + + + + )} + /> + + ( + + Description + +