1+ import {
2+ Box ,
3+ Button ,
4+ Flex ,
5+ Heading ,
6+ Text ,
7+ Tooltip ,
8+ useColorModeValue ,
9+ Icon ,
10+ Badge ,
11+ VStack ,
12+ useDisclosure ,
13+ HStack ,
14+ Menu ,
15+ MenuButton ,
16+ MenuList ,
17+ MenuItem ,
18+ IconButton
19+ } from "@chakra-ui/react"
20+ import { dateToString } from "helpers/util"
21+ import * as React from "react"
22+ import DeleteIcon from "assets/icons/trash.svg"
23+ import EditIcon from "assets/icons/edit.svg"
24+ import FolderIcon from "assets/icons/folder.svg"
25+ import MoreIcon from "assets/icons/more-vertical.svg"
26+ import { AlbumData } from "@/client"
27+ import AlbumModal , { AlbumFormData } from "./AlbumModal"
28+
29+ const AlbumCard : React . FC < {
30+ album : AlbumData ,
31+ onOpen : ( album : AlbumData ) => void ,
32+ onDelete : ( album : AlbumData ) => void ,
33+ onUpdate ?: ( album : AlbumData , updates : AlbumFormData ) => void
34+ } > = ( { album, onOpen, onDelete, onUpdate } ) => {
35+ const { isOpen, onOpen : openModal , onClose : closeModal } = useDisclosure ( )
36+
37+ const handleEdit = ( e : React . MouseEvent ) => {
38+ e . stopPropagation ( )
39+ openModal ( )
40+ }
41+
42+ const handleDelete = ( e : React . MouseEvent ) => {
43+ e . stopPropagation ( )
44+ onDelete ( album )
45+ }
46+
47+ const handleUpdateSubmit = async ( formData : AlbumFormData ) => {
48+ if ( ! onUpdate ) return
49+
50+ try {
51+ await onUpdate ( album , formData )
52+ closeModal ( )
53+ } catch ( error ) {
54+ throw error
55+ }
56+ }
57+
58+
59+ const cardBg = useColorModeValue ( "white" , "gray.700" )
60+ const borderColor = useColorModeValue ( "gray.200" , "gray.600" )
61+ const textColor = useColorModeValue ( "gray.800" , "white" )
62+ const subtextColor = useColorModeValue ( "gray.600" , "gray.300" )
63+ const mutedTextColor = useColorModeValue ( "gray.500" , "gray.400" )
64+
65+ return (
66+ < >
67+ < Box
68+ bg = { cardBg }
69+ rounded = "lg"
70+ boxShadow = "lg"
71+ position = "relative"
72+ cursor = "pointer"
73+ onClick = { ( ) => onOpen ( album ) }
74+ w = { { sm : "100%" , md : "300px" } }
75+ minH = "200px"
76+ p = { 5 }
77+ >
78+ { /* Action Menu */ }
79+ < Menu >
80+ < MenuButton
81+ as = { IconButton }
82+ icon = { < Icon as = { MoreIcon } w = { 4 } h = { 4 } /> }
83+ variant = "ghost"
84+ size = "sm"
85+ position = "absolute"
86+ top = { 4 }
87+ right = { 4 }
88+ onClick = { ( e ) => e . stopPropagation ( ) }
89+ />
90+ < MenuList >
91+ { onUpdate && (
92+ < MenuItem icon = { < Icon as = { EditIcon } w = { 4 } h = { 4 } /> } onClick = { handleEdit } >
93+ Edit Album
94+ </ MenuItem >
95+ ) }
96+ < MenuItem
97+ icon = { < Icon as = { DeleteIcon } w = { 4 } h = { 4 } /> }
98+ onClick = { handleDelete }
99+ color = "red.500"
100+ >
101+ Delete Album
102+ </ MenuItem >
103+ </ MenuList >
104+ </ Menu >
105+
106+ < VStack spacing = { 4 } h = "full" justify = "space-between" align = "stretch" >
107+ { /* Header with icon and title */ }
108+ < VStack spacing = { 3 } align = "center" >
109+ < Box p = { 3 } >
110+ < Icon
111+ as = { FolderIcon }
112+ w = { 10 }
113+ h = { 10 }
114+ color = "primary.300"
115+ />
116+ </ Box >
117+
118+ < VStack spacing = { 1 } align = "center" >
119+ < Heading
120+ fontSize = "lg"
121+ fontWeight = "semibold"
122+ textAlign = "center"
123+ noOfLines = { 2 }
124+ color = { textColor }
125+ lineHeight = "1.3"
126+ >
127+ { album . name }
128+ </ Heading >
129+
130+ { album . description && (
131+ < Text
132+ fontSize = "sm"
133+ color = { subtextColor }
134+ textAlign = "center"
135+ noOfLines = { 2 }
136+ >
137+ { album . description }
138+ </ Text >
139+ ) }
140+ </ VStack >
141+ </ VStack >
142+
143+ { /* Footer */ }
144+ < VStack spacing = { 2 } >
145+ < HStack justify = "space-between" w = "full" >
146+ < Text fontSize = "xs" color = { mutedTextColor } >
147+ { dateToString ( new Date ( album . created ) ) }
148+ </ Text >
149+ < Badge
150+ colorScheme = { album . public ? "green" : "gray" }
151+ variant = "subtle"
152+ fontSize = "xs"
153+ px = { 2 }
154+ py = { 1 }
155+ rounded = "md"
156+ >
157+ { album . public ? "Public" : "Private" }
158+ </ Badge >
159+ </ HStack >
160+ </ VStack >
161+ </ VStack >
162+ </ Box >
163+
164+ { onUpdate && (
165+ < AlbumModal
166+ isOpen = { isOpen }
167+ onClose = { closeModal }
168+ album = { album }
169+ onSubmit = { handleUpdateSubmit }
170+ />
171+ ) }
172+ </ >
173+ )
174+ }
175+
176+ export default AlbumCard
0 commit comments