1+ "use client" ;
2+
3+ import React , { useState , useEffect } from "react" ;
4+ import { Input } from "@/components/ui/input" ;
5+ import { Button } from "@/components/ui/button" ;
6+ import { Tabs , TabsList , TabsTrigger , TabsContent } from "@/components/ui/tabs" ;
7+ import { addDropdownOption , deleteDropdownOption , getDropdownOptions } from "@/server-action/db-actions" ;
8+
9+ interface DropdownOption {
10+ id : number ;
11+ category : string ;
12+ option_value : string ;
13+ }
14+
15+ export default function ManageCategoriesPage ( ) {
16+ const [ activeTab , setActiveTab ] = useState ( "add" ) ; // State to manage active tab
17+ const [ categories , setCategories ] = useState < string [ ] > ( [ ] ) ;
18+
19+ useEffect ( ( ) => {
20+ fetchCategories ( ) ;
21+ } , [ ] ) ;
22+
23+ const fetchCategories = async ( ) => {
24+ try {
25+ const allCategories = await getDropdownOptions ( "all" ) ; // Fetch all categories
26+ const uniqueCategories = Array . from ( new Set ( allCategories . map ( ( option : DropdownOption ) => option . category ) ) ) ;
27+ setCategories ( uniqueCategories ) ;
28+ } catch ( error ) {
29+ console . error ( "Error fetching categories:" , error ) ;
30+ }
31+ } ;
32+
33+ return (
34+ < div className = "p-6" >
35+ < h1 className = "text-2xl font-bold mb-6" > Manage Categories</ h1 >
36+ < Tabs defaultValue = "add" value = { activeTab } onValueChange = { setActiveTab } >
37+ < TabsList className = "mb-4" >
38+ < TabsTrigger value = "add" > Add Category Option</ TabsTrigger >
39+ < TabsTrigger value = "delete" > Delete Category Option</ TabsTrigger >
40+ </ TabsList >
41+ < TabsContent value = "add" >
42+ < AddCategoryOption fetchCategories = { fetchCategories } categories = { categories } />
43+ </ TabsContent >
44+ < TabsContent value = "delete" >
45+ < DeleteCategoryOption fetchCategories = { fetchCategories } categories = { categories } />
46+ </ TabsContent >
47+ </ Tabs >
48+ </ div >
49+ ) ;
50+ }
51+
52+ interface CategoryManagementProps {
53+ fetchCategories : ( ) => void ;
54+ categories : string [ ] ;
55+ }
56+
57+ function AddCategoryOption ( { fetchCategories } : CategoryManagementProps ) {
58+ const [ categoryName , setCategoryName ] = useState ( "" ) ;
59+ const [ optionValue , setOptionValue ] = useState ( "" ) ;
60+ const [ status , setStatus ] = useState < string | null > ( null ) ;
61+ const [ loading , setLoading ] = useState ( false ) ;
62+
63+ const handleSubmit = async ( e : React . FormEvent ) => {
64+ e . preventDefault ( ) ;
65+ if ( ! categoryName || ! optionValue ) {
66+ setStatus ( "Please fill in both fields." ) ;
67+ return ;
68+ }
69+ setLoading ( true ) ;
70+ setStatus ( null ) ;
71+ try {
72+ const result = await addDropdownOption ( categoryName , optionValue ) ;
73+ if ( result . success ) {
74+ setStatus ( "Category option added successfully!" ) ;
75+ setCategoryName ( "" ) ;
76+ setOptionValue ( "" ) ;
77+ fetchCategories ( ) ; // Refresh categories list
78+ } else {
79+ setStatus ( `Failed to add category option: ${ result . error } ` ) ;
80+ }
81+ } catch ( error ) {
82+ console . error ( "Error adding category option:" , error ) ;
83+ setStatus ( "An unexpected error occurred." ) ;
84+ } finally {
85+ setLoading ( false ) ;
86+ }
87+ } ;
88+
89+ return (
90+ < div className = "space-y-4" >
91+ < h3 className = "text-lg font-semibold" > Add New Category Option</ h3 >
92+ < form onSubmit = { handleSubmit } className = "space-y-4" >
93+ < div >
94+ < label htmlFor = "categoryName" className = "block text-sm font-medium text-gray-700" > Category Name</ label >
95+ < Input
96+ type = "text"
97+ id = "categoryName"
98+ placeholder = "e.g., project_types, departments, domains, submission_years"
99+ value = { categoryName }
100+ onChange = { ( e ) => setCategoryName ( e . target . value ) }
101+ className = "w-full"
102+ />
103+ </ div >
104+ < div >
105+ < label htmlFor = "optionValue" className = "block text-sm font-medium text-gray-700" > Option Value</ label >
106+ < Input
107+ type = "text"
108+ id = "optionValue"
109+ placeholder = "e.g., Research Project, Computer Science, Web Development, 2023"
110+ value = { optionValue }
111+ onChange = { ( e ) => setOptionValue ( e . target . value ) }
112+ className = "w-full"
113+ />
114+ </ div >
115+ < Button type = "submit" disabled = { loading } >
116+ { loading ? "Adding..." : "Add Option" }
117+ </ Button >
118+ { status && < p className = "mt-2 text-sm" > { status } </ p > }
119+ </ form >
120+ </ div >
121+ ) ;
122+ }
123+
124+ function DeleteCategoryOption ( { fetchCategories } : CategoryManagementProps ) {
125+ const [ categoryName , setCategoryName ] = useState ( "" ) ;
126+ const [ optionValue , setOptionValue ] = useState ( "" ) ;
127+ const [ categoryOptions , setCategoryOptions ] = useState < DropdownOption [ ] > ( [ ] ) ;
128+ const [ status , setStatus ] = useState < string | null > ( null ) ;
129+ const [ loading , setLoading ] = useState ( false ) ;
130+ const [ confirming , setConfirming ] = useState ( false ) ;
131+
132+ useEffect ( ( ) => {
133+ const fetchOptions = async ( ) => {
134+ if ( categoryName ) {
135+ try {
136+ const options = await getDropdownOptions ( categoryName ) ;
137+ setCategoryOptions ( options ) ;
138+ } catch ( error ) {
139+ console . error ( `Error fetching options for category ${ categoryName } :` , error ) ;
140+ setCategoryOptions ( [ ] ) ;
141+ }
142+ } else {
143+ setCategoryOptions ( [ ] ) ;
144+ }
145+ } ;
146+ fetchOptions ( ) ;
147+ } , [ categoryName ] ) ;
148+
149+ const handleDelete = async ( ) => {
150+ if ( ! categoryName || ! optionValue ) {
151+ setStatus ( "Please select both category and option value." ) ;
152+ return ;
153+ }
154+ setLoading ( true ) ;
155+ setStatus ( null ) ;
156+ try {
157+ const result = await deleteDropdownOption ( categoryName , optionValue ) ;
158+ if ( result . success ) {
159+ setStatus ( "Category option deleted successfully!" ) ;
160+ setCategoryName ( "" ) ;
161+ setOptionValue ( "" ) ;
162+ fetchCategories ( ) ; // Refresh categories list
163+ } else {
164+ setStatus ( `Failed to delete category option: ${ result . error } ` ) ;
165+ }
166+ } catch ( error ) {
167+ console . error ( "Error deleting category option:" , error ) ;
168+ setStatus ( "An unexpected error occurred." ) ;
169+ } finally {
170+ setLoading ( false ) ;
171+ setConfirming ( false ) ;
172+ }
173+ } ;
174+
175+ return (
176+ < div className = "space-y-4" >
177+ < h3 className = "text-lg font-semibold" > Delete Existing Category Option</ h3 >
178+ < form onSubmit = { ( e ) => e . preventDefault ( ) } className = "space-y-4" >
179+ < div >
180+ < label htmlFor = "deleteCategoryName" className = "block text-sm font-medium text-gray-700" > Category Name</ label >
181+ < select
182+ id = "deleteCategoryName"
183+ value = { categoryName }
184+ onChange = { ( e ) => {
185+ setCategoryName ( e . target . value ) ;
186+ setOptionValue ( "" ) ; // Reset option value when category changes
187+ } }
188+ className = "w-full px-4 py-2 border rounded-lg"
189+ >
190+ < option value = "" > Select Category</ option >
191+ { categories . map ( ( cat ) => (
192+ < option key = { cat } value = { cat } > { cat } </ option >
193+ ) ) }
194+ </ select >
195+ </ div >
196+ < div >
197+ < label htmlFor = "deleteOptionValue" className = "block text-sm font-medium text-gray-700" > Option Value</ label >
198+ < select
199+ id = "deleteOptionValue"
200+ value = { optionValue }
201+ onChange = { ( e ) => setOptionValue ( e . target . value ) }
202+ className = "w-full px-4 py-2 border rounded-lg"
203+ disabled = { ! categoryName }
204+ >
205+ < option value = "" > Select Option</ option >
206+ { categoryOptions . map ( ( option ) => (
207+ < option key = { option . id } value = { option . option_value } >
208+ { option . option_value }
209+ </ option >
210+ ) ) }
211+ </ select >
212+ </ div >
213+ { ! confirming ? (
214+ < Button
215+ type = "button"
216+ onClick = { ( ) => setConfirming ( true ) }
217+ disabled = { loading || ! categoryName || ! optionValue }
218+ variant = "destructive"
219+ >
220+ Delete Option
221+ </ Button >
222+ ) : (
223+ < div className = "space-x-4" >
224+ < Button onClick = { handleDelete } disabled = { loading } variant = "destructive" >
225+ Confirm Delete
226+ </ Button >
227+ < Button onClick = { ( ) => setConfirming ( false ) } disabled = { loading } variant = "outline" >
228+ Cancel
229+ </ Button >
230+ </ div >
231+ ) }
232+ { status && < p className = "mt-2 text-sm" > { status } </ p > }
233+ </ form >
234+ </ div >
235+ ) ;
236+ }
0 commit comments