mirror of https://github.com/TZGyn/shortener
added qr color customization
parent
9cb2243c00
commit
f5f911c30e
@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS "setting" (
|
||||
"user_id" integer NOT NULL,
|
||||
"qr_background" varchar(7),
|
||||
"qr_foreground" varchar(7)
|
||||
);
|
||||
@ -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": {}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -1,15 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types'
|
||||
import { Separator } from '$lib/components/ui/separator'
|
||||
export let data: PageData
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium">Profile</h3>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
This is how others will see you on the site.
|
||||
</p>
|
||||
</div>
|
||||
<Separator />
|
||||
</div>
|
||||
@ -0,0 +1,37 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { default as QrCode } from 'qrious'
|
||||
|
||||
export let errorCorrection = 'L'
|
||||
export let background = '#fff'
|
||||
export let color = '#000'
|
||||
export let size = '300'
|
||||
export let value = 'example.com/abcdefgh'
|
||||
export let padding = 10
|
||||
export let className = 'qrcode'
|
||||
|
||||
let image = ''
|
||||
|
||||
function generateQrCode() {
|
||||
if (!document || !window) {
|
||||
return
|
||||
}
|
||||
const QRcode = new QrCode()
|
||||
QRcode.set({
|
||||
background,
|
||||
foreground: color,
|
||||
level: errorCorrection,
|
||||
padding,
|
||||
size,
|
||||
value,
|
||||
})
|
||||
|
||||
image = QRcode.toDataURL()
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
generateQrCode()
|
||||
})
|
||||
</script>
|
||||
|
||||
<img src={image} alt={value} class={className} />
|
||||
@ -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
|
||||
@ -0,0 +1,87 @@
|
||||
<script lang="ts">
|
||||
import { Separator } from '$lib/components/ui/separator'
|
||||
import { Input } from '$lib/components/ui/input'
|
||||
import { Label } from '$lib/components/ui/label'
|
||||
import type { PageData } from './$types'
|
||||
import Button from '$lib/components/ui/button/button.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { invalidateAll } from '$app/navigation'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import DemoQr from './(components)/DemoQR.svelte'
|
||||
|
||||
export let data: PageData
|
||||
|
||||
let isLoading = false
|
||||
|
||||
let qr_data = {
|
||||
background_color: data.settings?.qr_background || '#fff',
|
||||
foreground_color: data.settings?.qr_foreground || '#000',
|
||||
}
|
||||
|
||||
console.log(qr_data)
|
||||
|
||||
const submit = async () => {
|
||||
isLoading = true
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/account/qr', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
qr_background: qr_data.background_color,
|
||||
qr_foreground: qr_data.foreground_color,
|
||||
}),
|
||||
})
|
||||
|
||||
const body = await response.json()
|
||||
|
||||
if (body.success) {
|
||||
toast.success('Account Details Updated')
|
||||
await invalidateAll()
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Error Occurred')
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium">Account</h3>
|
||||
<p class="text-muted-foreground text-sm">
|
||||
Update your account settings.
|
||||
</p>
|
||||
</div>
|
||||
<Separator />
|
||||
|
||||
{#key qr_data}
|
||||
<DemoQr
|
||||
background={qr_data.background_color}
|
||||
color={qr_data.foreground_color} />
|
||||
{/key}
|
||||
|
||||
<div class="flex w-full max-w-sm flex-col gap-2">
|
||||
<Label for="background_color">Background Color</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="background_color"
|
||||
placeholder="#ffffff"
|
||||
bind:value={qr_data.background_color} />
|
||||
</div>
|
||||
|
||||
<div class="flex w-full max-w-sm flex-col gap-2">
|
||||
<Label for="foreground_color">Foreground Color</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="foreground_color"
|
||||
placeholder="#000000"
|
||||
bind:value={qr_data.foreground_color} />
|
||||
</div>
|
||||
|
||||
<Button disabled={isLoading} on:click={submit} class="flex gap-2">
|
||||
{#if isLoading}
|
||||
<Loader2 class="animate-spin" />
|
||||
{/if}
|
||||
Save</Button>
|
||||
</div>
|
||||
@ -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 }))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue