diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index f146c18..172003f 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -1,24 +1,26 @@
-import { BookmarkCard } from '@/components/bookmarkCard'
-import { bookmarkSchema } from '@/types'
+import { bookmarkCategoryWithBookmarksSchema } from '@/types'
import { bookmark } from '@/lib/schema'
import { db } from '@/lib/db'
import EditBookmarkForm from '@/components/editBookmarkForm'
+import BookmarkTabs from '@/components/tabs'
export const dynamic = 'force-dynamic'
export const fetchCache = 'force-no-store'
export default async function DashboardPage() {
- const data = await db.select().from(bookmark)
+ const data = await db.query.bookmarkCategory.findMany({
+ with: {
+ bookmark: true,
+ },
+ })
+
+ const bookmarkCategoryWithBookmarks = bookmarkCategoryWithBookmarksSchema
+ .array()
+ .parse(data)
- const bookmarks = bookmarkSchema.array().parse(data)
return (
-
- {bookmarks.map((data, index) => (
-
- ))}
+
+
)
diff --git a/components/newBookmarkForm.tsx b/components/newBookmarkForm.tsx
index 5f793fa..798aa80 100644
--- a/components/newBookmarkForm.tsx
+++ b/components/newBookmarkForm.tsx
@@ -24,6 +24,7 @@ export default function NewBookmarkForm() {
link,
description,
url,
+ category_id: 2,
}
await fetch('/api/bookmark', {
method: 'POST',
diff --git a/components/tabs.tsx b/components/tabs.tsx
new file mode 100644
index 0000000..e16aabb
--- /dev/null
+++ b/components/tabs.tsx
@@ -0,0 +1,34 @@
+'use client'
+
+import { BookmarkCategoryWithBookmarks } from '@/types'
+import { Tabs, Tab } from '@nextui-org/tabs'
+import { BookmarkCard } from './bookmarkCard'
+
+export default function BookmarkTabs({
+ data,
+}: {
+ data: BookmarkCategoryWithBookmarks[]
+}) {
+ return (
+
+
+ {data.map((bookmarkCategory, index) => (
+
+
+ {bookmarkCategory.bookmark.map((data, index) => (
+
+ ))}
+
+
+ ))}
+
+
+ )
+}
diff --git a/drizzle/0001_busy_legion.sql b/drizzle/0001_busy_legion.sql
new file mode 100644
index 0000000..167f9cf
--- /dev/null
+++ b/drizzle/0001_busy_legion.sql
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS "bookmark_categories" (
+ "id" serial PRIMARY KEY NOT NULL,
+ "name" text
+);
+--> statement-breakpoint
+ALTER TABLE "bookmarks" ADD COLUMN "category_id" integer;
\ No newline at end of file
diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json
new file mode 100644
index 0000000..1c29e8c
--- /dev/null
+++ b/drizzle/meta/0001_snapshot.json
@@ -0,0 +1,83 @@
+{
+ "version": "5",
+ "dialect": "pg",
+ "id": "288ceda5-82ca-464f-a1f1-ab4e1aad475d",
+ "prevId": "e3dded07-6461-44e7-9c2e-e9e750dfcbd7",
+ "tables": {
+ "bookmarks": {
+ "name": "bookmarks",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "category_id": {
+ "name": "category_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "link": {
+ "name": "link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "bookmark_categories": {
+ "name": "bookmark_categories",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index b99cb0b..eb31c3c 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -8,6 +8,13 @@
"when": 1691820164272,
"tag": "0000_charming_warstar",
"breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "5",
+ "when": 1692095367785,
+ "tag": "0001_busy_legion",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/lib/schema.ts b/lib/schema.ts
index 46fda81..8e65468 100644
--- a/lib/schema.ts
+++ b/lib/schema.ts
@@ -20,19 +20,36 @@ import {
// export const userRelations = relations(user, ({ many }) => ({
// bookmark: many(bookmark),
// }))
-//
+
export const bookmark = pgTable('bookmarks', {
id: serial('id').primaryKey(),
// userId: integer('user_id').references(() => user.id),
+ category_id: integer('category_id'),
name: text('name'),
link: text('link'),
description: text('description'),
url: text('url'),
})
-// export const bookmarkRelations = relations(bookmark, ({ one }) => ({
-// user: one(user, {
-// fields: [bookmark.userId],
-// references: [user.id],
-// }),
-// }))
+export const bookmarkRelations = relations(bookmark, ({ one }) => ({
+ // user: one(user, {
+ // fields: [bookmark.userId],
+ // references: [user.id],
+ // }),
+ bookmarkCategory: one(bookmarkCategory, {
+ fields: [bookmark.category_id],
+ references: [bookmarkCategory.id],
+ }),
+}))
+
+export const bookmarkCategory = pgTable('bookmark_categories', {
+ id: serial('id').primaryKey(),
+ name: text('name'),
+})
+
+export const bookmarkCategoryRelations = relations(
+ bookmarkCategory,
+ ({ many }) => ({
+ bookmark: many(bookmark),
+ })
+)
diff --git a/package.json b/package.json
index c7b7f2f..d630676 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@nextui-org/snippet": "2.0.0",
"@nextui-org/switch": "2.0.0",
"@nextui-org/system": "2.0.0",
+ "@nextui-org/tabs": "^2.0.11",
"@nextui-org/theme": "2.0.0",
"@react-aria/ssr": "^3.6.0",
"@react-aria/visually-hidden": "^3.8.1",
@@ -39,6 +40,7 @@
"intl-messageformat": "^10.1.0",
"next": "13.4.9",
"next-themes": "^0.2.1",
+ "pocketbase": "^0.16.0",
"postcss": "8.4.24",
"postgres": "^3.3.5",
"react": "18.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8eb6697..8e320ff 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -50,6 +50,9 @@ dependencies:
'@nextui-org/system':
specifier: 2.0.0
version: 2.0.0(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2)
+ '@nextui-org/tabs':
+ specifier: ^2.0.11
+ version: 2.0.11(framer-motion@10.12.16)(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2)
'@nextui-org/theme':
specifier: 2.0.0
version: 2.0.0(tailwindcss@3.3.2)
@@ -95,6 +98,9 @@ dependencies:
next-themes:
specifier: ^0.2.1
version: 0.2.1(next@13.4.9)(react-dom@18.2.0)(react@18.2.0)
+ pocketbase:
+ specifier: ^0.16.0
+ version: 0.16.0
postcss:
specifier: 8.4.24
version: 8.4.24
@@ -1269,6 +1275,10 @@ packages:
resolution: {integrity: sha512-hYGZ6vgyF1QhatU1E3BhhOy/sOrw5D813lXD/bQ+YkSAS5eRhhWFgpka+PLiZGq8GWAyXKfsOuRucdoGSo4g1w==}
dev: false
+ /@nextui-org/react-rsc-utils@2.0.6:
+ resolution: {integrity: sha512-32hglPfvA77lFO1XbDBf8a/bVthRjFTWWsYAMGWFF4GKhl/WYBzVRaFmIQjimd6FOfLFhPvolWHEukH3kTlyyQ==}
+ dev: false
+
/@nextui-org/react-utils@2.0.0(react@18.2.0):
resolution: {integrity: sha512-CFAsbxv1Dw9Hp6x6wvUuhgyqP4w4cFgMva5YhLaWE/5gH9c1PpHRMtTvxouA523r1iRDRuImyNJeSinvvTZPkw==}
peerDependencies:
@@ -1309,6 +1319,16 @@ packages:
react: 18.2.0
dev: false
+ /@nextui-org/react-utils@2.0.6(react@18.2.0):
+ resolution: {integrity: sha512-9mRDJj1hNNdKTxt9idQc/OVI+3UzvSErRCjN2hfVVi2c5JrR5b0/Zpx7PV0PHy7yfGUn1hyvwK5ZTVkNhxISKQ==}
+ peerDependencies:
+ react: '>=18'
+ dependencies:
+ '@nextui-org/react-rsc-utils': 2.0.6
+ '@nextui-org/shared-utils': 2.0.2(react@18.2.0)
+ react: 18.2.0
+ dev: false
+
/@nextui-org/ripple@2.0.0(framer-motion@10.12.16)(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2):
resolution: {integrity: sha512-9pHZwwRVkMVU8SjNBwhEpwX5Lwb2HXx53N4O0bVy63/yA6L+jt50ZBuL8k0F42Sx2ApdkIi6o8Y10yBqowthsg==}
peerDependencies:
@@ -1522,6 +1542,35 @@ packages:
- tailwindcss
dev: false
+ /@nextui-org/tabs@2.0.11(framer-motion@10.12.16)(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2):
+ resolution: {integrity: sha512-7LsOAuhDF1MscYM1l0v0S1l77cS6CT99C6Mr5VDu9VbFraSiB+I2sFAwlwkTRxYvrX5JVSwywyU3zvwQcJ/e+w==}
+ peerDependencies:
+ framer-motion: '>=4.0.0'
+ react: '>=18'
+ dependencies:
+ '@nextui-org/aria-utils': 2.0.5(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2)
+ '@nextui-org/framer-transitions': 2.0.5(framer-motion@10.12.16)(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2)
+ '@nextui-org/react-utils': 2.0.6(react@18.2.0)
+ '@nextui-org/shared-utils': 2.0.2(react@18.2.0)
+ '@nextui-org/system': 2.0.5(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2)
+ '@nextui-org/theme': 2.0.5(tailwindcss@3.3.2)
+ '@nextui-org/use-is-mounted': 2.0.2(react-dom@18.2.0)(react@18.2.0)
+ '@nextui-org/use-update-effect': 2.0.2(react@18.2.0)
+ '@react-aria/focus': 3.14.0(react@18.2.0)
+ '@react-aria/interactions': 3.17.0(react@18.2.0)
+ '@react-aria/tabs': 3.6.2(react@18.2.0)
+ '@react-aria/utils': 3.19.0(react@18.2.0)
+ '@react-stately/tabs': 3.5.1(react@18.2.0)
+ '@react-types/shared': 3.19.0(react@18.2.0)
+ '@react-types/tabs': 3.3.1(react@18.2.0)
+ framer-motion: 10.12.16(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ scroll-into-view-if-needed: 3.0.10
+ transitivePeerDependencies:
+ - react-dom
+ - tailwindcss
+ dev: false
+
/@nextui-org/theme@2.0.0(tailwindcss@3.3.2):
resolution: {integrity: sha512-Pe19Ts3xg9uOv/Azv1s6aUIQT16Fhc6ox//25zFCctQv5AwpW56gmt09wcQTCUXG+0niIvh2ODFZzKlN3v6cmQ==}
peerDependencies:
@@ -1558,6 +1607,24 @@ packages:
tailwindcss: 3.3.2
dev: false
+ /@nextui-org/theme@2.0.5(tailwindcss@3.3.2):
+ resolution: {integrity: sha512-4br6xRTYGbDCL8Vwm4CP0VqEXORROyQCmLSwNFaXr6s0vB5nX5tnJHqyDIvpq5Al7I2H8HMG2KOUHyhbaKOKFA==}
+ peerDependencies:
+ tailwindcss: '*'
+ dependencies:
+ color: 4.2.3
+ color2k: 2.0.2
+ deepmerge: 4.3.1
+ flat: 5.0.2
+ lodash.foreach: 4.5.0
+ lodash.get: 4.4.2
+ lodash.kebabcase: 4.1.1
+ lodash.mapkeys: 4.6.0
+ lodash.omit: 4.5.0
+ tailwind-variants: 0.1.13(tailwindcss@3.3.2)
+ tailwindcss: 3.3.2
+ dev: false
+
/@nextui-org/tooltip@2.0.0(framer-motion@10.12.16)(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.2):
resolution: {integrity: sha512-uY+NbZaWLevKAWZSpPPG3JXApMEXemZZmQnkPmKWLHBTxO3TNIcYDIePfQiSQs4AxbWpeJaSgv0BCykatTQSwg==}
peerDependencies:
@@ -1696,6 +1763,16 @@ packages:
react: 18.2.0
dev: false
+ /@nextui-org/use-is-mounted@2.0.2(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-PjwpTkl5f+bTVU9l5GzgZDHd+uOwCZ3bhuYzbbamw1J5kBWruVnKUqZihS3zrLtJxKNxk/f7RT0UWK2a4wGpDw==}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=16.8.0'
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@nextui-org/use-safe-layout-effect@2.0.2(react@18.2.0):
resolution: {integrity: sha512-HsFP2e+o2eSiQyAXdiicPBj6qj1naHuiNqqeTPqeJBsr0aUZI8l+7vZ5OXjLc8Qou4AOyNyJBBGFNhwsraxdpw==}
peerDependencies:
@@ -1712,6 +1789,14 @@ packages:
react: 18.2.0
dev: false
+ /@nextui-org/use-update-effect@2.0.2(react@18.2.0):
+ resolution: {integrity: sha512-yN2LWvG2QNDz6XDjRZq6jBQ7+Jaz2eihy+Q7IR+XLXi6fsyKQuYKxphw5VANa0ZbvKVuN/n5m5WRDRmWmeeOWw==}
+ peerDependencies:
+ react: '>=18'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -2049,6 +2134,24 @@ packages:
react: 18.2.0
dev: false
+ /@react-aria/tabs@3.6.2(react@18.2.0):
+ resolution: {integrity: sha512-FjI0h1Z4TsLOvIODhdDrVLz0O8RAqxDi58DO88CwkdUrWwZspNEpSpHhDarzUT7MlX3X72lsAUwvQLqY1OmaBQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ dependencies:
+ '@react-aria/focus': 3.14.0(react@18.2.0)
+ '@react-aria/i18n': 3.8.1(react@18.2.0)
+ '@react-aria/interactions': 3.17.0(react@18.2.0)
+ '@react-aria/selection': 3.16.1(react@18.2.0)
+ '@react-aria/utils': 3.19.0(react@18.2.0)
+ '@react-stately/list': 3.9.1(react@18.2.0)
+ '@react-stately/tabs': 3.5.1(react@18.2.0)
+ '@react-types/shared': 3.19.0(react@18.2.0)
+ '@react-types/tabs': 3.3.1(react@18.2.0)
+ '@swc/helpers': 0.5.1
+ react: 18.2.0
+ dev: false
+
/@react-aria/textfield@3.10.0(react@18.2.0):
resolution: {integrity: sha512-TYFgDTlxrljakD0TGOkoSCvot9BfVCZSrTKy3+/PICSTkPIzXThLIQmpX6yObLMXQSNW6SvBCl6CMetJMJHcbw==}
peerDependencies:
@@ -2208,6 +2311,19 @@ packages:
react: 18.2.0
dev: false
+ /@react-stately/list@3.9.1(react@18.2.0):
+ resolution: {integrity: sha512-GiKrxGakzMTZKe3mp410l4xKiHbZplJCGrtqlxq/+YRD0uCQwWGYpRG+z9A7tTCusruRD3m91/OjWsbfbGdiEw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ dependencies:
+ '@react-stately/collections': 3.10.0(react@18.2.0)
+ '@react-stately/selection': 3.13.3(react@18.2.0)
+ '@react-stately/utils': 3.7.0(react@18.2.0)
+ '@react-types/shared': 3.19.0(react@18.2.0)
+ '@swc/helpers': 0.5.1
+ react: 18.2.0
+ dev: false
+
/@react-stately/menu@3.5.4(react@18.2.0):
resolution: {integrity: sha512-+Q71fMDhMM1iARPFtwqpXY/8qkb0dN4PBJbcjwjGCumGs+ja2YbZxLBHCP0DYBElS9l6m3ssF47RKNMtF/Oi5w==}
peerDependencies:
@@ -2255,6 +2371,19 @@ packages:
react: 18.2.0
dev: false
+ /@react-stately/tabs@3.5.1(react@18.2.0):
+ resolution: {integrity: sha512-p1vZOuIS98GMF9jfEHQA6Pir1wYY6j+Gni6DcluNnWj90rLEubuwARNw7uscoOaXKlK/DiZIhkLKSDsA5tbadQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ dependencies:
+ '@react-stately/list': 3.9.1(react@18.2.0)
+ '@react-stately/utils': 3.7.0(react@18.2.0)
+ '@react-types/shared': 3.19.0(react@18.2.0)
+ '@react-types/tabs': 3.3.1(react@18.2.0)
+ '@swc/helpers': 0.5.1
+ react: 18.2.0
+ dev: false
+
/@react-stately/toggle@3.6.0(react@18.2.0):
resolution: {integrity: sha512-w+Aqh78H9MLs0FDUYTjAzYhrHQWaDJ2zWjyg2oYcSvERES0+D0obmPvtJLWsFrJ8fHJrTmxd7ezVFBY9BbPeFQ==}
peerDependencies:
@@ -2451,6 +2580,15 @@ packages:
react: 18.2.0
dev: false
+ /@react-types/tabs@3.3.1(react@18.2.0):
+ resolution: {integrity: sha512-vPxSbLCU7RT+Rupvu/1uOAesxlR/53GD5ZbgLuQRr/oEZRbsjY8Cs3CE3LGv49VdvBWivXUvHiF5wSE7CdWs1w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ dependencies:
+ '@react-types/shared': 3.19.0(react@18.2.0)
+ react: 18.2.0
+ dev: false
+
/@react-types/textfield@3.7.2(react@18.2.0):
resolution: {integrity: sha512-TsZTf1+4Ve9QHm6mbXr26uLOA4QtZPgyjYgYclL2nHoOl67algeQIFxIVfdlNIKFFMOw5BtC6Mer0I3KUWtbOQ==}
peerDependencies:
@@ -2934,6 +3072,10 @@ packages:
engines: {node: ^12.20.0 || >=14}
dev: true
+ /compute-scroll-into-view@3.0.3:
+ resolution: {integrity: sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==}
+ dev: false
+
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: false
@@ -4730,6 +4872,10 @@ packages:
engines: {node: '>= 6'}
dev: false
+ /pocketbase@0.16.0:
+ resolution: {integrity: sha512-sndPu4Mbu1Y2VefvO2gIa/No5aIdP6E5br9HUGomvDYSfkmGfBxJNkivJSSKlr9KwWnaF8otf14304UUeFGOrA==}
+ dev: false
+
/postcss-import@15.1.0(postcss@8.4.24):
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'}
@@ -5092,6 +5238,12 @@ packages:
loose-envify: 1.4.0
dev: false
+ /scroll-into-view-if-needed@3.0.10:
+ resolution: {integrity: sha512-t44QCeDKAPf1mtQH3fYpWz8IM/DyvHLjs8wUvvwMYxk5moOqCzrMSxK6HQVD0QVmVjXFavoFIPRVrMuJPKAvtg==}
+ dependencies:
+ compute-scroll-into-view: 3.0.3
+ dev: false
+
/semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
diff --git a/types/index.ts b/types/index.ts
index d2070cb..fc6ed32 100644
--- a/types/index.ts
+++ b/types/index.ts
@@ -6,10 +6,11 @@ export type IconSvgProps = SVGProps
& {
}
const bookmark = {
- name: z.string(),
- link: z.string(),
- description: z.string(),
- url: z.string().url(),
+ name: z.string().nonempty(),
+ link: z.string().nonempty(),
+ description: z.string().nonempty(),
+ url: z.string().url().nonempty(),
+ category_id: z.number(),
}
export const bookmarkSchema = z.object({
@@ -22,3 +23,21 @@ export const newBookmarkSchema = z.object({
})
export type Bookmark = z.infer
+
+const bookmarkCategory = {
+ name: z.string().nonempty(),
+}
+
+export const bookmarkCategorySchema = z.object({
+ id: z.number(),
+})
+
+export const bookmarkCategoryWithBookmarksSchema = z.object({
+ id: z.number(),
+ ...bookmarkCategory,
+ bookmark: bookmarkSchema.array(),
+})
+
+export type BookmarkCategoryWithBookmarks = z.infer<
+ typeof bookmarkCategoryWithBookmarksSchema
+>