From 85a9b2d81857121f937304907bbbaab141f2993b Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Tue, 10 Jun 2025 21:28:17 -0400 Subject: [PATCH 1/3] happy monday _ bleh _ --- frontend/src/pages/loads/Add.vue | 93 +++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/loads/Add.vue b/frontend/src/pages/loads/Add.vue index abc0a8d..d30b9fa 100644 --- a/frontend/src/pages/loads/Add.vue +++ b/frontend/src/pages/loads/Add.vue @@ -67,7 +67,7 @@ COL
- + COL Required @@ -75,15 +75,20 @@
- Picture Required +
+ picture +
@@ -161,6 +166,12 @@ const fileSelected = (e: FileUploadSelectEvent) => { file.value = e.files[0] } +const pictureUrl = ref(null) +const calcUrl = () => { + const cache = new Date().getMilliseconds() + pictureUrl.value = import.meta.env.VITE_API + `/load/${route.params.id}/photo?cache=${cache}` +} + const load = ref({ bullet: '', cartridge: '', @@ -243,7 +254,7 @@ const addCartridgeName = async () => { const add = async () => { v$.value.$touch() - if (v$.value.$invalid || !file.value) { + if (v$.value.$invalid || (!route.params.id && !file.value)) { toast.add({ severity: 'error', summary: 'Error', @@ -254,6 +265,12 @@ const add = async () => { return } + let id = "" + + if (route.params.id) { + id = "/" + route.params.id.toString() + } + const formData = new FormData() formData.append('bullet_id', load.value.bullet) formData.append('cartridge_id', load.value.cartridge) @@ -261,22 +278,43 @@ const add = async () => { formData.append('powder_gr', load.value.powderGrs.toString()) formData.append('primer_id', load.value.primer) formData.append('col', load.value.col) - formData.append('photo', file.value) + if (file.value) { + formData.append('photo', file.value) + } try { - const response = await axios.post>(`${import.meta.env.VITE_API}/load`, formData, { + const response = await axios.post>(`${import.meta.env.VITE_API}/load${id}`, formData, { headers: { 'Content-Type': 'multipart/form-data', }, }) + const message = route.params.id ? 'Load updated' : 'Load added' + toast.add({ severity: 'success', summary: 'Success', - detail: 'Load added', + detail: message, life: 3000, }) + if (route.params.id) { + load.value = { + bullet: '', + cartridge: '', + powder: '', + powderGrs: 0, + primer: '', + col: '', + } + file.value = null + + v$.value.$reset() + await fetchLoad() + + return + } + await router.push(`/loads/edit/${response.data.payload}`) } catch (error) { toast.add({ @@ -287,6 +325,31 @@ const add = async () => { }) } } +const deleteLoad = async () => { + if (!route.params.id) { + return + } + + try { + await axios.delete(`${import.meta.env.VITE_API}/load/${route.params.id}`) + + toast.add({ + severity: 'success', + summary: 'Success', + detail: 'Load deleted', + life: 3000, + }) + + await router.push('/loads/search') + } catch (error) { + toast.add({ + severity: 'error', + summary: 'Error', + detail: 'Error deleting load', + life: 3000, + }) + } +} const fetchLoad = async () => { if (!route.params.id) { @@ -302,6 +365,18 @@ const fetchLoad = async () => { load.value.powder = response.data.payload.powder.id load.value.powderGrs = response.data.payload.powder_gr load.value.primer = response.data.payload.primer.id + load.value.col = response.data.payload.col.toString() + + if (!load.value.col.includes('.')) { + load.value.col = `${load.value.col}.000` + } else { + const parts = load.value.col.split('.') + if (parts[1].length < 3) { + load.value.col = `${parts[0]}.${parts[1].padEnd(3, '0')}` + } + } + + calcUrl() } else { toast.add({ severity: 'error', @@ -324,7 +399,9 @@ onMounted(() => { fetchCartridges() if (route.params.id) { - fetchLoad() + setTimeout(() => { + fetchLoad() + }, 100) } }) -- 2.49.1 From cd874f174f4485d06c4a8cf83fd697cf277e4de9 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Tue, 10 Jun 2025 21:28:56 -0400 Subject: [PATCH 2/3] A full commitment's what I'm thinking of --- backend/handlers/file.go | 6 -- backend/handlers/loads/handler.go | 103 ++++++++++++++++++++++++++++-- backend/main.go | 3 + backend/models/loads.sql | 17 +++++ backend/models/loads/loads.sql.go | 53 +++++++++++++++ 5 files changed, 172 insertions(+), 10 deletions(-) diff --git a/backend/handlers/file.go b/backend/handlers/file.go index 96f3ace..9891cc4 100644 --- a/backend/handlers/file.go +++ b/backend/handlers/file.go @@ -2,18 +2,12 @@ package handlers import ( "github.com/labstack/echo/v4" - "net/http" ) func ReadFile(c echo.Context, formName string) ([]byte, error) { file, err := c.FormFile(formName) if err != nil { c.Logger().Error(err) - _ = c.JSON(http.StatusBadRequest, struct { - Message string `json:"message"` - }{ - Message: "No file provided", - }) return nil, err } diff --git a/backend/handlers/loads/handler.go b/backend/handlers/loads/handler.go index ea3f26d..e2a2b34 100644 --- a/backend/handlers/loads/handler.go +++ b/backend/handlers/loads/handler.go @@ -2,7 +2,6 @@ package loads import ( "context" - "fmt" "git.siteworxpro.com/reloading-manager/backend/.gen/loading/public/table" "git.siteworxpro.com/reloading-manager/backend/database" "git.siteworxpro.com/reloading-manager/backend/handlers" @@ -13,6 +12,7 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" "github.com/labstack/echo/v4" + "mime" "net/http" "strings" ) @@ -65,11 +65,29 @@ type ResultChan[T any] struct { } func Post(c echo.Context) error { + var exists *loads.GetLoadByIdRow + db := c.(*database.CustomContext).Db defer func() { _ = db.Db.Close(context.Background()) }() + id := c.Param("id") + + if id != "" { + uuid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "id is not a valid UUID") + } + + gbi, err := db.Loads.GetLoadById(context.Background(), *uuid) + if err != nil || gbi.ID.Valid == false { + return handlers.NotFound(c, "load not found") + } + + exists = &gbi + } + cartridgeID, err := handlers.ParseUuid(c.FormValue("cartridge_id")) if err != nil { return handlers.BadRequest(c, "cartridge_id is not a valid UUID") @@ -91,8 +109,11 @@ func Post(c echo.Context) error { } file, err := handlers.ReadFile(c, "photo") - if err != nil { + if err != nil && exists == nil { return handlers.BadRequest(c, "photo is not valid") + } else if err != nil { + // If we are updating an existing load, we can ignore the error + file = exists.Photo } meta := c.FormValue("meta") @@ -119,6 +140,26 @@ func Post(c echo.Context) error { return handlers.BadRequest(c, "col is not valid") } + if exists != nil { + err = db.Loads.UpdateLoad(context.Background(), loads.UpdateLoadParams{ + ID: exists.ID, + CartridgeID: *cartridgeID, + Col: colFl, + PowderID: *powderId, + PowderGr: powderGrFl, + PrimerID: *primerId, + BulletID: *bulletId, + Photo: file, + Meta: []byte(meta), + }) + + if err != nil { + return err + } + + return c.JSON(http.StatusOK, handlers.Response[string]{Payload: exists.ID.String()}) + } + uid, err := db.Loads.CreateLoad(context.Background(), loads.CreateLoadParams{ CartridgeID: *cartridgeID, Col: colFl, @@ -137,6 +178,33 @@ func Post(c echo.Context) error { return c.JSON(http.StatusCreated, handlers.Response[string]{Payload: uid.String()}) } +func Photo(c echo.Context) error { + id := c.Param("id") + if id == "" { + return handlers.BadRequest(c, "id is required") + } + + uuid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "id is not a valid UUID") + } + + db := c.(*database.CustomContext).Db + defer func() { + _ = db.Db.Close(context.Background()) + }() + + file, err := db.Loads.GetLoadById(context.Background(), *uuid) + if err != nil { + return handlers.NotFound(c, "load photo not found") + } + + mt, _, _ := mime.ParseMediaType(string(file.Photo)) + + return c.Blob(http.StatusOK, mt, file.Photo) + +} + func Get(c echo.Context) error { id := c.Param("id") cResults := make(chan ResultChan[[]loadResponseResults]) @@ -213,8 +281,6 @@ func execResultsQuery(ch chan ResultChan[[]loadResponseResults], c echo.Context) return } - fmt.Println(q.DebugSql()) - sql, params := q.Sql() rows, err := db.Db.Query(context.Background(), sql, params...) @@ -496,6 +562,35 @@ func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement { return q } +func Delete(c echo.Context) error { + id := c.Param("id") + if id == "" { + return handlers.BadRequest(c, "id is required") + } + + uuid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "id is not a valid UUID") + } + + db := c.(*database.CustomContext).Db + defer func() { + _ = db.Db.Close(context.Background()) + }() + + exists, err := db.Loads.GetLoadById(context.Background(), *uuid) + if err != nil || exists.ID.Valid == false { + return handlers.NotFound(c, "load not found") + } + + err = db.Loads.DeleteLoad(context.Background(), *uuid) + if err != nil { + return err + } + + return c.NoContent(http.StatusNoContent) +} + func getUuidExpr(ids []string) []postgres.Expression { expr := make([]postgres.Expression, 0) for _, id := range ids { diff --git a/backend/main.go b/backend/main.go index 5928ad2..f209817 100644 --- a/backend/main.go +++ b/backend/main.go @@ -99,7 +99,10 @@ func main() { // loads e.GET("/load", loads.Get) e.GET("/load/:id", loads.Get) + e.GET("/load/:id/photo", loads.Photo) e.POST("/load", loads.Post) + e.POST("/load/:id", loads.Post) + e.DELETE("/load/:id", loads.Delete) addr := fmt.Sprintf("0.0.0.0:%s", Port.GetEnvString("8080")) diff --git a/backend/models/loads.sql b/backend/models/loads.sql index 94f5077..96766b7 100644 --- a/backend/models/loads.sql +++ b/backend/models/loads.sql @@ -18,6 +18,22 @@ delete from cartridges where id = $1; +-- name: DeleteLoad :exec +delete from loads +where id = $1; + +-- name: UpdateLoad :exec +update loads set + cartridge_id = $1, + col = $2, + powder_id = $3, + powder_gr = $4, + primer_id = $5, + bullet_id = $6, + photo = $7, + meta = $8 +where id = $9; + -- name: CreateLoad :one insert into loads (cartridge_id, col, powder_id, powder_gr, primer_id, bullet_id, photo, meta) values ($1, $2, $3, $4, $5, $6, $7, $8) @@ -25,6 +41,7 @@ returning id; -- name: GetLoadById :one select l.id as id, + l.photo as photo, c.id as cartridge_id, c.name as cartridge_name, c.meta as cartridge_meta, diff --git a/backend/models/loads/loads.sql.go b/backend/models/loads/loads.sql.go index af17e6f..504dd70 100644 --- a/backend/models/loads/loads.sql.go +++ b/backend/models/loads/loads.sql.go @@ -73,6 +73,16 @@ func (q *Queries) DeleteCartridge(ctx context.Context, id pgtype.UUID) error { return err } +const deleteLoad = `-- name: DeleteLoad :exec +delete from loads +where id = $1 +` + +func (q *Queries) DeleteLoad(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteLoad, id) + return err +} + const getCartridgeById = `-- name: GetCartridgeById :one select c.id as id, c.name, c.meta from cartridges c @@ -126,6 +136,7 @@ func (q *Queries) GetCartridges(ctx context.Context) ([]GetCartridgesRow, error) const getLoadById = `-- name: GetLoadById :one select l.id as id, + l.photo as photo, c.id as cartridge_id, c.name as cartridge_name, c.meta as cartridge_meta, @@ -147,6 +158,7 @@ where l.id = $1 type GetLoadByIdRow struct { ID pgtype.UUID `json:"id"` + Photo []byte `json:"photo"` CartridgeID pgtype.UUID `json:"cartridge_id"` CartridgeName string `json:"cartridge_name"` CartridgeMeta []byte `json:"cartridge_meta"` @@ -165,6 +177,7 @@ func (q *Queries) GetLoadById(ctx context.Context, id pgtype.UUID) (GetLoadByIdR var i GetLoadByIdRow err := row.Scan( &i.ID, + &i.Photo, &i.CartridgeID, &i.CartridgeName, &i.CartridgeMeta, @@ -376,3 +389,43 @@ func (q *Queries) TotalLoads(ctx context.Context) (int64, error) { err := row.Scan(&count) return count, err } + +const updateLoad = `-- name: UpdateLoad :exec +update loads set + cartridge_id = $1, + col = $2, + powder_id = $3, + powder_gr = $4, + primer_id = $5, + bullet_id = $6, + photo = $7, + meta = $8 +where id = $9 +` + +type UpdateLoadParams struct { + CartridgeID pgtype.UUID `json:"cartridge_id"` + Col float32 `json:"col"` + PowderID pgtype.UUID `json:"powder_id"` + PowderGr float32 `json:"powder_gr"` + PrimerID pgtype.UUID `json:"primer_id"` + BulletID pgtype.UUID `json:"bullet_id"` + Photo []byte `json:"photo"` + Meta []byte `json:"meta"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateLoad(ctx context.Context, arg UpdateLoadParams) error { + _, err := q.db.Exec(ctx, updateLoad, + arg.CartridgeID, + arg.Col, + arg.PowderID, + arg.PowderGr, + arg.PrimerID, + arg.BulletID, + arg.Photo, + arg.Meta, + arg.ID, + ) + return err +} -- 2.49.1 From 56e2acfe3033eebe69909cceb503e5baa9dde3c1 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Tue, 10 Jun 2025 21:39:28 -0400 Subject: [PATCH 3/3] simplify ID validity checks in LoadById function --- backend/handlers/loads/handler.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/handlers/loads/handler.go b/backend/handlers/loads/handler.go index e2a2b34..c92b089 100644 --- a/backend/handlers/loads/handler.go +++ b/backend/handlers/loads/handler.go @@ -81,7 +81,7 @@ func Post(c echo.Context) error { } gbi, err := db.Loads.GetLoadById(context.Background(), *uuid) - if err != nil || gbi.ID.Valid == false { + if err != nil || !gbi.ID.Valid { return handlers.NotFound(c, "load not found") } @@ -202,7 +202,6 @@ func Photo(c echo.Context) error { mt, _, _ := mime.ParseMediaType(string(file.Photo)) return c.Blob(http.StatusOK, mt, file.Photo) - } func Get(c echo.Context) error { @@ -579,7 +578,7 @@ func Delete(c echo.Context) error { }() exists, err := db.Loads.GetLoadById(context.Background(), *uuid) - if err != nil || exists.ID.Valid == false { + if err != nil || !exists.ID.Valid { return handlers.NotFound(c, "load not found") } -- 2.49.1