From 649cfe0b02badbd46902ba76ce1b52676286d16b Mon Sep 17 00:00:00 2001 From: John Costa Date: Sun, 5 Oct 2025 10:44:57 +0100 Subject: [PATCH] fix: frontend with new backend schema --- backend/images/handler.go | 8 +- backend/models/image.go | 2 +- backend/stacks/handler.go | 4 +- frontend/src/components/list-card/index.tsx | 46 +++---- frontend/src/contexts/Notifications.tsx | 12 +- frontend/src/contexts/SearchImageContext.tsx | 15 +-- frontend/src/network/index.ts | 122 ++++++++----------- frontend/src/pages/all-images/index.tsx | 3 +- frontend/src/pages/front/gallery.tsx | 12 +- frontend/src/pages/front/recent.tsx | 3 +- frontend/src/pages/image/index.tsx | 14 +-- frontend/src/pages/list/index.tsx | 8 +- frontend/src/pages/search/index.tsx | 4 +- 13 files changed, 111 insertions(+), 142 deletions(-) diff --git a/backend/images/handler.go b/backend/images/handler.go index db1a2fe..61eda7b 100644 --- a/backend/images/handler.go +++ b/backend/images/handler.go @@ -34,8 +34,8 @@ type ImageHandler struct { } type ImagesReturn struct { - UserImages []models.UserImageWithImage `json:"userImages"` - Lists []models.ListsWithImages `json:"lists"` + UserImages []models.UserImageWithImage + Stacks []models.ListsWithImages } func (h *ImageHandler) serveImage(w http.ResponseWriter, r *http.Request) { @@ -89,7 +89,7 @@ func (h *ImageHandler) listImages(w http.ResponseWriter, r *http.Request) { return } - listsWithImages, err := h.userModel.ListWithImages(r.Context(), userId) + stacksWithImages, err := h.userModel.ListWithImages(r.Context(), userId) if err != nil { middleware.WriteErrorInternal(h.logger, "could not get lists with images", w) return @@ -97,7 +97,7 @@ func (h *ImageHandler) listImages(w http.ResponseWriter, r *http.Request) { imagesReturn := ImagesReturn{ UserImages: images, - Lists: listsWithImages, + Stacks: stacksWithImages, } middleware.WriteJsonOrError(h.logger, imagesReturn, w) diff --git a/backend/models/image.go b/backend/models/image.go index 302d9fe..a15f591 100644 --- a/backend/models/image.go +++ b/backend/models/image.go @@ -29,7 +29,7 @@ func (m ImageModel) Save(ctx context.Context, name string, image []byte, userID } func (m ImageModel) Get(ctx context.Context, imageID uuid.UUID) (model.Image, bool, error) { - getImageStmt := Image.SELECT(Image.AllColumns.Except(Image.Image)).WHERE(Image.ID.EQ(UUID(imageID))) + getImageStmt := Image.SELECT(Image.AllColumns).WHERE(Image.ID.EQ(UUID(imageID))) image := model.Image{} err := getImageStmt.QueryContext(ctx, m.dbPool, &image) diff --git a/backend/stacks/handler.go b/backend/stacks/handler.go index 9adde25..5465226 100644 --- a/backend/stacks/handler.go +++ b/backend/stacks/handler.go @@ -33,14 +33,14 @@ func (h *StackHandler) getAllStacks(w http.ResponseWriter, r *http.Request) { return } - lists, err := h.stackModel.List(ctx, userID) + stacks, err := h.stackModel.List(ctx, userID) if err != nil { h.logger.Warn("could not get stacks", "err", err) w.WriteHeader(http.StatusBadRequest) return } - middleware.WriteJsonOrError(h.logger, lists, w) + middleware.WriteJsonOrError(h.logger, stacks, w) } func (h *StackHandler) getStackItems(w http.ResponseWriter, r *http.Request) { diff --git a/frontend/src/components/list-card/index.tsx b/frontend/src/components/list-card/index.tsx index 3565eaf..81959f8 100644 --- a/frontend/src/components/list-card/index.tsx +++ b/frontend/src/components/list-card/index.tsx @@ -4,32 +4,32 @@ import fastHashCode from "../../utils/hash"; import { A } from "@solidjs/router"; const colors = [ - "bg-emerald-50", - "bg-lime-50", + "bg-emerald-50", + "bg-lime-50", - "bg-indigo-50", - "bg-sky-50", + "bg-indigo-50", + "bg-sky-50", - "bg-amber-50", - "bg-teal-50", + "bg-amber-50", + "bg-teal-50", - "bg-fuchsia-50", - "bg-pink-50", + "bg-fuchsia-50", + "bg-pink-50", ]; -export const ListCard: Component<{ list: List }> = (props) => { - return ( - -

{props.list.Name}

-

{props.list.Images.length}

-
- ); +export const StackCard: Component<{ list: List }> = (props) => { + return ( + +

{props.list.Name}

+

{props.list.Images.length}

+
+ ); }; diff --git a/frontend/src/contexts/Notifications.tsx b/frontend/src/contexts/Notifications.tsx index 02b50d5..d7ea796 100644 --- a/frontend/src/contexts/Notifications.tsx +++ b/frontend/src/contexts/Notifications.tsx @@ -34,7 +34,7 @@ export const Notifications = (onCompleteImage: () => void) => { ProcessingLists: {}, }); - const { processingImages } = useSearchImageContext(); + const { userImages } = useSearchImageContext(); const [accessToken] = createResource(getAccessToken); @@ -91,19 +91,19 @@ export const Notifications = (onCompleteImage: () => void) => { }; createEffect(() => { - const images = processingImages(); + const images = userImages(); if (images == null) { return; } upsertImageProcessing( Object.fromEntries( - images.map((i) => [ - i.ImageID, + images.filter(i => i.Status !== 'complete').map((i) => [ + i.ID, { Type: "image", - ImageID: i.ImageID, - ImageName: i.Image.ImageName, + ImageID: i.ID, + ImageName: i.ImageName, Status: i.Status, }, ]), diff --git a/frontend/src/contexts/SearchImageContext.tsx b/frontend/src/contexts/SearchImageContext.tsx index e6d87ac..7f122d9 100644 --- a/frontend/src/contexts/SearchImageContext.tsx +++ b/frontend/src/contexts/SearchImageContext.tsx @@ -14,14 +14,10 @@ export type SearchImageStore = { Array<{ date: Date; images: JustTheImageWhatAreTheseNames }> >; - lists: Accessor>["lists"]>; + stacks: Accessor>["Stacks"]>; userImages: Accessor; - processingImages: Accessor< - Awaited>["processingImages"] | undefined - >; - onRefetchImages: () => void; onDeleteImage: (imageID: string) => void; onDeleteImageFromStack: (stackID: string, imageID: string) => void; @@ -41,7 +37,7 @@ export const SearchImageContextProvider: Component = (props) => { // Sorted by day. But we could potentially add more in the future. const buckets: Record = {}; - for (const image of d.userImages) { + for (const image of d.UserImages) { if (image.CreatedAt == null) { continue; } @@ -60,15 +56,12 @@ export const SearchImageContextProvider: Component = (props) => { }, ); - const processingImages = () => data()?.processingImages ?? []; - return ( data()?.lists ?? [], - userImages: () => data()?.userImages ?? [], - processingImages, + stacks: () => data()?.Stacks ?? [], + userImages: () => data()?.UserImages ?? [], onRefetchImages: refetch, onDeleteImage: (imageID: string) => { deleteImage(imageID).then(refetch); diff --git a/frontend/src/network/index.ts b/frontend/src/network/index.ts index 29ea561..882a0b0 100644 --- a/frontend/src/network/index.ts +++ b/frontend/src/network/index.ts @@ -1,12 +1,11 @@ import { getTokenProperties } from "@components/protected-route"; import { fetch } from "@tauri-apps/plugin-http"; -import { jwtDecode } from "jwt-decode"; import { type InferOutput, array, - literal, null_, + literal, nullable, parse, pipe, @@ -172,91 +171,70 @@ export const sendImage = async ( return parsedRes.output; }; -const imageMetaValidator = strictObject({ - ID: pipe(string(), uuid()), - ImageName: string(), - Description: string(), - Image: null_(), -}); - const userImageValidator = strictObject({ ID: pipe(string(), uuid()), - CreatedAt: pipe(string()), - ImageID: pipe(string(), uuid()), + CreatedAt: string(), UserID: pipe(string(), uuid()), - Image: strictObject({ - ...imageMetaValidator.entries, - ImageLists: pipe(nullable(array( - strictObject({ - ID: pipe(string(), uuid()), - ImageID: pipe(string(), uuid()), - ListID: pipe(string(), uuid()), - }), - )), transform(l => l ?? [])), - }), + Description: string(), + + Image: null_(), + ImageName: string(), + + Status: union([literal('not-started'), literal('in-progress'), literal('complete')]), + + ImageStacks: pipe(nullable(array( + strictObject({ + ID: pipe(string(), uuid()), + ImageID: pipe(string(), uuid()), + ListID: pipe(string(), uuid()), + }), + )), transform(l => l ?? [])), }); -const userProcessingImageValidator = strictObject({ +const stackItem = strictObject({ ID: pipe(string(), uuid()), ImageID: pipe(string(), uuid()), - UserID: pipe(string(), uuid()), - Image: imageMetaValidator, - Status: union([ - literal("not-started"), - literal("in-progress"), - literal("complete"), - ]), + SchemaItemID: pipe(string(), uuid()), + + Value: string(), +}) + +const stackImage = strictObject({ + ID: pipe(string(), uuid()), + ImageID: pipe(string(), uuid()), + StackID: pipe(string(), uuid()), + + Items: pipe(nullable(array(stackItem)), transform(l => l ?? [])), }); -const listValidator = strictObject({ +const stackSchemaItem = strictObject({ ID: pipe(string(), uuid()), + StackID: pipe(string(), uuid()), + + Description: string(), + Item: string(), + Value: string(), +}) + +const stackValidator = strictObject({ + ID: pipe(string(), uuid()), + CreatedAt: string(), UserID: pipe(string(), uuid()), - CreatedAt: pipe(string()), + Description: string(), + + Status: union([literal('not-started'), literal('in-progress'), literal('complete')]), + Name: string(), - Description: nullable(string()), - Images: pipe( - nullable( - array( - strictObject({ - ID: pipe(string(), uuid()), - ImageID: pipe(string(), uuid()), - ListID: pipe(string(), uuid()), - Items: array( - strictObject({ - ID: pipe(string(), uuid()), - ImageID: pipe(string(), uuid()), - SchemaItemID: pipe(string(), uuid()), - Value: string(), - }), - ), - }), - ), - ), - transform((n) => n ?? []), - ), - - Schema: strictObject({ - ID: pipe(string(), uuid()), - ListID: pipe(string(), uuid()), - SchemaItems: array( - strictObject({ - ID: pipe(string(), uuid()), - SchemaID: pipe(string(), uuid()), - Item: string(), - Value: nullable(string()), - Description: string(), - }), - ), - }), + Images: array(stackImage), + SchemaItems: array(stackSchemaItem), }); -export type List = InferOutput; +export type List = InferOutput; const imageRequestValidator = strictObject({ - userImages: array(userImageValidator), - processingImages: array(userProcessingImageValidator), - lists: array(listValidator), + UserImages: array(userImageValidator), + Stacks: array(stackValidator), }); export type JustTheImageWhatAreTheseNames = InferOutput< @@ -274,7 +252,7 @@ export const getUserImages = async (): Promise< const parsedRes = safeParse(imageRequestValidator, res); if (!parsedRes.success) { - console.log(parsedRes.issues) + console.log("Schema error: ", parsedRes.issues) throw new Error(JSON.stringify(parsedRes.issues)); } @@ -310,7 +288,7 @@ export const postCode = async ( const parsedRes = safeParse(codeValidator, res); if (!parsedRes.success) { - console.log(parsedRes.issues) + console.log("Schema error: ", parsedRes.issues) throw new Error(JSON.stringify(parsedRes.issues)); } diff --git a/frontend/src/pages/all-images/index.tsx b/frontend/src/pages/all-images/index.tsx index c588907..2efa05c 100644 --- a/frontend/src/pages/all-images/index.tsx +++ b/frontend/src/pages/all-images/index.tsx @@ -3,7 +3,6 @@ import { Component, For } from "solid-js"; import { createVirtualizer } from "@tanstack/solid-virtual"; import { ImageComponent } from "@components/image"; import { chunkRows } from "./chunk"; -import { deleteImage } from "@network/index"; type ImageOrDate = | { type: "image"; ID: string[] } @@ -21,7 +20,7 @@ export const AllImages: Component = () => { items.push({ type: "date", date }); const chunkedRows = chunkRows(3, images); for (const chunk of chunkedRows) { - items.push({ type: "image", ID: chunk.map((c) => c.ImageID) }); + items.push({ type: "image", ID: chunk.map((c) => c.ID) }); } } diff --git a/frontend/src/pages/front/gallery.tsx b/frontend/src/pages/front/gallery.tsx index b6628a5..ae8d879 100644 --- a/frontend/src/pages/front/gallery.tsx +++ b/frontend/src/pages/front/gallery.tsx @@ -1,13 +1,13 @@ import { Component, For, createSignal } from "solid-js"; import { useSearchImageContext } from "@contexts/SearchImageContext"; -import { ListCard } from "@components/list-card"; +import { StackCard } from "@components/list-card"; import { Button } from "@kobalte/core/button"; import { Dialog } from "@kobalte/core/dialog"; import { createList, ReachedListLimit } from "../../network"; import { createToast } from "../../utils/show-toast"; export const Categories: Component = () => { - const { lists, onRefetchImages } = useSearchImageContext(); + const { stacks, onRefetchImages } = useSearchImageContext(); const [title, setTitle] = createSignal(""); const [description, setDescription] = createSignal(""); @@ -25,11 +25,11 @@ export const Categories: Component = () => { setTitle(""); setDescription(""); setShowForm(false); - onRefetchImages(); // Refresh the lists + onRefetchImages(); // Refresh the stacks } catch (error) { console.error("Failed to create list:", error); if (error instanceof ReachedListLimit) { - createToast("Reached limit!", "You've reached your limit for new lists"); + createToast("Reached limit!", "You've reached your limit for new stacks"); } } finally { setIsCreating(false); @@ -38,9 +38,9 @@ export const Categories: Component = () => { return (
-

Generated Lists

+

Generated stacks

- {(list) => } + {(list) => }
diff --git a/frontend/src/pages/front/recent.tsx b/frontend/src/pages/front/recent.tsx index 4925179..b5fa5b5 100644 --- a/frontend/src/pages/front/recent.tsx +++ b/frontend/src/pages/front/recent.tsx @@ -1,7 +1,6 @@ import { Component, For } from "solid-js"; import { ImageComponent } from "@components/image"; import { useSearchImageContext } from "@contexts/SearchImageContext"; -import { deleteImage } from "@network/index"; const NUMBER_OF_MAX_RECENT_IMAGES = 10; @@ -21,7 +20,7 @@ export const Recent: Component = () => {

Recent Screenshots

- {(image) => } + {(image) => }
diff --git a/frontend/src/pages/image/index.tsx b/frontend/src/pages/image/index.tsx index 96b3747..751135c 100644 --- a/frontend/src/pages/image/index.tsx +++ b/frontend/src/pages/image/index.tsx @@ -3,15 +3,15 @@ import { useSearchImageContext } from "@contexts/SearchImageContext"; import { useNavigate, useParams } from "@solidjs/router"; import { For, type Component } from "solid-js"; import SolidjsMarkdown from "solidjs-markdown"; -import { ListCard } from "@components/list-card"; +import { StackCard } from "@components/list-card"; export const ImagePage: Component = () => { const { imageId } = useParams<{ imageId: string }>(); const nav = useNavigate(); - const { userImages, lists, onDeleteImage } = useSearchImageContext(); + const { userImages, stacks, onDeleteImage } = useSearchImageContext(); - const image = () => userImages().find((i) => i.ImageID === imageId); + const image = () => userImages().find((i) => i.ID === imageId); return (
@@ -24,10 +24,10 @@ export const ImagePage: Component = () => {

Description

- + {(imageList) => ( - l.ID === imageList.ListID)!} + l.ID === imageList.ListID)!} /> )} @@ -35,7 +35,7 @@ export const ImagePage: Component = () => {

Description

- {image()?.Image.Description} + {image()?.Description}
); diff --git a/frontend/src/pages/list/index.tsx b/frontend/src/pages/list/index.tsx index 0785f47..e0e6964 100644 --- a/frontend/src/pages/list/index.tsx +++ b/frontend/src/pages/list/index.tsx @@ -107,11 +107,11 @@ export const List: Component = () => { const { listId } = useParams(); const nav = useNavigate(); - const { lists, onDeleteImageFromStack } = useSearchImageContext(); + const { stacks, onDeleteImageFromStack } = useSearchImageContext(); const [accessToken] = createResource(getAccessToken); - const list = () => lists().find((l) => l.ID === listId); + const list = () => stacks().find((l) => l.ID === listId); const handleDeleteList = async () => { await deleteList(listId) @@ -148,11 +148,11 @@ export const List: Component = () => { Image - + {(item, index) => ( { - {(item) => } + {(item) => } No result found