Skip to content

Commit bf991e6

Browse files
Issues in the database schema solved. Issues in the creation of new category and it's optional value resolved. Now when the admin add an category the category and it's optional values automatically add in the filter section and add project form (page). problem in the editing and deleting the project resolved
1 parent a4bf8d1 commit bf991e6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5882
-510
lines changed

app/add-project/page.tsx

Lines changed: 132 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,80 @@
11
"use client";
22

3-
import React, { useState } from "react";
3+
import React, { useState, useEffect } from "react";
44
import { ArrowLeft, Save, XCircle } from "lucide-react";
55
import 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

8718
const 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

Comments
 (0)