From f5f911c30e47b493a4441b6a667c137b5ff34c11 Mon Sep 17 00:00:00 2001 From: TZGyn Date: Mon, 8 Jan 2024 22:49:46 +0800 Subject: [PATCH] added qr color customization --- frontend/drizzle/0004_cynical_the_hand.sql | 5 + frontend/drizzle/meta/0004_snapshot.json | 256 ++++++++++++++++++ frontend/drizzle/meta/_journal.json | 7 + frontend/src/lib/db/schema.ts | 15 +- frontend/src/lib/server/types.ts | 5 + .../src/routes/(app)/links/+page.server.ts | 6 +- frontend/src/routes/(app)/links/+page.svelte | 5 +- .../src/routes/(app)/settings/+layout.svelte | 8 +- .../src/routes/(app)/settings/+page.server.ts | 6 + .../src/routes/(app)/settings/+page.svelte | 15 - .../settings/qr/(components)/DemoQR.svelte | 37 +++ .../routes/(app)/settings/qr/+page.server.ts | 10 + .../src/routes/(app)/settings/qr/+page.svelte | 87 ++++++ frontend/src/routes/api/account/qr/+server.ts | 32 +++ 14 files changed, 472 insertions(+), 22 deletions(-) create mode 100644 frontend/drizzle/0004_cynical_the_hand.sql create mode 100644 frontend/drizzle/meta/0004_snapshot.json create mode 100644 frontend/src/routes/(app)/settings/+page.server.ts delete mode 100644 frontend/src/routes/(app)/settings/+page.svelte create mode 100644 frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte create mode 100644 frontend/src/routes/(app)/settings/qr/+page.server.ts create mode 100644 frontend/src/routes/(app)/settings/qr/+page.svelte create mode 100644 frontend/src/routes/api/account/qr/+server.ts diff --git a/frontend/drizzle/0004_cynical_the_hand.sql b/frontend/drizzle/0004_cynical_the_hand.sql new file mode 100644 index 0000000..b152a5c --- /dev/null +++ b/frontend/drizzle/0004_cynical_the_hand.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS "setting" ( + "user_id" integer NOT NULL, + "qr_background" varchar(7), + "qr_foreground" varchar(7) +); diff --git a/frontend/drizzle/meta/0004_snapshot.json b/frontend/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..b803f32 --- /dev/null +++ b/frontend/drizzle/meta/0004_snapshot.json @@ -0,0 +1,256 @@ +{ + "id": "6fa088cf-5f05-40aa-bb38-94848c4feb92", + "prevId": "9a3733db-b0ef-4744-b0bf-f69e9b03917c", + "version": "5", + "dialect": "pg", + "tables": { + "project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "setting": { + "name": "setting", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "qr_background": { + "name": "qr_background", + "type": "varchar(7)", + "primaryKey": false, + "notNull": false + }, + "qr_foreground": { + "name": "qr_foreground", + "type": "varchar(7)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "shortener": { + "name": "shortener", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "link": { + "name": "link", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + } + }, + "visitor": { + "name": "visitor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "shortener_id": { + "name": "shortener_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "country_code": { + "name": "country_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "city": { + "name": "city", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/frontend/drizzle/meta/_journal.json b/frontend/drizzle/meta/_journal.json index a5c087d..79f7260 100644 --- a/frontend/drizzle/meta/_journal.json +++ b/frontend/drizzle/meta/_journal.json @@ -29,6 +29,13 @@ "when": 1701590526323, "tag": "0003_glorious_norrin_radd", "breakpoints": true + }, + { + "idx": 4, + "version": "5", + "when": 1704723435338, + "tag": "0004_cynical_the_hand", + "breakpoints": true } ] } \ No newline at end of file diff --git a/frontend/src/lib/db/schema.ts b/frontend/src/lib/db/schema.ts index 2a8a2a3..3b688ba 100644 --- a/frontend/src/lib/db/schema.ts +++ b/frontend/src/lib/db/schema.ts @@ -64,6 +64,8 @@ export const user = pgTable('user', { .notNull(), }) +export type User = InferSelectModel + export const visitor = pgTable('visitor', { id: serial('id').primaryKey().notNull(), shortenerId: integer('shortener_id').notNull(), @@ -99,4 +101,15 @@ export const sessionRelations = relations(session, ({ one }) => ({ }), })) -export type User = InferSelectModel +export const setting = pgTable('setting', { + userId: integer('user_id').notNull(), + qr_background: varchar('qr_background', { length: 7 }), + qr_foreground: varchar('qr_foreground', { length: 7 }), +}) + +export const settingRelations = relations(setting, ({ one }) => ({ + user: one(user, { + fields: [setting.userId], + references: [user.id], + }), +})) diff --git a/frontend/src/lib/server/types.ts b/frontend/src/lib/server/types.ts index f1a000e..cec96e2 100644 --- a/frontend/src/lib/server/types.ts +++ b/frontend/src/lib/server/types.ts @@ -12,3 +12,8 @@ export const userCreateSchema = z.object({ password: z.string(), password_confirm: z.string(), }) + +export const qrUpdateSchema = z.object({ + qr_background: z.string().max(7), + qr_foreground: z.string().max(7), +}) diff --git a/frontend/src/routes/(app)/links/+page.server.ts b/frontend/src/routes/(app)/links/+page.server.ts index 6831290..7faed2d 100644 --- a/frontend/src/routes/(app)/links/+page.server.ts +++ b/frontend/src/routes/(app)/links/+page.server.ts @@ -43,5 +43,9 @@ export const load = (async (event) => { where: (project, { eq }) => eq(project.userId, user.id), }) - return { shorteners, projects, selected_project } + const settings = await db.query.setting.findFirst({ + where: (settings, { eq }) => eq(settings.userId, user.id), + }) + + return { shorteners, projects, selected_project, settings } }) satisfies PageServerLoad diff --git a/frontend/src/routes/(app)/links/+page.svelte b/frontend/src/routes/(app)/links/+page.svelte index eacc19d..f6365d0 100644 --- a/frontend/src/routes/(app)/links/+page.svelte +++ b/frontend/src/routes/(app)/links/+page.svelte @@ -282,7 +282,10 @@ {data.shortener_url + '/' + qrCode} - + diff --git a/frontend/src/routes/(app)/settings/+layout.svelte b/frontend/src/routes/(app)/settings/+layout.svelte index d576da4..757fc46 100644 --- a/frontend/src/routes/(app)/settings/+layout.svelte +++ b/frontend/src/routes/(app)/settings/+layout.svelte @@ -3,14 +3,14 @@ import SidebarNav from './(components)/sidebar-nav.svelte' const sidebarNavItems = [ - { - title: 'Profile', - href: '/settings', - }, { title: 'Account', href: '/settings/account', }, + { + title: 'QR', + href: '/settings/qr', + }, ] diff --git a/frontend/src/routes/(app)/settings/+page.server.ts b/frontend/src/routes/(app)/settings/+page.server.ts new file mode 100644 index 0000000..39f30d7 --- /dev/null +++ b/frontend/src/routes/(app)/settings/+page.server.ts @@ -0,0 +1,6 @@ +import { redirect } from '@sveltejs/kit' +import type { PageServerLoad } from './$types' + +export const load = (async () => { + throw redirect(300, '/settings/account') +}) satisfies PageServerLoad diff --git a/frontend/src/routes/(app)/settings/+page.svelte b/frontend/src/routes/(app)/settings/+page.svelte deleted file mode 100644 index 6dbe5c9..0000000 --- a/frontend/src/routes/(app)/settings/+page.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - -
-
-

Profile

-

- This is how others will see you on the site. -

-
- -
diff --git a/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte b/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte new file mode 100644 index 0000000..0d94107 --- /dev/null +++ b/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte @@ -0,0 +1,37 @@ + + +{value} diff --git a/frontend/src/routes/(app)/settings/qr/+page.server.ts b/frontend/src/routes/(app)/settings/qr/+page.server.ts new file mode 100644 index 0000000..5f64555 --- /dev/null +++ b/frontend/src/routes/(app)/settings/qr/+page.server.ts @@ -0,0 +1,10 @@ +import { db } from '$lib/db' +import type { PageServerLoad } from './$types' + +export const load = (async (event) => { + const settings = await db.query.setting.findFirst({ + where: (setting, { eq }) => + eq(setting.userId, event.locals.userObject.id), + }) + return { settings } +}) satisfies PageServerLoad diff --git a/frontend/src/routes/(app)/settings/qr/+page.svelte b/frontend/src/routes/(app)/settings/qr/+page.svelte new file mode 100644 index 0000000..0e5aaf7 --- /dev/null +++ b/frontend/src/routes/(app)/settings/qr/+page.svelte @@ -0,0 +1,87 @@ + + +
+
+

Account

+

+ Update your account settings. +

+
+ + + {#key qr_data} + + {/key} + +
+ + +
+ +
+ + +
+ + +
diff --git a/frontend/src/routes/api/account/qr/+server.ts b/frontend/src/routes/api/account/qr/+server.ts new file mode 100644 index 0000000..e372505 --- /dev/null +++ b/frontend/src/routes/api/account/qr/+server.ts @@ -0,0 +1,32 @@ +import { db } from '$lib/db' +import { setting } from '$lib/db/schema' +import { qrUpdateSchema } from '$lib/server/types' +import { eq } from 'drizzle-orm' +import type { RequestHandler } from './$types' + +export const PUT: RequestHandler = async (event) => { + const body = await event.request.json() + const qrUpdate = qrUpdateSchema.safeParse(body) + const userId = event.locals.userObject.id + + if (!qrUpdate.success) { + return new Response(JSON.stringify({ success: false })) + } + + try { + const settings = await db.query.setting.findFirst({ + where: (settingData, { eq }) => eq(settingData.userId, userId), + }) + + if (!settings) { + await db.insert(setting).values({ userId }) + } + await db + .update(setting) + .set(qrUpdate.data) + .where(eq(setting.userId, userId)) + return new Response(JSON.stringify({ success: true })) + } catch (error) { + return new Response(JSON.stringify({ success: false })) + } +}