nextjs_zzyxyz/components/markcard/MarkCardsClient.tsx
ZZY 73917d6001 dev feat(markcard, static): 实现书签卡片功能和静态资源获取
- 书签卡片相关的组件和页面
- 实现书签数据的获取、添加、编辑和删除功能
- 添加用户验证和权限控制
- 优化页面布局和样式
2024-12-22 15:21:07 +08:00

184 lines
5.1 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { Button } from '@/components/ui/button'
import { PlusCircle } from 'lucide-react'
import { MarkCardI, MarkLinkI } from '@/lib/markcard/card'
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import EditCardModal from './EditMarkCardModal'
import MarkCardGrid from './MarkCardGrid'
import { notFound } from 'next/navigation'
import path from 'path'
interface CardContainerProps {
userId?: string
canEdit?: boolean
fileUrl?: string[]
}
export default function MarkCards({ canEdit, userId, fileUrl }: CardContainerProps) {
const baseUrl = '/api/card'
const userUrl = `${baseUrl}${userId ? `/${userId}` : ''}`
const url = path.join(userUrl, ...(fileUrl || []))
const [validToken, setValidToken] = useState<boolean>(canEdit ? true : false)
const [token, setToken] = useState<string>('')
const [isEditModalOpen, setIsEditModalOpen] = useState<boolean>(false)
const [cards, setCards] = useState<MarkCardI[] | undefined>([])
const [card, setCard] = useState<{
id: string
card: MarkCardI
}>()
const fetchData = async () => {
console.log('fetching')
try {
const response = await fetch(url, {
method: 'GET'
})
if (!response.ok) {
setCards(undefined)
return notFound()
}
const retcards = await (response.json()) as MarkCardI[]
setCards(retcards)
} catch (error) {
console.error('Error fetching data:', error)
}
}
useEffect(() => {
fetchData()
}, [])
const onEditMarkCard = async (id: string, updatedData: Partial<MarkLinkI>) => {
try {
const response = await fetch(url, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(updatedData),
})
if (!response.ok) {
throw new Error('Failed to update card')
}
setCards((prevCards) =>
prevCards?.map((card) => (card.id === id ? { ...card, ...updatedData } : card))
)
} catch (error) {
console.error('Error updating card:', error)
}
}
const onAddMarkCard = async () => {
const newCard: MarkCardI = {
id: '',
title: 'New Bookmark',
shortDescription: 'Add a description',
links: [{ title: 'Add a link', url: 'https://example.com' }],
}
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newCard),
})
if (!response.ok) {
throw new Error('Failed to add card')
}
const realNewCard = await response.json()
setCards((prevCards) => [...prevCards ?? [], realNewCard])
} catch (error) {
console.error('Error adding card:', error)
}
}
const onVailidate = async (token: string, override?: boolean) => {
if (!override && validToken === true) {
return
}
try {
const response = await fetch('/api/card/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ userId, token }),
})
if (!response.ok) {
setValidToken(false)
} else {
setValidToken((await response.json()).validate ?? false)
}
} catch {
setValidToken(false)
}
}
const onEdit = (id: string, origin: MarkCardI) => {
setCard({id, card: {...origin}})
console.log(id, origin)
setIsEditModalOpen(true)
}
return (
<div className="space-y-8 container">
<div className="flex justify-between space-x-4">
<div className='flex space-x-4'>
<Label
htmlFor='token-input'
className='flex items-center whitespace-nowrap text-xl font-bold'>
Card For
</Label>
<Input
className='flex border-none shadow-none max-w-[50vw]'
placeholder='TOKEN'
id='token-input'
value={token}
onChange={(e) => setToken(e.target.value)}
onBlur={(_) => onVailidate(token)}
/>
</div>
<div className='flex'>
{validToken && (
<div className="flex justify-end space-x-4">
<Button onClick={() => { setValidToken(false); setToken(''); fetchData()} }>Disable Edit</Button>
<Button onClick={onAddMarkCard}>
<PlusCircle className="mr-2 h-4 w-4" />
Add New Bookmark
</Button>
</div>
)}
</div>
</div>
{
cards === undefined ? notFound() :
<MarkCardGrid cards={cards} onEdit={onEdit} baseUrl={'/markcard'} canEdit={validToken} />
}
{validToken && card && (
<EditCardModal
isOpen={isEditModalOpen}
onClose={() => { setIsEditModalOpen(false); setCard(undefined) }}
cardData={card.card}
onSave={(updatedData) => {
onEditMarkCard(card.id, updatedData)
setCard(undefined)
setIsEditModalOpen(false)
}}
/>
)}
</div>
)
}