11"use client" ;
22
3- import React , { useState } from "react" ;
3+ import React , { useState , useEffect } from "react" ;
44import { ArrowLeft , Save , XCircle } from "lucide-react" ;
55import Link from "next/link" ;
66
7- const submissionYears = [ 2025 , 2024 , 2023 , 2022 , 2021 ] ;
8- const projectTypes = [
9- "Final Year Project" ,
10- "Mini Project" ,
11- "Research Project" ,
12- "Personal Project" ,
13- "Others"
14- ] ;
15- const departments = [ "CSE" , "IT" , "ECE" , "EEE" , "MECH" , "CIVIL" , "Other" ] ;
16- const availableDomains = [
17- "Other" ,
18- "Web Development" ,
19- "Mobile App Development (Android & iOS)" ,
20- "Artificial Intelligence (AI) & Machine Learning (ML)" ,
21- "Data Science & Big Data Analytics" ,
22- "Cybersecurity & Ethical Hacking" ,
23- "Blockchain & Cryptocurrency" ,
24- "Cloud Computing & DevOps" ,
25- "Game Development & AR/VR" ,
26- "Internet of Things (IoT)" ,
27- "Natural Language Processing (NLP)" ,
28- "Database Management & Data Warehousing" ,
29- "Quantum Computing" ,
30- "Software Testing & Automation" ,
31- "Full Stack Development (MERN, MEAN, etc.)" ,
32- "UI/UX & Human-Computer Interaction" ,
33- "Computer Networks & Network Security" ,
34- "Augmented Reality (AR) & Virtual Reality (VR)" ,
35- "E-commerce & CMS Development" ,
36- "No-Code & Low-Code Development" ,
37- "Cloud Security & Serverless Computing" ,
38- "DevOps & Site Reliability Engineering (SRE)" ,
39- "Edge Computing & Distributed Systems" ,
40- "IT Infrastructure & System Administration" ,
41- "Data Engineering & Business Intelligence" ,
42- "IT Governance & Compliance" ,
43- "Structural Engineering & Earthquake-Resistant Design" ,
44- "Transportation & Highway Engineering" ,
45- "Geotechnical Engineering & Soil Mechanics" ,
46- "Smart Cities & Urban Planning" ,
47- "Sustainable & Green Building Technology" ,
48- "Hydraulics & Water Resource Engineering" ,
49- "Construction Management & Project Planning" ,
50- "Environmental Engineering & Waste Management" ,
51- "Building Information Modeling (BIM)" ,
52- "Disaster Management & Risk Analysis" ,
53- "Bridge & Tunnel Engineering" ,
54- "Surveying & Remote Sensing (GIS & GPS)" ,
55- "VLSI & Chip Design" ,
56- "Embedded Systems & Microcontrollers" ,
57- "Wireless Communication (5G, LTE, Satellite)" ,
58- "Signal & Image Processing" ,
59- "Optical Fiber & Photonics" ,
60- "Digital & Analog Circuit Design" ,
61- "Antenna & RF Engineering" ,
62- "Smart Sensors & Wearable Technology" ,
63- "Audio & Speech Processing" ,
64- "Biomedical Electronics & Bionics" ,
65- "MEMS & Nanoelectronics" ,
66- "Power Systems & Smart Grids" ,
67- "Renewable Energy (Solar, Wind, Hydro)" ,
68- "Control Systems & Automation" ,
69- "Robotics & Mechatronics" ,
70- "Electric Vehicles (EV) & Battery Technologies" ,
71- "High Voltage Engineering" ,
72- "Energy Management & Conservation" ,
73- "Industrial Instrumentation & Process Control" ,
74- "Electrical Machines & Drives" ,
75- "Smart Home & Building Automation" ,
76- "CAD, CAM & 3D Printing" ,
77- "Automotive & Aerospace Engineering" ,
78- "Thermodynamics & Fluid Mechanics" ,
79- "Mechatronics & Smart Manufacturing" ,
80- "HVAC & Refrigeration Systems" ,
81- "Material Science & Composites" ,
82- "Renewable Energy in Mechanical Systems" ,
83- "Computational Fluid Dynamics (CFD)" ,
84- "Finite Element Analysis (FEA)"
85- ] ;
7+ interface CategoryOption { // Define interfaces for fetched data
8+ optionId : number ;
9+ optionName : string ;
10+ }
11+
12+ interface Category {
13+ categoryId : number ;
14+ categoryName : string ;
15+ options : CategoryOption [ ] ;
16+ }
8617
8718const AddProjectPage = ( ) => {
19+ const [ categories , setCategories ] = useState < Category [ ] > ( [ ] ) ;
20+ const [ loadingCategories , setLoadingCategories ] = useState ( true ) ;
21+ const [ errorCategories , setErrorCategories ] = useState < string | null > ( null ) ;
22+
23+ useEffect ( ( ) => {
24+ const fetchCategories = async ( ) => {
25+ try {
26+ const res = await fetch ( '/api/categories' ) ;
27+ if ( ! res . ok ) {
28+ throw new Error ( `HTTP error! status: ${ res . status } ` ) ;
29+ }
30+ const data : Category [ ] = await res . json ( ) ;
31+ setCategories ( data ) ;
32+ } catch ( e : any ) {
33+ setErrorCategories ( e . message ) ;
34+ console . error ( "Failed to fetch categories for form:" , e ) ;
35+ } finally {
36+ setLoadingCategories ( false ) ;
37+ }
38+ } ;
39+ fetchCategories ( ) ;
40+ } , [ ] ) ;
41+
8842 const initialFormState = {
8943 projectName : "" ,
9044 projectDescription : "" ,
91- yearOfSubmission : "2025" ,
92- projectType : "Personal Project" ,
93- department : "" ,
94- domain : "Web Development" ,
95- customDomain : "" ,
9645 projectLink : "" ,
97- members : [ { name : "" , linkedin : "" } ]
46+ createdAt : "" ,
47+ members : [ { name : "" , linkedin : "" } ] ,
48+ selectedCategoryOptions : { } as Record < string , string > , // Map category name to selected option name
49+ customDomain : "" , // Keep customDomain separate if 'Domain' is 'Other'
9850 } ;
9951
10052 const [ formData , setFormData ] = useState ( initialFormState ) ;
101- const [ showPopup , setShowPopup ] = useState ( false ) ; // State for pop-up visibility
102- const [ loading , setLoading ] = useState ( false ) ; // Loading state to prevent duplicate submissions
53+ const [ showPopup , setShowPopup ] = useState ( false ) ;
54+ const [ loading , setLoading ] = useState ( false ) ;
55+
56+ useEffect ( ( ) => {
57+ // Set initial default values for dropdowns after categories are fetched
58+ if ( ! loadingCategories && categories . length > 0 ) {
59+ setFormData ( prev => {
60+ const newSelectedOptions : Record < string , string > = { } ;
61+ categories . forEach ( cat => {
62+ if ( cat . options . length > 0 ) {
63+ newSelectedOptions [ cat . categoryName ] = cat . options [ 0 ] . optionName ;
64+ }
65+ } ) ;
66+ return {
67+ ...prev ,
68+ selectedCategoryOptions : newSelectedOptions ,
69+ } ;
70+ } ) ;
71+ }
72+ } , [ loadingCategories , categories ] ) ;
10373
10474 const handleSubmit = async ( e : React . FormEvent ) => {
10575 e . preventDefault ( ) ;
106- if ( loading ) return ; // Prevent multiple submissions
76+ if ( loading ) return ;
10777
108- // Ensure at least one member has a name
10978 const hasValidMember = formData . members . some (
11079 ( member ) => member . name . trim ( ) !== ""
11180 ) ;
@@ -114,37 +83,47 @@ const AddProjectPage = () => {
11483 return ;
11584 }
11685
117- setLoading ( true ) ; // Begin submission
86+ setLoading ( true ) ;
11887
119- // Filter out empty members
12088 const filteredMembers = formData . members . filter (
12189 ( member ) => member . name . trim ( ) !== ""
12290 ) ;
12391
92+ // Prepare category options for backend
93+ const projectCategoryOptions : { categoryName : string ; optionName : string } [ ] = Object . entries ( formData . selectedCategoryOptions ) . map ( ( [ categoryName , optionName ] ) => ( {
94+ categoryName,
95+ optionName : categoryName === 'Domain' && optionName === 'Other' ? formData . customDomain : optionName // Use customDomain if 'Other' domain is selected
96+ } ) ) ;
97+
12498 const projectData = {
125- ...formData ,
99+ projectName : formData . projectName ,
100+ projectDescription : formData . projectDescription ,
101+ projectLink : formData . projectLink ,
102+ createdAt : new Date ( ) . toISOString ( ) ,
126103 members : filteredMembers ,
127- createdAt : new Date ( ) . toISOString ( )
104+ projectCategoryOptions, // Send as a generic array of category options
105+ customDomain : formData . selectedCategoryOptions [ 'Domain' ] === 'Other' ? formData . customDomain : undefined , // Send customDomain separately
128106 } ;
129107
130108 try {
131- const response = await fetch ( "/api/saveProject " , {
109+ const response = await fetch ( "/api/projects " , {
132110 method : "POST" ,
133111 headers : { "Content-Type" : "application/json" } ,
134112 body : JSON . stringify ( projectData )
135113 } ) ;
136114
137115 if ( response . ok ) {
138- setFormData ( initialFormState ) ;
139- setShowPopup ( true ) ; // Show the congratulatory pop-up
140- setTimeout ( ( ) => setShowPopup ( false ) , 3000 ) ; // Hide it after 3 seconds
116+ localStorage . removeItem ( 'cachedProjects' ) ; // Invalidate projects cache
117+ setFormData ( initialFormState ) ; // Reset form
118+ setShowPopup ( true ) ;
119+ setTimeout ( ( ) => setShowPopup ( false ) , 3000 ) ;
141120 } else {
142121 alert ( "Failed to save project." ) ;
143122 }
144123 } catch ( error ) {
145124 console . error ( "Error saving project:" , error ) ;
146125 } finally {
147- setLoading ( false ) ; // End submission
126+ setLoading ( false ) ;
148127 }
149128 } ;
150129
@@ -154,7 +133,18 @@ const AddProjectPage = () => {
154133 >
155134 ) => {
156135 const { name, value } = e . target ;
157- setFormData ( ( prev ) => ( { ...prev , [ name ] : value } ) ) ;
136+ if ( name . startsWith ( "category-" ) ) {
137+ const categoryName = name . replace ( "category-" , "" ) ;
138+ setFormData ( ( prev ) => ( {
139+ ...prev ,
140+ selectedCategoryOptions : {
141+ ...prev . selectedCategoryOptions ,
142+ [ categoryName ] : value ,
143+ } ,
144+ } ) ) ;
145+ } else {
146+ setFormData ( ( prev ) => ( { ...prev , [ name ] : value } ) ) ;
147+ }
158148 } ;
159149
160150 const handleMemberChange = ( index : number , field : string , value : string ) => {
@@ -179,6 +169,14 @@ const AddProjectPage = () => {
179169 setFormData ( initialFormState ) ;
180170 } ;
181171
172+ if ( loadingCategories ) {
173+ return < div className = "text-center py-8" > Loading form data...</ div > ;
174+ }
175+
176+ if ( errorCategories ) {
177+ return < div className = "text-center py-8 text-red-500" > Error loading form data: { errorCategories } </ div > ;
178+ }
179+
182180 return (
183181 < div className = "min-h-screen bg-gradient-to-br from-gray-50 to-gray-100" >
184182 < div className = "bg-white shadow-sm" >
@@ -234,68 +232,40 @@ const AddProjectPage = () => {
234232 onChange = { handleChange }
235233 value = { formData . projectDescription }
236234 />
237- < select
238- name = "yearOfSubmission"
239- required
240- className = "w-full px-4 py-2 border rounded-lg"
241- onChange = { handleChange }
242- value = { formData . yearOfSubmission }
243- >
244- { submissionYears . map ( ( year ) => (
245- < option key = { year } value = { year } >
246- { year }
247- </ option >
248- ) ) }
249- </ select >
250- < select
251- name = "projectType"
252- required
253- className = "w-full px-4 py-2 border rounded-lg"
254- onChange = { handleChange }
255- value = { formData . projectType }
256- >
257- { projectTypes . map ( ( type ) => (
258- < option key = { type } value = { type } >
259- { type }
260- </ option >
261- ) ) }
262- </ select >
263- < select
264- name = "department"
265- required
266- className = "w-full px-4 py-2 border rounded-lg"
267- onChange = { handleChange }
268- value = { formData . department }
269- >
270- { departments . map ( ( dept ) => (
271- < option key = { dept } value = { dept } >
272- { dept }
273- </ option >
274- ) ) }
275- </ select >
276- < select
277- name = "domain"
278- required
279- className = "w-full px-4 py-2 border rounded-lg"
280- onChange = { handleChange }
281- value = { formData . domain }
282- >
283- { availableDomains . map ( ( domain ) => (
284- < option key = { domain } value = { domain } >
285- { domain }
286- </ option >
287- ) ) }
288- </ select >
289- { formData . domain === "Other" && (
290- < input
291- type = "text"
292- name = "customDomain"
293- className = "w-full px-4 py-2 border rounded-lg"
294- placeholder = "Enter custom domain"
295- onChange = { handleChange }
296- value = { formData . customDomain }
297- />
298- ) }
235+
236+ { /* Dynamic Category Selects */ }
237+ { categories . length > 0 && categories . map ( category => (
238+ < div key = { category . categoryId } className = "mb-4" >
239+ < label htmlFor = { `category-${ category . categoryName } ` } className = "block text-sm font-medium text-gray-700" >
240+ { category . categoryName }
241+ </ label >
242+ < select
243+ id = { `category-${ category . categoryName } ` }
244+ name = { `category-${ category . categoryName } ` }
245+ required
246+ className = "w-full px-4 py-2 border rounded-lg"
247+ onChange = { handleChange }
248+ value = { formData . selectedCategoryOptions [ category . categoryName ] || '' }
249+ >
250+ { category . options . map ( ( option ) => (
251+ < option key = { option . optionId } value = { option . optionName } >
252+ { option . optionName }
253+ </ option >
254+ ) ) }
255+ </ select >
256+ { category . categoryName === "Domain" && formData . selectedCategoryOptions [ "Domain" ] === "Other" && (
257+ < input
258+ type = "text"
259+ name = "customDomain"
260+ className = "mt-2 w-full px-4 py-2 border rounded-lg"
261+ placeholder = "Enter custom domain"
262+ onChange = { handleChange }
263+ value = { formData . customDomain }
264+ />
265+ ) }
266+ </ div >
267+ ) ) }
268+
299269 < input
300270 type = "url"
301271 name = "projectLink"
0 commit comments