New feature: add category

master
TZGyn 2 years ago
parent b7446257d0
commit 19f4a2ebde
Signed by: TZGyn
GPG Key ID: 122EAF77AE81FD4A

@ -9,6 +9,7 @@ import clsx from 'clsx'
import { userSchema } from '@/types'
import { cookies } from 'next/headers'
import { getUser } from '@/lib/auth'
import { getBookmarkCategories } from '@/lib/utils'
export const metadata: Metadata = {
title: {
@ -40,6 +41,10 @@ export default async function RootLayout({
user = userSchema.parse(userData)
}
const bookmarkCategories = await getBookmarkCategories(
userData ? userData.id : null
)
return (
<html
lang='en'
@ -53,7 +58,10 @@ export default async function RootLayout({
<Providers
themeProps={{ attribute: 'class', defaultTheme: 'dark' }}>
<div className='relative flex h-screen flex-col'>
<Navbar user={user} />
<Navbar
user={user}
categories={bookmarkCategories}
/>
<main className='container mx-auto max-w-7xl flex-grow px-6 pt-16'>
{children}
</main>

@ -30,9 +30,15 @@ import { GithubIcon } from '@/components/icons'
import { Icon } from '@iconify/react'
import NewBookmarkForm from '@/components/newBookmarkForm'
import { usePathname, useRouter } from 'next/navigation'
import { User } from '@/types'
import { BookmarkCategory, User } from '@/types'
export const Navbar = ({ user }: { user: User | null }) => {
export const Navbar = ({
user,
categories,
}: {
user: User | null
categories: BookmarkCategory[]
}) => {
const pathname = usePathname()
const router = useRouter()
@ -95,7 +101,7 @@ export const Navbar = ({ user }: { user: User | null }) => {
<NavbarItem className='hidden gap-2 sm:flex'>
{pathname.startsWith('/dashboard') && (
<div className='mr-4'>
<NewBookmarkForm />
<NewBookmarkForm categories={categories} />
</div>
)}
<Link

@ -8,30 +8,37 @@ import {
ModalFooter,
useDisclosure,
} from '@nextui-org/modal'
import {
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
} from '@nextui-org/dropdown'
import { Select, SelectSection, SelectItem } from '@nextui-org/select'
import { Selection } from '@nextui-org/react'
import { Button } from '@nextui-org/button'
import { Input } from '@nextui-org/input'
import { Icon } from '@iconify/react'
import { ChevronDownIcon } from './ChevronDownIcon'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { Tab, Tabs } from '@nextui-org/tabs'
import { BookmarkCategory } from '@/types'
export default function NewBookmarkForm() {
export default function NewBookmarkForm({
categories,
}: {
categories: BookmarkCategory[]
}) {
const { isOpen, onOpen, onOpenChange } = useDisclosure()
const router = useRouter()
const onSubmit = async () => {
const onSubmit = () => {
if (selected === 'bookmark') submitBookmark()
else if (selected === 'bookmark_category') submitCategory()
else return
}
const submitBookmark = async () => {
const body = {
name,
link,
description,
url,
category_id: selectedKey === 'Other' ? 2 : 1,
category_id: parseInt(value.currentKey),
}
await fetch('/api/bookmark', {
method: 'POST',
@ -41,12 +48,28 @@ export default function NewBookmarkForm() {
router.refresh()
}
const submitCategory = async () => {
const body = {
name: categoryName,
}
await fetch('/api/bookmark_category', {
method: 'POST',
body: JSON.stringify(body),
})
onOpenChange()
router.refresh()
}
const [selected, setSelected] = useState('bookmark')
const [name, setName] = useState('')
const [link, setLink] = useState('')
const [description, setDescription] = useState('')
const [url, setUrl] = useState('')
const [selectedKey, setSelectedKey] = useState('Other')
const [categoryName, setCategoryName] = useState('')
const [value, setValue] = useState<Selection>(new Set([]))
return (
<>
@ -67,84 +90,95 @@ export default function NewBookmarkForm() {
onOpenChange={onOpenChange}
placement='center'
hideCloseButton>
<ModalContent
onKeyUp={(e) => {
if (e.key === 'Enter') onSubmit()
}}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className='flex gap-2'>
New Bookmark
New Bookmark/Category
<Icon
icon='mdi:bookmark-box'
fontSize={30}
/>
</ModalHeader>
<ModalBody className='gap-4'>
<Dropdown>
<DropdownTrigger className='flex justify-start'>
<Button
startContent={
<span className='text-default-400'>
Category:
</span>
}
endContent={
<ChevronDownIcon className='text-small' />
}
variant='flat'>
<div className='w-full text-start'>
{selectedKey}
</div>
</Button>
</DropdownTrigger>
<DropdownMenu
disallowEmptySelection
selectionMode='single'
selectedKeys={selectedKey}
disabledKeys={['disabled']}>
<DropdownItem
key='disabled'
className='capitalize'>
Select Category
</DropdownItem>
<DropdownItem
key='Coding'
onClick={() => {
setSelectedKey('Coding')
}}
className='capitalize'>
Coding
</DropdownItem>
<DropdownItem
key='Other'
onClick={() => {
setSelectedKey('Other')
}}
className='capitalize'>
Other
</DropdownItem>
</DropdownMenu>
</Dropdown>
<Input
autoFocus
label='Name'
placeholder='Enter Name'
variant='bordered'
value={name}
onChange={(event) => {
setName(event.target.value)
}}
/>
<Input
label='Url'
placeholder='Enter Url'
variant='bordered'
value={url}
onChange={(event) => {
setUrl(event.target.value)
}}
/>
<Tabs
selectedKey={selected}
onSelectionChange={(key) =>
setSelected(key.toString())
}
fullWidth
color='primary'>
<Tab
key='bookmark'
title={'Bookmark'}>
<form className='flex flex-col gap-4'>
<Select
label='Select category'
placeholder='Select a category'
selectedKeys={value}
onSelectionChange={setValue}>
{categories.length > 0 ? (
categories.map(
(category) => (
<SelectItem
key={
category.id
}
value={
category.id
}>
{category.name}
</SelectItem>
)
)
) : (
<SelectItem
key={'test'}
value={'test'}>
{'test'}
</SelectItem>
)}
</Select>
<Input
autoFocus
label='Name'
placeholder='Enter Name'
variant='bordered'
value={name}
onChange={(event) => {
setName(event.target.value)
}}
/>
<Input
label='Url'
placeholder='Enter Url'
variant='bordered'
value={url}
onChange={(event) => {
setUrl(event.target.value)
}}
/>
</form>
</Tab>
<Tab
key='bookmark_category'
title={'Bookmark Category'}>
<form className='flex flex-col gap-4'>
<Input
autoFocus
label='Category Name'
placeholder='Enter Category Name'
variant='bordered'
value={categoryName}
onChange={(event) => {
setCategoryName(
event.target.value
)
}}
/>
</form>
</Tab>
</Tabs>
</ModalBody>
<ModalFooter>
<Button

@ -0,0 +1,10 @@
import { db } from './db'
export const getBookmarkCategories = async (user: number | null) => {
const bookmarkCategories = await db.query.bookmarkCategory.findMany({
where: (bookmarkCategory, { eq }) =>
eq(bookmarkCategory.userId, user ?? 0),
})
return bookmarkCategories
}

@ -22,6 +22,8 @@
"@nextui-org/link": "2.0.0",
"@nextui-org/modal": "^2.0.13",
"@nextui-org/navbar": "2.0.0",
"@nextui-org/react": "^2.1.5",
"@nextui-org/select": "^2.1.4",
"@nextui-org/snippet": "2.0.0",
"@nextui-org/switch": "2.0.0",
"@nextui-org/system": "2.0.0",

File diff suppressed because it is too large Load Diff

@ -34,9 +34,12 @@ export const newBookmarkCategorySchema = z.object({
export const bookmarkCategorySchema = z.object({
id: z.number(),
userId: z.number(),
...bookmarkCategory,
})
export type BookmarkCategory = z.infer<typeof bookmarkCategorySchema>
export const bookmarkCategoryWithBookmarksSchema = z.object({
id: z.number(),
...bookmarkCategory,

Loading…
Cancel
Save