From c202764966a2d4df52bdc5d89aa3a25e6ae7d7db Mon Sep 17 00:00:00 2001 From: gom-by Date: Mon, 27 Jan 2025 10:15:05 +0100 Subject: [PATCH 1/3] events api file init --- backend/api/delete_event.go | 36 +++++++++++ backend/api/new_event.go | 17 ++--- backend/api/update_event.go | 91 +++++++++++++++++++++++++++ backend/main.go | 23 +++++-- frontend/app/(main)/planning/page.tsx | 61 +++++++++++------- frontend/hooks/events.tsx | 38 +++++++++++ 6 files changed, 226 insertions(+), 40 deletions(-) create mode 100644 backend/api/delete_event.go create mode 100644 backend/api/update_event.go create mode 100644 frontend/hooks/events.tsx diff --git a/backend/api/delete_event.go b/backend/api/delete_event.go new file mode 100644 index 0000000..ae3ded7 --- /dev/null +++ b/backend/api/delete_event.go @@ -0,0 +1,36 @@ + +package api + +import ( + "context" + "log" + "net/http" + + "fr.latosa-escrima/api/core" +) + +func HandleDeleteEvent(w http.ResponseWriter, r *http.Request) { + uuid := r.PathValue("event_uuid") + var event core.Event + res, err := core.DB.NewDelete(). + Model(&event). + Where("id = ?", uuid). + Returning("*"). + Exec(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + log.Println(res) + + // TODO sql request to remove content + + core.JSONSuccess{ + Status: core.Success, + Message: "Event deleted.", + }.Respond(w, http.StatusOK) +} + diff --git a/backend/api/new_event.go b/backend/api/new_event.go index cefd342..dfd0abb 100644 --- a/backend/api/new_event.go +++ b/backend/api/new_event.go @@ -2,22 +2,16 @@ package api import ( "context" - "net/http" - "io" "encoding/json" + "io" + "log" + "net/http" + "time" core "fr.latosa-escrima/api/core" ) func HandleCreateEvent(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - core.JSONError{ - Status: core.Error, - Message: "Method is not allowed", - }.Respond(w, http.StatusMethodNotAllowed) - return - } - body, err := io.ReadAll(r.Body) if err != nil { core.JSONError{ @@ -26,6 +20,7 @@ func HandleCreateEvent(w http.ResponseWriter, r *http.Request) { }.Respond(w, http.StatusNoContent) return } + log.Println(time.Now()) var event core.Event if err = json.Unmarshal(body, &event); err != nil { core.JSONError{ @@ -41,7 +36,7 @@ func HandleCreateEvent(w http.ResponseWriter, r *http.Request) { Status: core.Error, Message: err.Error(), }.Respond(w, http.StatusNotAcceptable) - } + } core.JSONSuccess{ Status: core.Success, diff --git a/backend/api/update_event.go b/backend/api/update_event.go new file mode 100644 index 0000000..6a3e2d6 --- /dev/null +++ b/backend/api/update_event.go @@ -0,0 +1,91 @@ +package api + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "fr.latosa-escrima/api/core" +) + +type UpdateEventArgs struct { + FirstName *string `json:"firstname,omitempty"` + LastName *string `json:"lastname,omitempty"` + Email *string `json:"email,omitempty"` + Password *string `json:"password,omitempty"` + Phone *string `json:"phone,omitempty"` + Role *core.Role `json:"role,omitempty"` +} + +func HandleUpdateEvent(w http.ResponseWriter, r *http.Request) { + // var updateArgs UpdateEventArgs + body, err := io.ReadAll(r.Body) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + fmt.Println("update and event here. Body : ", body) + var event core.Event + err = json.Unmarshal(body, &event) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + // var event core.Event + // updateQuery := core.DB.NewUpdate().Model(&event) + // + // val := reflect.ValueOf(updateArgs) + // typ := reflect.TypeOf(updateArgs) + // + // for i := 0; i < val.NumField(); i++ { + // field := val.Field(i) + // fieldname := typ.Field(i).Name + // + // tag := typ.Field(i).Tag.Get("bun") + // if tag == "" { + // tag = typ.Field(i).Tag.Get("json") + // } + // + // // Only add fields that are non-nil and non-zero + // if field.IsValid() && !field.IsNil() && !field.IsZero() { + // if fieldname == "Password" { + // updateQuery.Set(fmt.Sprintf("%s = crypt(?, gen_salt('bf'))", strings.Split(tag, ",")[0]), field.Interface()) + // } else { + // updateQuery.Set(fmt.Sprintf("%s = ?", strings.Split(tag, ",")[0]), field.Interface()) + // } + // } + // } + // + // // Always update the `updated_at` field + // updateQuery.Set("updated_at = ?", time.Now()) + // + // uuid := r.PathValue("user_uuid") + // _, err = updateQuery. + // Where("user_id = ?", uuid). + // Returning("*"). + // Exec(context.Background()) + // + // if err != nil { + // core.JSONError{ + // Status: core.Error, + // Message: err.Error(), + // }.Respond(w, http.StatusInternalServerError) + // return + // } + // + // user.Password = "" + // + core.JSONSuccess{ + Status: core.Success, + Message: "User updated.", + Data: "ok", + }.Respond(w, http.StatusOK) +} diff --git a/backend/main.go b/backend/main.go index e27b64e..ddea390 100644 --- a/backend/main.go +++ b/backend/main.go @@ -105,11 +105,24 @@ func main() { "/users/{user_uuid}/update": { Handler: api.HandleUpdateUser, Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}}, - // "/users/{user_uuid}/events": {Handler: nil, Middleware: nil}, - // "/users/{user_uuid}/events/{event_uuid}": {Handler: nil, Middleware: nil}, - // "/users/{user_uuid}/events/{event_uuid}/delete": {Handler: nil, Middleware: nil}, - // "/users/{user_uuid}/events/{event_uuid}/update": {Handler: nil, Middleware: nil}, - "/blogs/new": {Handler: api.HandleCreateBlog, Middlewares: nil}, + "/events": { + Handler: api.HandleGetEvent, + Middlewares: []core.Middleware{api.Methods("GET")}}, + "/events/new": { + Handler: api.HandleCreateEvent, + Middlewares: []core.Middleware{api.Methods("POST")}}, + "/events/{event_uuid}": { + Handler: api.HandleGetEvent, + Middlewares: []core.Middleware{api.Methods("GET")}}, + "/events/{event_uuid}/delete": { + Handler: api.HandleDeleteEvent, + Middlewares: []core.Middleware{api.Methods("DELETE")}}, + "/events/{event_uuid}/update": { + Handler: api.HandleUpdateEvent, + Middlewares: []core.Middleware{api.Methods("PATCH")}}, + "/blogs/new": { + Handler: api.HandleCreateBlog, + Middlewares: []core.Middleware{api.Methods(("POST"))}}, "/blogs/{uuid}": { Handler: api.HandleGetBlog, Middlewares: []core.Middleware{api.Methods("GET")}}, diff --git a/frontend/app/(main)/planning/page.tsx b/frontend/app/(main)/planning/page.tsx index 4079025..dbe47c7 100644 --- a/frontend/app/(main)/planning/page.tsx +++ b/frontend/app/(main)/planning/page.tsx @@ -1,5 +1,6 @@ "use client"; +import { 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"; @@ -36,7 +37,7 @@ const Planning = () => { useState(null); const [events, setEvents] = useState([ { - id: "1", + id: "1", // TODO put an uuid there title: "Event 1", start: format(new Date(Date.now()), "yyyy-MM-dd HH:mm"), end: format( @@ -45,6 +46,7 @@ const Planning = () => { ), }, ]); + const calendar = useNextCalendarApp( { theme: "shadcn", @@ -61,7 +63,7 @@ const Planning = () => { callbacks: { onEventClick(event, e) { setEventSelected(event); - }, + } }, }, plugins, @@ -78,7 +80,7 @@ const Planning = () => { { setEventSelected((e) => (open ? e : null)); }} @@ -137,10 +139,9 @@ const Planning = () => { */} { const val = e.currentTarget.value; - console.log(val); setEventSelected((ev) => { if (ev) return { @@ -159,36 +160,48 @@ const Planning = () => { + value={eventSelected?.end || ""} + onChange={(e) => { + const val = e.currentTarget.value setEventSelected((ev) => { if (ev) return { ...ev, - end: e.currentTarget.value, + end: val, }; return ev; }) - } + }} className="col-span-3" /> - - + diff --git a/frontend/hooks/events.tsx b/frontend/hooks/events.tsx new file mode 100644 index 0000000..a8b4eb5 --- /dev/null +++ b/frontend/hooks/events.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { setCookie } from "cookies-next"; +import { useEffect, useState } from "react"; +import { API_URL } from "@/lib/constants"; + +export interface LoginArgs { + email: string; + password: string; +} + +export default function useLogin() { + const { + data, + isLoading: loading, + isSuccess, + } = request( + "/users/login", + undefined, + "POST", + false, + true, + ); + + const login = async (inputs: LoginArgs) => { + try { + const res = await trigger(inputs); + if (!res) throw new Error("The server hasn't responded."); + if (res.status === "Error") throw new Error(res.message); + if (res.data) setCookie("auth_token", res.data); + return res; + } catch (error: any) { + throw new Error(error.message); + } + }; + + return { login, loading, isSuccess }; +} From 18dd8fa1bb12ac07077e4243849fa46d8ddd1586 Mon Sep 17 00:00:00 2001 From: gom-by Date: Mon, 27 Jan 2025 11:37:13 +0100 Subject: [PATCH 2/3] event api v1 --- backend/api/delete_event.go | 3 - backend/api/{event.go => get_event.go} | 17 +---- backend/api/new_event.go | 21 ++---- backend/api/update_event.go | 100 +++++++++---------------- 4 files changed, 45 insertions(+), 96 deletions(-) rename backend/api/{event.go => get_event.go} (59%) diff --git a/backend/api/delete_event.go b/backend/api/delete_event.go index ae3ded7..586eac4 100644 --- a/backend/api/delete_event.go +++ b/backend/api/delete_event.go @@ -1,4 +1,3 @@ - package api import ( @@ -26,8 +25,6 @@ func HandleDeleteEvent(w http.ResponseWriter, r *http.Request) { } log.Println(res) - // TODO sql request to remove content - core.JSONSuccess{ Status: core.Success, Message: "Event deleted.", diff --git a/backend/api/event.go b/backend/api/get_event.go similarity index 59% rename from backend/api/event.go rename to backend/api/get_event.go index 20e3869..dc50e4b 100644 --- a/backend/api/event.go +++ b/backend/api/get_event.go @@ -8,31 +8,20 @@ import ( ) func HandleGetEvent(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - if r.Method != http.MethodGet { - core.JSONError{ - Status: core.Error, - Message: "Method not Allowed", - }.Respond(w, http.StatusMethodNotAllowed) - return - } - - event_uuid := r.PathValue("uuid") - + event_uuid := r.PathValue("event_uuid") var event core.Event _, err := core.DB.NewSelect().Model(&event).Where("uuid = ?", event_uuid).ScanAndCount(context.Background()) if err != nil { core.JSONError{ Status: core.Error, Message: err.Error(), - }.Respond(w, http.StatusNotAcceptable) + }.Respond(w, http.StatusInternalServerError) return } core.JSONSuccess{ Status: core.Success, - Message: "Event Returned", + Message: "Event successfully sent", Data: event, }.Respond(w, http.StatusOK) return diff --git a/backend/api/new_event.go b/backend/api/new_event.go index dfd0abb..6abba18 100644 --- a/backend/api/new_event.go +++ b/backend/api/new_event.go @@ -3,33 +3,22 @@ package api import ( "context" "encoding/json" - "io" - "log" "net/http" - "time" core "fr.latosa-escrima/api/core" ) func HandleCreateEvent(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) + var event core.Event + err := json.NewDecoder(r.Body).Decode(&event) if err != nil { core.JSONError{ Status: core.Error, Message: err.Error(), - }.Respond(w, http.StatusNoContent) + }.Respond(w, http.StatusBadRequest) return } - log.Println(time.Now()) - var event core.Event - if err = json.Unmarshal(body, &event); err != nil { - core.JSONError{ - Status: core.Error, - Message: err.Error(), - }.Respond(w, http.StatusNoContent) - return - } - + _, err = core.DB.NewInsert().Model(&event).Exec(context.Background()) if err != nil { core.JSONError{ @@ -40,7 +29,7 @@ func HandleCreateEvent(w http.ResponseWriter, r *http.Request) { core.JSONSuccess{ Status: core.Success, - Message: "Event inserted", + Message: "Event created", Data: event, }.Respond(w, http.StatusCreated) } diff --git a/backend/api/update_event.go b/backend/api/update_event.go index 6a3e2d6..ed143ae 100644 --- a/backend/api/update_event.go +++ b/backend/api/update_event.go @@ -1,91 +1,65 @@ package api import ( + "context" "encoding/json" - "fmt" - "io" + "log" "net/http" "fr.latosa-escrima/api/core" + "github.com/google/uuid" ) -type UpdateEventArgs struct { - FirstName *string `json:"firstname,omitempty"` - LastName *string `json:"lastname,omitempty"` - Email *string `json:"email,omitempty"` - Password *string `json:"password,omitempty"` - Phone *string `json:"phone,omitempty"` - Role *core.Role `json:"role,omitempty"` -} - func HandleUpdateEvent(w http.ResponseWriter, r *http.Request) { - // var updateArgs UpdateEventArgs - body, err := io.ReadAll(r.Body) + var event core.Event + err := json.NewDecoder(r.Body).Decode(&event) if err != nil { core.JSONError{ Status: core.Error, Message: err.Error(), - }.Respond(w, http.StatusInternalServerError) + }.Respond(w, http.StatusBadRequest) return } - fmt.Println("update and event here. Body : ", body) - var event core.Event - err = json.Unmarshal(body, &event) + + event_uuid := r.PathValue("event_uuid") + event.EventID, err = uuid.FromBytes([]byte(event_uuid)) if err != nil { core.JSONError{ Status: core.Error, Message: err.Error(), + }.Respond(w, http.StatusBadRequest) + return + } + + log.Println(event) + + // val := reflect.ValueOf(event) + // typ := val.Type() + // + // for i := 0; i < val.NumField(); i++ { + // field := val.Field(i) + // fieldType := typ.Field(i) + // fmt.Printf("Field Name: %s, Field Value: %v\n", fieldType.Name, field.Interface()) + // if fiel + // } + + _, err = core.DB.NewUpdate(). + Model(event). + OmitZero(). + WherePK(). + Exec(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: "Event not found.", }.Respond(w, http.StatusInternalServerError) return } - // var event core.Event - // updateQuery := core.DB.NewUpdate().Model(&event) - // - // val := reflect.ValueOf(updateArgs) - // typ := reflect.TypeOf(updateArgs) - // - // for i := 0; i < val.NumField(); i++ { - // field := val.Field(i) - // fieldname := typ.Field(i).Name - // - // tag := typ.Field(i).Tag.Get("bun") - // if tag == "" { - // tag = typ.Field(i).Tag.Get("json") - // } - // - // // Only add fields that are non-nil and non-zero - // if field.IsValid() && !field.IsNil() && !field.IsZero() { - // if fieldname == "Password" { - // updateQuery.Set(fmt.Sprintf("%s = crypt(?, gen_salt('bf'))", strings.Split(tag, ",")[0]), field.Interface()) - // } else { - // updateQuery.Set(fmt.Sprintf("%s = ?", strings.Split(tag, ",")[0]), field.Interface()) - // } - // } - // } - // - // // Always update the `updated_at` field - // updateQuery.Set("updated_at = ?", time.Now()) - // - // uuid := r.PathValue("user_uuid") - // _, err = updateQuery. - // Where("user_id = ?", uuid). - // Returning("*"). - // Exec(context.Background()) - // - // if err != nil { - // core.JSONError{ - // Status: core.Error, - // Message: err.Error(), - // }.Respond(w, http.StatusInternalServerError) - // return - // } - // - // user.Password = "" - // core.JSONSuccess{ Status: core.Success, - Message: "User updated.", - Data: "ok", + Message: "Event updated", + Data: event, }.Respond(w, http.StatusOK) } + From 6ebfbdb9c8cba03805456bc6d3fd14118ef617e3 Mon Sep 17 00:00:00 2001 From: gom-by Date: Mon, 27 Jan 2025 12:57:21 +0100 Subject: [PATCH 3/3] events api in frontend, setting routes for events --- backend/api/get_event.go | 33 +++++-- backend/main.go | 12 +-- frontend/app/(main)/planning/page.tsx | 120 ++++++++++++++++++++------ 3 files changed, 125 insertions(+), 40 deletions(-) diff --git a/backend/api/get_event.go b/backend/api/get_event.go index dc50e4b..56bbc11 100644 --- a/backend/api/get_event.go +++ b/backend/api/get_event.go @@ -2,6 +2,7 @@ package api import ( "context" + "fmt" "net/http" core "fr.latosa-escrima/api/core" @@ -9,20 +10,40 @@ import ( func HandleGetEvent(w http.ResponseWriter, r *http.Request) { event_uuid := r.PathValue("event_uuid") - var event core.Event + var event core.Event _, err := core.DB.NewSelect().Model(&event).Where("uuid = ?", event_uuid).ScanAndCount(context.Background()) if err != nil { core.JSONError{ - Status: core.Error, + Status: core.Error, Message: err.Error(), }.Respond(w, http.StatusInternalServerError) - return + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Event successfully sent", + Data: event, + }.Respond(w, http.StatusOK) + return +} + +func HangleGetEvents(w http.ResponseWriter, r *http.Request) { + var events []core.Event + rowsCount, err := core.DB.NewSelect().Model(&events).ScanAndCount(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return } core.JSONSuccess{ Status: core.Success, - Message: "Event successfully sent", - Data: event, + Message: fmt.Sprintf("%d Event successfully sent", rowsCount), + Data: events, }.Respond(w, http.StatusOK) - return + return } + diff --git a/backend/main.go b/backend/main.go index ddea390..7e03dfe 100644 --- a/backend/main.go +++ b/backend/main.go @@ -106,22 +106,22 @@ func main() { Handler: api.HandleUpdateUser, Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}}, "/events": { - Handler: api.HandleGetEvent, + Handler: api.HangleGetEvents, Middlewares: []core.Middleware{api.Methods("GET")}}, "/events/new": { - Handler: api.HandleCreateEvent, + Handler: api.HandleCreateEvent, Middlewares: []core.Middleware{api.Methods("POST")}}, "/events/{event_uuid}": { - Handler: api.HandleGetEvent, + Handler: api.HandleGetEvent, Middlewares: []core.Middleware{api.Methods("GET")}}, "/events/{event_uuid}/delete": { - Handler: api.HandleDeleteEvent, + Handler: api.HandleDeleteEvent, Middlewares: []core.Middleware{api.Methods("DELETE")}}, "/events/{event_uuid}/update": { - Handler: api.HandleUpdateEvent, + Handler: api.HandleUpdateEvent, Middlewares: []core.Middleware{api.Methods("PATCH")}}, "/blogs/new": { - Handler: api.HandleCreateBlog, + Handler: api.HandleCreateBlog, Middlewares: []core.Middleware{api.Methods(("POST"))}}, "/blogs/{uuid}": { Handler: api.HandleGetBlog, diff --git a/frontend/app/(main)/planning/page.tsx b/frontend/app/(main)/planning/page.tsx index dbe47c7..3584f6a 100644 --- a/frontend/app/(main)/planning/page.tsx +++ b/frontend/app/(main)/planning/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { request } from "@/hooks/use-api"; +import { request, useApi } 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"; @@ -30,6 +30,7 @@ import { import { CalendarIcon } from "lucide-react"; import { Calendar } from "@/components/ui/calendar"; import { cn } from "@/lib/utils"; +import { requestFormReset } from "react-dom"; const Planning = () => { const plugins = [createEventsServicePlugin()]; @@ -47,6 +48,8 @@ const Planning = () => { }, ]); + const [requestCreateEvent, setRequestCreateEvent] = useState(false) + const calendar = useNextCalendarApp( { theme: "shadcn", @@ -63,12 +66,48 @@ const Planning = () => { callbacks: { onEventClick(event, e) { setEventSelected(event); - } + }, + async onClickDateTime(dateTime) { + setRequestCreateEvent(true) + const newEvent: CalendarEventExternal = { + id: "5", + title: "Event 1", + start: dateTime, + end: format( + new Date(dateTime).getTime() + (1000 * 60 * 60), + "yyyy-MM-dd HH:mm", + ), + } + try { + const res = await request( + `/events/new`, + { + method: "POST", + body: JSON.stringify(newEvent), + requiresAuth: true, + csrfToken: false + },) + if (res.status === "Error") { + console.log("Error") + } + if (res.status === "Success") { + console.log("Success") + } + } catch (e) { + console.log(e) + } + }, }, }, plugins, ); - + + const {data: requestedEvents, isLoading, success} = useApi("/events", { + onSuccess: (data) => { + calendar?.events.set(data) + } + }, false, false) + useEffect(() => { // get all events calendar?.events.getAll(); @@ -176,32 +215,57 @@ const Planning = () => { /> - + }} + type="submit"> + Supprimer + +