From 1bd6ae14ebc4ffeea2e4a4d61c3d0078869afce6 Mon Sep 17 00:00:00 2001 From: Elian Van Cutsem Date: Wed, 15 Oct 2025 10:10:34 +0200 Subject: [PATCH] feat: add location info --- db/config.ts | 19 +- db/seed.ts | 13 +- src/actions/index.ts | 69 +++++++ src/components/admin/LocationAdmin.astro | 199 +++++++++++++++++++ src/components/home/AboutMe.astro | 13 +- src/components/home/CurrentLocation.astro | 29 +++ src/content/config.ts | 5 + src/content/events/23-09-13-qwik-paris.md | 4 + src/content/events/23-11-03-cityjs-berlin.md | 6 +- src/pages/admin.astro | 46 +++++ 10 files changed, 390 insertions(+), 13 deletions(-) create mode 100644 src/actions/index.ts create mode 100644 src/components/admin/LocationAdmin.astro create mode 100644 src/components/home/CurrentLocation.astro create mode 100644 src/pages/admin.astro diff --git a/db/config.ts b/db/config.ts index 0932e2ab..f570ad5c 100644 --- a/db/config.ts +++ b/db/config.ts @@ -1,6 +1,21 @@ -import { defineDb } from 'astro:db'; +import { defineDb, defineTable, column } from 'astro:db'; + +const UserLocation = defineTable({ + columns: { + id: column.number({ primaryKey: true }), + city: column.text(), + country: column.text(), + countryFlag: column.text(), + isHome: column.boolean({ default: false }), + isTravelling: column.boolean({ default: false }), + currentEvent: column.text({ optional: true }), + updatedAt: column.date({ default: new Date() }) + } +}); // https://astro.build/db/config export default defineDb({ - tables: {} + tables: { + UserLocation + } }); diff --git a/db/seed.ts b/db/seed.ts index fffc464b..302fb89e 100644 --- a/db/seed.ts +++ b/db/seed.ts @@ -1,6 +1,15 @@ -import { db } from 'astro:db'; +import { db, UserLocation } from 'astro:db'; // https://astro.build/db/seed export default async function seed() { - // TODO + // Insert default home location + await db.insert(UserLocation).values({ + id: 1, + city: 'Ghent', + country: 'Belgium', + countryFlag: 'πŸ‡§πŸ‡ͺ', + isHome: true, + isTravelling: false, + updatedAt: new Date() + }); } diff --git a/src/actions/index.ts b/src/actions/index.ts new file mode 100644 index 00000000..d8124c96 --- /dev/null +++ b/src/actions/index.ts @@ -0,0 +1,69 @@ +import { defineAction, ActionError } from 'astro:actions'; +import { z } from 'astro:schema'; +import { db, UserLocation } from 'astro:db'; + +export const server = { + updateLocation: defineAction({ + accept: 'form', + input: z.object({ + city: z.string().min(1, 'City is required'), + country: z.string().min(1, 'Country is required'), + countryFlag: z.string().min(1, 'Country flag is required'), + isTravelling: z.boolean().optional().default(false), + currentEvent: z.string().optional(), + }), + handler: async (input) => { + try { + // Update location (delete and insert for simplicity since we only store one location) + await db.delete(UserLocation); + await db.insert(UserLocation).values({ + id: 1, + city: input.city, + country: input.country, + countryFlag: input.countryFlag, + isHome: !input.isTravelling, + isTravelling: input.isTravelling, + currentEvent: input.currentEvent || null, + updatedAt: new Date() + }); + + return { + success: true, + message: 'Location updated successfully', + location: { + city: input.city, + country: input.country, + countryFlag: input.countryFlag, + isTravelling: input.isTravelling, + currentEvent: input.currentEvent + } + }; + } catch (error) { + throw new ActionError({ + code: 'INTERNAL_SERVER_ERROR', + message: 'Failed to update location' + }); + } + } + }), + + getCurrentLocation: defineAction({ + input: z.object({}), + handler: async () => { + try { + const locations = await db.select().from(UserLocation).limit(1); + const location = locations[0] || null; + + return { + success: true, + location + }; + } catch (error) { + throw new ActionError({ + code: 'INTERNAL_SERVER_ERROR', + message: 'Failed to get current location' + }); + } + } + }) +}; diff --git a/src/components/admin/LocationAdmin.astro b/src/components/admin/LocationAdmin.astro new file mode 100644 index 00000000..9e8c05bc --- /dev/null +++ b/src/components/admin/LocationAdmin.astro @@ -0,0 +1,199 @@ +--- +import { actions, isInputError } from 'astro:actions'; + +// Handle form result (only if form was submitted) +const result = Astro.getActionResult(actions.updateLocation); +const inputErrors = isInputError(result?.error) ? result.error.fields : {}; + +// Show success message if location was updated +const showSuccess = result && !result.error; + +// Default location for form (will be loaded client-side) +const defaultLocation = { + city: 'Ghent', + country: 'Belgium', + countryFlag: 'πŸ‡§πŸ‡ͺ', + isHome: true, + isTravelling: false, + currentEvent: '', +}; +--- + +
+

Update Location

+ + { + showSuccess && ( +
+ Location updated successfully! βœ… +
+ ) + } + + { + result?.error && !isInputError(result.error) && ( +
+ Error: {result.error.message} +
+ ) + } + +
+
+ + + { + inputErrors.city && ( +

{inputErrors.city.join(', ')}

+ ) + } +
+ +
+ + + { + inputErrors.country && ( +

+ {inputErrors.country.join(', ')} +

+ ) + } +
+ +
+ + + { + inputErrors.countryFlag && ( +

+ {inputErrors.countryFlag.join(', ')} +

+ ) + } +
+ +
+ + +
+ +
+ + + { + inputErrors.currentEvent && ( +

+ {inputErrors.currentEvent.join(', ')} +

+ ) + } +
+ + +
+
+ + + + diff --git a/src/components/home/AboutMe.astro b/src/components/home/AboutMe.astro index 68b30616..7728f34f 100644 --- a/src/components/home/AboutMe.astro +++ b/src/components/home/AboutMe.astro @@ -1,8 +1,9 @@ --- -import { Image } from "astro:assets"; -import { Button, Card } from "@eliancodes/brutal-ui"; +import { Image } from 'astro:assets'; +import { Button, Card } from '@eliancodes/brutal-ui'; +import CurrentLocation from './CurrentLocation.astro'; -import headshot from "../../assets/elian.jpg"; +import headshot from '../../assets/elian.jpg'; ---
@@ -44,11 +45,7 @@ import headshot from "../../assets/elian.jpg"; href='https://www.devs.gent/'>devs.gent meetup organizer

-

- Located in Ghent, πŸ‡§πŸ‡ͺ (or somewhere travelling) -

+