diff --git a/drizzle/0008_common_supreme_intelligence.sql b/drizzle/0008_common_supreme_intelligence.sql new file mode 100644 index 0000000..cde9457 --- /dev/null +++ b/drizzle/0008_common_supreme_intelligence.sql @@ -0,0 +1,15 @@ +CREATE TABLE "location" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "latitude" text NOT NULL, + "longitude" text NOT NULL, + "location_name" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "find" ADD COLUMN "location_id" text NOT NULL;--> statement-breakpoint +ALTER TABLE "location" ADD CONSTRAINT "location_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "find" ADD CONSTRAINT "find_location_id_location_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."location"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "find" DROP COLUMN "latitude";--> statement-breakpoint +ALTER TABLE "find" DROP COLUMN "longitude";--> statement-breakpoint +ALTER TABLE "find" DROP COLUMN "location_name"; \ No newline at end of file diff --git a/drizzle/meta/0008_snapshot.json b/drizzle/meta/0008_snapshot.json new file mode 100644 index 0000000..646cffe --- /dev/null +++ b/drizzle/meta/0008_snapshot.json @@ -0,0 +1,829 @@ +{ + "id": "5654d58b-23f8-48cb-9933-5ac32141b75e", + "prevId": "1dbab94c-004e-4d34-b171-408bb1d36c91", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.find": { + "name": "find", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "location_id": { + "name": "location_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "find_location_id_location_id_fk": { + "name": "find_location_id_location_id_fk", + "tableFrom": "find", + "tableTo": "location", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "find_user_id_user_id_fk": { + "name": "find_user_id_user_id_fk", + "tableFrom": "find", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.find_comment": { + "name": "find_comment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "find_id": { + "name": "find_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "find_comment_find_id_find_id_fk": { + "name": "find_comment_find_id_find_id_fk", + "tableFrom": "find_comment", + "tableTo": "find", + "columnsFrom": [ + "find_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "find_comment_user_id_user_id_fk": { + "name": "find_comment_user_id_user_id_fk", + "tableFrom": "find_comment", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.find_like": { + "name": "find_like", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "find_id": { + "name": "find_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "find_like_find_id_find_id_fk": { + "name": "find_like_find_id_find_id_fk", + "tableFrom": "find_like", + "tableTo": "find", + "columnsFrom": [ + "find_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "find_like_user_id_user_id_fk": { + "name": "find_like_user_id_user_id_fk", + "tableFrom": "find_like", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.find_media": { + "name": "find_media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "find_id": { + "name": "find_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fallback_url": { + "name": "fallback_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fallback_thumbnail_url": { + "name": "fallback_thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "order_index": { + "name": "order_index", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "find_media_find_id_find_id_fk": { + "name": "find_media_find_id_find_id_fk", + "tableFrom": "find_media", + "tableTo": "find", + "columnsFrom": [ + "find_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.friendship": { + "name": "friendship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "friend_id": { + "name": "friend_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "friendship_user_id_user_id_fk": { + "name": "friendship_user_id_user_id_fk", + "tableFrom": "friendship", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "friendship_friend_id_user_id_fk": { + "name": "friendship_friend_id_user_id_fk", + "tableFrom": "friendship", + "tableTo": "user", + "columnsFrom": [ + "friend_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.location": { + "name": "location", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "latitude": { + "name": "latitude", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "longitude": { + "name": "longitude", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location_name": { + "name": "location_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "location_user_id_user_id_fk": { + "name": "location_user_id_user_id_fk", + "tableFrom": "location", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_user_id_user_id_fk": { + "name": "notification_user_id_user_id_fk", + "tableFrom": "notification", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_preferences": { + "name": "notification_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "friend_requests": { + "name": "friend_requests", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "friend_accepted": { + "name": "friend_accepted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "find_liked": { + "name": "find_liked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "find_commented": { + "name": "find_commented", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "push_enabled": { + "name": "push_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_preferences_user_id_user_id_fk": { + "name": "notification_preferences_user_id_user_id_fk", + "tableFrom": "notification_preferences", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_subscription": { + "name": "notification_subscription", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "p256dh_key": { + "name": "p256dh_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_key": { + "name": "auth_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_subscription_user_id_user_id_fk": { + "name": "notification_subscription_user_id_user_id_fk", + "tableFrom": "notification_subscription", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profile_picture_url": { + "name": "profile_picture_url", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_username_unique": { + "name": "user_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "user_google_id_unique": { + "name": "user_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index f4867dc..59d6529 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -57,6 +57,13 @@ "when": 1762522687342, "tag": "0007_grey_dark_beast", "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1765885558230, + "tag": "0008_common_supreme_intelligence", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/components/finds/CreateFindModal.svelte b/src/lib/components/finds/CreateFindModal.svelte index 43ba23d..bf9b178 100644 --- a/src/lib/components/finds/CreateFindModal.svelte +++ b/src/lib/components/finds/CreateFindModal.svelte @@ -14,7 +14,6 @@ let title = $state(''); let description = $state(''); - let locationName = $state(''); let category = $state('cafe'); let isPublic = $state(true); let selectedFiles = $state(null); @@ -93,7 +92,6 @@ locationId, title: title.trim(), description: description.trim() || null, - locationName: locationName.trim() || null, category, isPublic, media: uploadedMedia @@ -118,7 +116,6 @@ function resetForm() { title = ''; description = ''; - locationName = ''; category = 'cafe'; isPublic = true; selectedFiles = null; @@ -177,15 +174,6 @@ > -
- - -
-
diff --git a/src/lib/components/locations/CreateLocationModal.svelte b/src/lib/components/locations/CreateLocationModal.svelte index b5b65ea..d166269 100644 --- a/src/lib/components/locations/CreateLocationModal.svelte +++ b/src/lib/components/locations/CreateLocationModal.svelte @@ -16,6 +16,7 @@ let latitude = $state(''); let longitude = $state(''); + let locationName = $state(''); let isSubmitting = $state(false); let useManualLocation = $state(false); @@ -59,7 +60,8 @@ }, body: JSON.stringify({ latitude: lat, - longitude: lng + longitude: lng, + locationName: locationName.trim() || null }) }); @@ -85,6 +87,7 @@ } function handlePlaceSelected(place: PlaceResult) { + locationName = place.name; latitude = place.latitude.toString(); longitude = place.longitude.toString(); } @@ -100,6 +103,7 @@ function resetForm() { latitude = ''; longitude = ''; + locationName = ''; useManualLocation = false; } @@ -158,6 +162,16 @@ {/if}
+
+ + +
+ {#if useManualLocation || (!latitude && !longitude)}
diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index b12a956..e622429 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -29,6 +29,7 @@ export const location = pgTable('location', { .references(() => user.id, { onDelete: 'cascade' }), latitude: text('latitude').notNull(), // Using text for precision longitude: text('longitude').notNull(), // Using text for precision + locationName: text('location_name'), // e.g., "Café Belga, Brussels" createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).defaultNow().notNull() }); @@ -43,7 +44,6 @@ export const find = pgTable('find', { .references(() => user.id, { onDelete: 'cascade' }), title: text('title').notNull(), description: text('description'), - locationName: text('location_name'), // e.g., "Café Belga, Brussels" category: text('category'), // e.g., "cafe", "restaurant", "park", "landmark" isPublic: integer('is_public').default(1), // Using integer for boolean (1 = true, 0 = false) createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' }).defaultNow().notNull(), diff --git a/src/routes/api/finds/+server.ts b/src/routes/api/finds/+server.ts index f2cd4f3..40fbc01 100644 --- a/src/routes/api/finds/+server.ts +++ b/src/routes/api/finds/+server.ts @@ -70,7 +70,7 @@ export const GET: RequestHandler = async ({ url, locals }) => { locationId: find.locationId, title: find.title, description: find.description, - locationName: find.locationName, + locationName: location.locationName, category: find.category, isPublic: find.isPublic, createdAt: find.createdAt, @@ -98,9 +98,10 @@ export const GET: RequestHandler = async ({ url, locals }) => { }) .from(find) .innerJoin(user, eq(find.userId, user.id)) + .innerJoin(location, eq(find.locationId, location.id)) .leftJoin(findLike, eq(find.id, findLike.findId)) .where(whereConditions) - .groupBy(find.id, user.username, user.profilePictureUrl) + .groupBy(find.id, user.username, user.profilePictureUrl, location.locationName) .orderBy(order === 'desc' ? desc(find.createdAt) : find.createdAt); // Get media for all finds @@ -198,7 +199,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { } const data = await request.json(); - const { locationId, title, description, locationName, category, isPublic, media } = data; + const { locationId, title, description, category, isPublic, media } = data; if (!title || !locationId) { throw error(400, 'Title and locationId are required'); @@ -234,7 +235,6 @@ export const POST: RequestHandler = async ({ request, locals }) => { userId: locals.user.id, title, description, - locationName, category, isPublic: isPublic ? 1 : 0 }) diff --git a/src/routes/api/locations/+server.ts b/src/routes/api/locations/+server.ts index bf2f4ac..de34565 100644 --- a/src/routes/api/locations/+server.ts +++ b/src/routes/api/locations/+server.ts @@ -66,6 +66,7 @@ export const GET: RequestHandler = async ({ url, locals }) => { id: location.id, latitude: location.latitude, longitude: location.longitude, + locationName: location.locationName, createdAt: location.createdAt, userId: location.userId, username: user.username, @@ -109,7 +110,6 @@ export const GET: RequestHandler = async ({ url, locals }) => { id: find.id, title: find.title, description: find.description, - locationName: find.locationName, category: find.category, isPublic: find.isPublic, createdAt: find.createdAt, @@ -239,7 +239,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { } const data = await request.json(); - const { latitude, longitude } = data; + const { latitude, longitude, locationName } = data; if (!latitude || !longitude) { throw error(400, 'Latitude and longitude are required'); @@ -254,7 +254,8 @@ export const POST: RequestHandler = async ({ request, locals }) => { id: locationId, userId: locals.user.id, latitude: latitude.toString(), - longitude: longitude.toString() + longitude: longitude.toString(), + locationName: locationName || null }) .returning();