diff --git a/app/app.vue b/app/app.vue index fed55a9..8dd09d3 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,7 +1,5 @@ - - - + diff --git a/app/components/LinkModal.vue b/app/components/LinkModal.vue new file mode 100644 index 0000000..cbe47ee --- /dev/null +++ b/app/components/LinkModal.vue @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + Cancel + {{ link ? "Save" : "Create" }} + + + + + diff --git a/app/components/LinksTable.vue b/app/components/LinksTable.vue new file mode 100644 index 0000000..c9eb203 --- /dev/null +++ b/app/components/LinksTable.vue @@ -0,0 +1,63 @@ + + + + + + {{ status === "pending" || status === "idle" ? "Loading..." : "No data" }} + + + diff --git a/app/pages/index.vue b/app/pages/index.vue index 7bd7df4..c025254 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -1,3 +1,90 @@ + + - ok + + + + + + + + + + + + + + + New link + + + + + + + + + + diff --git a/nuxt.config.ts b/nuxt.config.ts index 3985560..a91edac 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -5,17 +5,21 @@ export default defineNuxtConfig({ '@nuxt/ui' ], + vite: { + optimizeDeps: { + include: [ + 'zod', + ] + } + }, + devtools: { enabled: true }, css: ['~/assets/css/main.css'], - routeRules: { - '/': { prerender: true } - }, - - compatibilityDate: '2025-01-15', +compatibilityDate: '2025-01-15', eslint: { config: { diff --git a/package.json b/package.json index df801f1..4730179 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "zod": "^4.3.6" }, "devDependencies": { + "@iconify-json/lucide": "^1.2.98", "@nuxt/eslint": "^1.15.2", "@types/better-sqlite3": "^7.6.13", "drizzle-kit": "^0.31.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78cbfd4..89d7771 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,9 @@ importers: specifier: ^4.3.6 version: 4.3.6 devDependencies: + '@iconify-json/lucide': + specifier: ^1.2.98 + version: 1.2.98 '@nuxt/eslint': specifier: ^1.15.2 version: 1.15.2(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.30)(eslint@10.0.3(jiti@2.6.1))(magicast@0.5.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) @@ -767,6 +770,9 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify-json/lucide@1.2.98': + resolution: {integrity: sha512-Lx2464W8Tty/QEnZ2UPb73nPdML/HpGCj0J0w37jP3/jx3l4fniZBjDxe1TgHiIL5XW9QO3vlx53ZQZ5JsNpzQ==} + '@iconify/collections@1.0.661': resolution: {integrity: sha512-r5dwSQHdwNxfjx8AZiHfn8IuafVYTl850xzN9gKVMi8L9sXyd/XNawTr1qIJQZbJxouCETielBRP8TM9YmiSEA==} @@ -6083,6 +6089,10 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify-json/lucide@1.2.98': + dependencies: + '@iconify/types': 2.0.0 + '@iconify/collections@1.0.661': dependencies: '@iconify/types': 2.0.0 diff --git a/server/api/links/[id].delete.ts b/server/api/links/[id]/index.delete.ts similarity index 93% rename from server/api/links/[id].delete.ts rename to server/api/links/[id]/index.delete.ts index fba5750..ff37b6d 100644 --- a/server/api/links/[id].delete.ts +++ b/server/api/links/[id]/index.delete.ts @@ -4,7 +4,7 @@ import { eq } from "drizzle-orm"; import { z } from "zod"; const paramsSchema = z.object({ - id: z.number(), + id: z.string().transform(Number), }); export default defineEventHandler(async (event) => { diff --git a/server/api/links/[id].get.ts b/server/api/links/[id]/index.get.ts similarity index 93% rename from server/api/links/[id].get.ts rename to server/api/links/[id]/index.get.ts index 3763228..234a52e 100644 --- a/server/api/links/[id].get.ts +++ b/server/api/links/[id]/index.get.ts @@ -4,7 +4,7 @@ import { eq } from "drizzle-orm"; import { z } from "zod"; const paramsSchema = z.object({ - id: z.number(), + id: z.string().transform(Number), }); export default defineEventHandler(async (event) => { diff --git a/server/api/links/[id].patch.ts b/server/api/links/[id]/index.patch.ts similarity index 95% rename from server/api/links/[id].patch.ts rename to server/api/links/[id]/index.patch.ts index 6eac284..6b7ded6 100644 --- a/server/api/links/[id].patch.ts +++ b/server/api/links/[id]/index.patch.ts @@ -4,7 +4,7 @@ import { eq } from "drizzle-orm"; import { z } from "zod"; const paramsSchema = z.object({ - id: z.number(), + id: z.string().transform(Number), }); const bodySchema = z.object({ diff --git a/server/api/links/index.post.ts b/server/api/links/index.post.ts index 55d5920..b8df6db 100644 --- a/server/api/links/index.post.ts +++ b/server/api/links/index.post.ts @@ -5,7 +5,8 @@ import { z } from "zod"; const bodySchema = z.object({ name: z.string().min(1), path: z.string().min(1), - url: z.url().min(1), + url: z.url(), + disabled: z.boolean().optional(), }); export default defineEventHandler(async (event) => { diff --git a/server/db/migrations/0001_tense_grey_gargoyle.sql b/server/db/migrations/0001_tense_grey_gargoyle.sql new file mode 100644 index 0000000..04e262b --- /dev/null +++ b/server/db/migrations/0001_tense_grey_gargoyle.sql @@ -0,0 +1 @@ +ALTER TABLE `links` ADD `disabled` integer DEFAULT false NOT NULL; \ No newline at end of file diff --git a/server/db/migrations/meta/0001_snapshot.json b/server/db/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..f7fc2f5 --- /dev/null +++ b/server/db/migrations/meta/0001_snapshot.json @@ -0,0 +1,86 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "0e8befef-3e4f-43f1-a031-95c2ca514e30", + "prevId": "d03860ad-0d5c-4943-92ba-d704c74e1501", + "tables": { + "links": { + "name": "links", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": { + "links_name_unique": { + "name": "links_name_unique", + "columns": [ + "name" + ], + "isUnique": true + }, + "links_path_unique": { + "name": "links_path_unique", + "columns": [ + "path" + ], + "isUnique": true + }, + "links_url_unique": { + "name": "links_url_unique", + "columns": [ + "url" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/server/db/migrations/meta/_journal.json b/server/db/migrations/meta/_journal.json index f64968d..64a5003 100644 --- a/server/db/migrations/meta/_journal.json +++ b/server/db/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1773788954273, "tag": "0000_pretty_mentor", "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1773792538248, + "tag": "0001_tense_grey_gargoyle", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/db/schema.ts b/server/db/schema.ts index 167f2a9..39c6f8f 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -5,4 +5,5 @@ export const links = sqliteTable("links", { name: text("name").notNull().unique(), path: text("path").notNull().unique(), url: text("url").notNull().unique(), + disabled: integer("disabled", { mode: "boolean" }).notNull().default(false), }); diff --git a/shared/types/app.ts b/shared/types/app.ts new file mode 100644 index 0000000..b0ab15b --- /dev/null +++ b/shared/types/app.ts @@ -0,0 +1,3 @@ +import type { InternalApi } from "nitropack/types"; + +export type Link = InternalApi["/api/links"]["get"][number];