From 0058cdce4022fce14cd9eb7245ef3fa16d97d609 Mon Sep 17 00:00:00 2001 From: John Costa Date: Thu, 24 Jul 2025 14:23:20 +0100 Subject: [PATCH] feat: showing description on image page in frontend --- backend/models/user.go | 1 + .../src/components/search-card/SearchCard.tsx | 3 - .../components/search-card/SearchCardNote.tsx | 27 -- .../search-card/SearchCardReceipt.tsx | 30 -- .../search-card/SearchCardWebsite.tsx | 26 -- frontend/src/contexts/SearchImageContext.tsx | 1 - frontend/src/network/index.ts | 19 +- frontend/src/network/sampleData.ts | 436 ------------------ frontend/src/network/types.ts | 106 ----- frontend/src/pages/front/gallery.tsx | 1 - frontend/src/pages/image/index.tsx | 17 +- 11 files changed, 18 insertions(+), 649 deletions(-) delete mode 100644 frontend/src/components/search-card/SearchCardNote.tsx delete mode 100644 frontend/src/components/search-card/SearchCardReceipt.tsx delete mode 100644 frontend/src/components/search-card/SearchCardWebsite.tsx delete mode 100644 frontend/src/network/sampleData.ts delete mode 100644 frontend/src/network/types.ts diff --git a/backend/models/user.go b/backend/models/user.go index 62932e9..24a6040 100644 --- a/backend/models/user.go +++ b/backend/models/user.go @@ -262,6 +262,7 @@ func (m UserModel) GetUserImages(ctx context.Context, userId uuid.UUID) ([]UserI UserImages.AllColumns, Image.ID, Image.ImageName, + Image.Description, ). FROM(UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID))). WHERE(UserImages.UserID.EQ(UUID(userId))) diff --git a/frontend/src/components/search-card/SearchCard.tsx b/frontend/src/components/search-card/SearchCard.tsx index f3fff2c..439505b 100644 --- a/frontend/src/components/search-card/SearchCard.tsx +++ b/frontend/src/components/search-card/SearchCard.tsx @@ -3,7 +3,6 @@ import type { UserImage } from "../../network"; import { SearchCardContact } from "./SearchCardContact"; import { SearchCardEvent } from "./SearchCardEvent"; import { SearchCardLocation } from "./SearchCardLocation"; -import { SearchCardNote } from "./SearchCardNote"; const UnwrappedSearchCard = (props: { item: UserImage }) => { const { item } = props; @@ -13,8 +12,6 @@ const UnwrappedSearchCard = (props: { item: UserImage }) => { return ; case "event": return ; - case "note": - return ; case "contact": return ; default: diff --git a/frontend/src/components/search-card/SearchCardNote.tsx b/frontend/src/components/search-card/SearchCardNote.tsx deleted file mode 100644 index 466800c..0000000 --- a/frontend/src/components/search-card/SearchCardNote.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import SolidjsMarkdown from "solidjs-markdown"; - -import { IconNote } from "@tabler/icons-solidjs"; -import type { UserImage } from "../../network"; - -type Props = { - item: Extract; -}; - -export const SearchCardNote = ({ item }: Props) => { - const { data } = item; - - return ( -
-
- -

Note

-
-

- {data.Name.length > 0 ? data.Name : "Unknown 🐞"} -

-

- {data.Content} -

-
- ); -}; diff --git a/frontend/src/components/search-card/SearchCardReceipt.tsx b/frontend/src/components/search-card/SearchCardReceipt.tsx deleted file mode 100644 index a91f79f..0000000 --- a/frontend/src/components/search-card/SearchCardReceipt.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Separator } from "@kobalte/core/separator"; - -import { IconReceipt } from "@tabler/icons-solidjs"; -import type { Receipt } from "../../network/types"; - -type Props = { - item: Receipt; -}; - -export const SearchCardReceipt = ({ item }: Props) => { - const { data } = item; - - return ( -
-
-

- {data.orderNumber} - {data.vendor} -

- -
-

- {data.shippingAddress.address} -

- -

- {data.amount} {data.currency} -

-
- ); -}; diff --git a/frontend/src/components/search-card/SearchCardWebsite.tsx b/frontend/src/components/search-card/SearchCardWebsite.tsx deleted file mode 100644 index 335ab50..0000000 --- a/frontend/src/components/search-card/SearchCardWebsite.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Separator } from "@kobalte/core/separator"; - -import { IconLink } from "@tabler/icons-solidjs"; -import type { Website } from "../../network/types"; - -type Props = { - item: Website; -}; - -export const SearchCardWebsite = ({ item }: Props) => { - const { data } = item; - - return ( -
-
-

{data.title}

- -
-

{data.url}

- -

- {data.description} -

-
- ); -}; diff --git a/frontend/src/contexts/SearchImageContext.tsx b/frontend/src/contexts/SearchImageContext.tsx index 72bd2af..3b96589 100644 --- a/frontend/src/contexts/SearchImageContext.tsx +++ b/frontend/src/contexts/SearchImageContext.tsx @@ -104,7 +104,6 @@ export const SearchImageContextProvider: Component = (props) => { contact: [], event: [], location: [], - note: [], }; for (const category of data()?.ImageProperties ?? []) { diff --git a/frontend/src/network/index.ts b/frontend/src/network/index.ts index dd591aa..17181e2 100644 --- a/frontend/src/network/index.ts +++ b/frontend/src/network/index.ts @@ -22,7 +22,7 @@ type BaseRequestParams = Partial<{ }>; // export const base = "https://haystack.johncosta.tech"; -export const base = "http://192.168.1.199:3040"; +export const base = "http://localhost:3040"; const getBaseRequest = ({ path, body, method }: BaseRequestParams): Request => { return new Request(`${base}/${path}`, { @@ -118,15 +118,6 @@ const eventValidator = strictObject({ Images: array(pipe(string(), uuid())), }); -const noteValidator = strictObject({ - ID: pipe(string(), uuid()), - CreatedAt: pipe(string()), - Name: string(), - Description: nullable(string()), - Content: string(), - Images: array(pipe(string(), uuid())), -}); - const locationDataType = strictObject({ type: literal("location"), data: locationValidator, @@ -137,11 +128,6 @@ const eventDataType = strictObject({ data: eventValidator, }); -const noteDataType = strictObject({ - type: literal("note"), - data: noteValidator, -}); - const contactDataType = strictObject({ type: literal("contact"), data: contactValidator, @@ -150,7 +136,6 @@ const contactDataType = strictObject({ const dataTypeValidator = variant("type", [ locationDataType, eventDataType, - noteDataType, contactDataType, ]); @@ -164,6 +149,7 @@ const userImageValidator = strictObject({ Image: strictObject({ ID: pipe(string(), uuid()), ImageName: string(), + Description: string(), Image: null_(), }), }); @@ -175,6 +161,7 @@ const userProcessingImageValidator = strictObject({ Image: strictObject({ ID: pipe(string(), uuid()), ImageName: string(), + Description: string(), Image: null_(), }), Status: union([ diff --git a/frontend/src/network/sampleData.ts b/frontend/src/network/sampleData.ts deleted file mode 100644 index 8c1f992..0000000 --- a/frontend/src/network/sampleData.ts +++ /dev/null @@ -1,436 +0,0 @@ -import type { DataArray, DataItem } from "./types"; - -const getRawData = (item: DataItem) => { - return Object.values(item.data).join(" "); -}; - -export const sampleData: DataArray = [ - { - id: "1", - type: "Event", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "Startup Mixer", - dateTime: { - start: "2025-06-01T19:00:00+01:00", - end: "2025-06-01T23:00:00+01:00", - }, - location: "The Kings Arms, 27 Ropemaker St, London EC2Y 9LY", - description: - "Casual networking event for tech entrepreneurs and investors in London.", - organizer: { - name: "London Startup Network", - email: "events@londonstartupnetwork.co.uk", - }, - attendees: ["Alex Smith", "Sofia Rodriguez"], - category: "Networking", - }, - }, - { - id: "2", - type: "Contact", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "João Silva", - phoneNumber: "+351 912 345 678", - emailAddress: "joao.silva@example.pt", - address: "Rua do Carmo 12, 1200-161 Lisboa, Portugal", - organization: "PortoTech Solutions", - title: "Marketing Manager", - notes: "Met at Web Summit Lisbon 2024", - }, - }, - { - id: "3", - type: "Location", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "Barman Dictat", - address: "14 Khreshchatyk St., Kyiv, Ukraine", - category: "Bar", - description: - "Stylish cocktail bar in the heart of Kyiv with an extensive menu of craft cocktails and a sophisticated atmosphere", - }, - }, - { - id: "4", - type: "Note", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "Q2 2025 Marketing Strategy", - keywords: ["strategy", "digital marketing", "Q2", "2025"], - content: - "## Executive Summary\n\nOur Q2 2025 marketing strategy focuses on expanding our digital presence and increasing customer engagement across all platforms. We will leverage AI-driven personalization to enhance user experience and implement a multi-channel approach to reach our target demographics more effectively.\n\n### Key Objectives\n\n1. Increase website traffic by 30% through SEO optimization and content marketing.\n2. Boost social media engagement rates by 25% using interactive campaigns and influencer partnerships.\n3. Implement a new customer loyalty program to improve retention rates by 15%.\n\nBy aligning our marketing efforts with emerging trends and customer preferences, we aim to solidify our market position and drive sustainable growth throughout Q2 and beyond.", - }, - }, - { - id: "5", - type: "Website", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - url: "https://www.attio.com", - title: "Attio", - description: - "Attio is the AI-native CRM that builds, scales and grows your company to the next level.", - category: "SaaS", - }, - }, - { - id: "6", - type: "Receipt", - get rawData() { - return getRawData(this); - }, - get title() { - return `${this.data.orderNumber} - ${this.data.vendor}`; - }, - data: { - receiptDate: "2025-03-12T20:15:30+01:00", - orderNumber: "ORD12345", - amount: 49.99, - currency: "GBP", - vendor: "Zara Online Store", - items: [ - { - name: "Slim Fit Dress Shirt", - quantity: 1, - price: 49.99, - currency: "GBP", - }, - ], - paymentMethod: "Visa", - shippingAddress: { - name: "Alex Smith", - address: "123 High St, London, EC2A 3AZ", - }, - category: "Online Shopping", - }, - }, - { - id: "7", - type: "Event", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "AI in Healthcare Summit", - dateTime: { - start: "2025-07-15T09:00:00+01:00", - end: "2025-07-15T17:00:00+01:00", - }, - location: - "Royal College of Physicians, 11 St Andrews Pl, London NW1 4LE", - description: - "Annual conference exploring the latest developments in AI applications for healthcare", - organizer: { - name: "HealthTech Alliance", - email: "events@healthtechalliance.org", - }, - attendees: [ - "Dr. Sarah Chen", - "Prof. James Wilson", - "Dr. Maria Santos", - ], - category: "Conference", - }, - }, - { - id: "8", - type: "Contact", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "Emma Schmidt", - phoneNumber: "+49 30 12345678", - emailAddress: "e.schmidt@techberlin.de", - address: "Friedrichstraße 123, 10117 Berlin, Germany", - organization: "TechBerlin GmbH", - title: "Chief Technology Officer", - notes: "Key contact for Berlin tech scene, met at TechFest 2024", - }, - }, - { - id: "9", - type: "Location", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "Digital Nomad Hub", - address: "Calle Princesa 25, 08001 Barcelona, Spain", - category: "Coworking Space", - description: - "Modern coworking space with high-speed internet, meeting rooms, and a vibrant community of international remote workers", - }, - }, - { - id: "10", - type: "Website", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - url: "https://www.techcrunch.com", - title: "TechCrunch", - description: - "Leading technology media platform covering startups, tech news, and venture capital", - category: "Tech News", - }, - }, - { - id: "11", - type: "Note", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "Product Roadmap 2025", - keywords: ["product development", "strategy", "features", "2025"], - content: - "## Overview\n\nPriority features for 2025:\n\n1. AI-powered customer insights dashboard\n2. Integration with major CRM platforms\n3. Mobile app redesign\n4. Enhanced analytics suite\n\n### Timeline\n- Q1: Research and planning\n- Q2: Development phase 1\n- Q3: Beta testing\n- Q4: Full release", - }, - }, - { - id: "12", - type: "Receipt", - get rawData() { - return getRawData(this); - }, - get title() { - return `${this.data.orderNumber} - ${this.data.vendor}`; - }, - data: { - receiptDate: "2025-03-15T13:45:00+01:00", - orderNumber: "INV789012", - amount: 1299.99, - currency: "EUR", - vendor: "Apple Store", - items: [ - { - name: "MacBook Air M3", - quantity: 1, - price: 1299.99, - currency: "EUR", - }, - ], - paymentMethod: "MasterCard", - shippingAddress: { - name: "Emma Schmidt", - address: "Friedrichstraße 123, 10117 Berlin, Germany", - }, - category: "Electronics", - }, - }, - { - id: "13", - type: "Event", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "Sustainable Tech Workshop", - dateTime: { - start: "2025-08-20T14:00:00+02:00", - end: "2025-08-20T17:00:00+02:00", - }, - location: "GreenTech Hub, Prinsengracht 150, Amsterdam", - description: - "Workshop on implementing sustainable practices in tech companies", - organizer: { - name: "Green Digital Alliance", - email: "workshops@greendigital.org", - }, - attendees: ["Lisa van der Berg", "Mark Johnson"], - category: "Workshop", - }, - }, - { - id: "14", - type: "Contact", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "Akiko Tanaka", - phoneNumber: "+81 3 1234 5678", - emailAddress: "a.tanaka@tokyotech.jp", - address: "2-1-1 Marunouchi, Chiyoda-ku, Tokyo 100-0005", - organization: "Tokyo Tech Ventures", - title: "Investment Director", - notes: "Specialist in Asia-Pacific tech investments", - }, - }, - { - id: "15", - type: "Location", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "Innovation Center Stockholm", - address: "Regeringsgatan 65, 111 56 Stockholm, Sweden", - category: "Tech Hub", - description: - "Leading innovation center in Scandinavia, hosting startups and tech events", - }, - }, - { - id: "16", - type: "Website", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - url: "https://www.github.com", - title: "GitHub", - description: "World's leading software development platform", - category: "Development Tools", - }, - }, - { - id: "17", - type: "Note", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "Team Structure Reorganization", - keywords: ["organization", "teams", "structure", "management"], - content: - "## Proposed Changes\n\n1. Create dedicated AI/ML team\n2. Merge frontend and mobile teams\n3. Establish DevOps center of excellence\n\n### Timeline\nGradual implementation over Q3 2025\n\n### Expected Outcomes\n- Improved efficiency\n- Better resource allocation\n- Faster delivery cycles", - }, - }, - { - id: "18", - type: "Receipt", - get rawData() { - return getRawData(this); - }, - get title() { - return `${this.data.orderNumber} - ${this.data.vendor}`; - }, - data: { - receiptDate: "2025-03-18T10:30:00+01:00", - orderNumber: "BOK456789", - amount: 850.0, - currency: "USD", - vendor: "Hilton Hotels", - items: [ - { - name: "Executive Suite - 2 nights", - quantity: 1, - price: 850.0, - currency: "USD", - }, - ], - paymentMethod: "American Express", - shippingAddress: { - name: "Akiko Tanaka", - address: - "Hilton San Francisco, 333 O'Farrell St, San Francisco, CA 94102", - }, - category: "Travel", - }, - }, - { - id: "19", - type: "Event", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.title; - }, - data: { - title: "Web3 Developer Conference", - dateTime: { - start: "2025-09-10T09:00:00-07:00", - end: "2025-09-12T17:00:00-07:00", - }, - location: "Moscone Center, 747 Howard St, San Francisco, CA 94103", - description: - "Three-day conference focusing on blockchain, DeFi, and Web3 development", - organizer: { - name: "Web3 Alliance", - email: "conference@web3alliance.org", - }, - attendees: ["Vitalik B.", "Charles H.", "Gavin W."], - category: "Conference", - }, - }, - { - id: "20", - type: "Contact", - get rawData() { - return getRawData(this); - }, - get title() { - return this.data.name; - }, - data: { - name: "Rachel Chen", - phoneNumber: "+1 415 555 0123", - emailAddress: "rachel.chen@siliconvc.com", - address: "525 Market St, San Francisco, CA 94105", - organization: "Silicon Valley Capital", - title: "Partner", - notes: "Specializes in Series A/B investments in AI and ML startups", - }, - }, -]; diff --git a/frontend/src/network/types.ts b/frontend/src/network/types.ts deleted file mode 100644 index 410584e..0000000 --- a/frontend/src/network/types.ts +++ /dev/null @@ -1,106 +0,0 @@ -interface DateTime { - start: string; - end: string; -} - -interface Address { - name: string; - address: string; -} - -interface Organizer { - name: string; - email: string; -} - -interface ReceiptItem { - name: string; - quantity: number; - price: number; - currency: string; -} - -interface DataItemType { - id: string; - title: string; - type: T; - rawData: string; - data: D; -} - -interface EventData { - title: string; - dateTime: DateTime; - location: string; - description: string; - organizer: Organizer; - attendees: string[]; - category: string; -} - -interface ContactData { - name: string; - phoneNumber: string; - emailAddress: string; - address: string; - organization: string; - title: string; - notes: string; -} - -interface LocationData { - name: string; - address: string; - category: string; - description: string; -} - -interface NoteData { - title: string; - keywords: string[]; - content: string; -} - -interface WebsiteData { - url: string; - title: string; - description: string; - category: string; -} - -interface ReceiptData { - receiptDate: string; - orderNumber: string; - amount: number; - currency: string; - vendor: string; - items: ReceiptItem[]; - paymentMethod: string; - shippingAddress: Address; - category: string; -} - -type Event = DataItemType<"Event", EventData>; -type Contact = DataItemType<"Contact", ContactData>; -type Location = DataItemType<"Location", LocationData>; -type Note = DataItemType<"Note", NoteData>; -type Website = DataItemType<"Website", WebsiteData>; -type Receipt = DataItemType<"Receipt", ReceiptData>; - -type DataItem = Event | Contact | Location | Note | Website | Receipt; -type DataArray = DataItem[]; - -export type { - DateTime, - Address, - Organizer, - ReceiptItem, - Event, - Contact, - Location, - Note, - Website, - Receipt, - DataItem, - DataArray, -}; diff --git a/frontend/src/pages/front/gallery.tsx b/frontend/src/pages/front/gallery.tsx index 058eaec..db26ff6 100644 --- a/frontend/src/pages/front/gallery.tsx +++ b/frontend/src/pages/front/gallery.tsx @@ -14,7 +14,6 @@ const CategoryColor: Record< contact: "bg-orange-50", location: "bg-red-50", event: "bg-purple-50", - note: "bg-green-50", }; const colors = [ diff --git a/frontend/src/pages/image/index.tsx b/frontend/src/pages/image/index.tsx index 3f25af1..654b958 100644 --- a/frontend/src/pages/image/index.tsx +++ b/frontend/src/pages/image/index.tsx @@ -1,14 +1,21 @@ import { ImageComponent } from "@components/image"; import { SearchCard } from "@components/search-card/SearchCard"; import { useSearchImageContext } from "@contexts/SearchImageContext"; -import { base, UserImage } from "@network/index"; +import { UserImage } from "@network/index"; import { useParams } from "@solidjs/router"; -import { For, Show, type Component } from "solid-js"; +import { createEffect, For, Show, type Component } from "solid-js"; +import SolidjsMarkdown from "solidjs-markdown"; export const ImagePage: Component = () => { const { imageId } = useParams<{ imageId: string }>(); - const { imagesWithProperties } = useSearchImageContext(); + const { imagesWithProperties, userImages } = useSearchImageContext(); + + const image = () => userImages().find((i) => i.ImageID === imageId); + + createEffect(() => { + console.log(userImages()); + }); const imageProperties = (): UserImage[] | undefined => Object.entries(imagesWithProperties()).find(([id]) => id === imageId)?.[1]; @@ -18,6 +25,10 @@ export const ImagePage: Component = () => {
+
+

Description

+ {image()?.Image.Description} +
{(image) => (