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}
+
+ )
+ }
+
+
+
+
+
+
+
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)
-
+