From 26663b1c58223039cbd4721fd9d189baaab3d365 Mon Sep 17 00:00:00 2001 From: Atul Gunjal Date: Wed, 9 Apr 2025 17:14:27 +0530 Subject: [PATCH] submission from employee side done. --- src/app/components/AssignmentCard.js | 0 src/app/components/MemberList.js | 118 ++++++++---- src/app/lib/appwrite.js | 2 +- src/app/pages/admin/layout.js | 18 -- src/app/pages/admin/members/layout.js | 18 -- src/app/pages/assignments/page.js | 261 ++++++++++++++++++-------- src/app/pages/submissionPage/page.js | 226 ++++++++++++++++++++++ src/app/utils/auth.js | 47 +++-- 8 files changed, 526 insertions(+), 164 deletions(-) delete mode 100644 src/app/components/AssignmentCard.js delete mode 100644 src/app/pages/admin/layout.js delete mode 100644 src/app/pages/admin/members/layout.js create mode 100644 src/app/pages/submissionPage/page.js diff --git a/src/app/components/AssignmentCard.js b/src/app/components/AssignmentCard.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/components/MemberList.js b/src/app/components/MemberList.js index 8fb9686..c666099 100644 --- a/src/app/components/MemberList.js +++ b/src/app/components/MemberList.js @@ -4,57 +4,103 @@ import { useEffect, useState } from "react"; import { databases } from "../lib/appwrite"; import { databaseId, membersCollectionId } from "../lib/appwrite"; import { getCurrentUserWithRole } from "../utils/auth"; +import { Query } from "appwrite"; export default function MemberList() { const [members, setMembers] = useState([]); - const [user, setUser] = useState(null); + const [currentUser, setCurrentUser] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { - getCurrentUserWithRole().then(setUser); - }, []); - - useEffect(() => { - const getMembers = async () => { + const fetchData = async () => { try { + // Get current user with role first + const user = await getCurrentUserWithRole(); + setCurrentUser(user); + + // Then fetch members with proper error handling const res = await databases.listDocuments( databaseId, - membersCollectionId + membersCollectionId, + [Query.orderDesc("$createdAt")] // Optional: sort by creation date ); - setMembers(res.documents); // <-- Add this line + + setMembers(res.documents); } catch (err) { - console.error("Error fetching members:", err); + console.error("Error fetching data:", err); + setError("Failed to load member data"); + } finally { + setLoading(false); } }; - getMembers(); + fetchData(); }, []); - return ( -
-

All Members

- - - - - - - - - - - {members.map((m) => ( - - - - - - - ))} - -
NamePhoneJoin DateRole
{m.name}{m.phone}{m.joinDate}{m.role}
+ if (loading) { + return
Loading members...
; + } - {/* Render role only if user is loaded */} - {user ?

Role: {user.role}

:

Loading role...

} + if (error) { + return
{error}
; + } + + return ( +
+

All Members

+ + {/* Current user info */} + {currentUser && ( +
+

You are logged in as: {currentUser.name}

+

Your role: {currentUser.role}

+
+ )} + + {/* Members table */} +
+ + + + + + + + + + + + {members.length > 0 ? ( + members.map((member) => ( + + + + + + + + )) + ) : ( + + + + )} + +
NameEmailPhoneJoin DateRole
{member.name || "N/A"}{member.email || "N/A"}{member.phone || "N/A"} + {member.joinDate ? new Date(member.joinDate).toLocaleDateString() : "N/A"} + + + {member.role || "unknown"} + +
+ No members found +
+
); -} +} \ No newline at end of file diff --git a/src/app/lib/appwrite.js b/src/app/lib/appwrite.js index da1cadc..3ff4847 100644 --- a/src/app/lib/appwrite.js +++ b/src/app/lib/appwrite.js @@ -9,7 +9,7 @@ client const account = new Account(client); const databases = new Databases(client); -// ✅ Import from .env +// Import from .env const databaseId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID; const membersCollectionId = process.env.NEXT_PUBLIC_COLLECTION_MEMBERS_ID; diff --git a/src/app/pages/admin/layout.js b/src/app/pages/admin/layout.js deleted file mode 100644 index 9f238e0..0000000 --- a/src/app/pages/admin/layout.js +++ /dev/null @@ -1,18 +0,0 @@ -// app/admin/members/layout.js - -export const metadata = { - title: "Manage Members | Admin", - description: "Add or view team members in the portal", - }; - - export default function AdminLayout({ children }) { - return ( -
-

Team Members

-
- {children} -
-
- ); - } - \ No newline at end of file diff --git a/src/app/pages/admin/members/layout.js b/src/app/pages/admin/members/layout.js deleted file mode 100644 index c2a961c..0000000 --- a/src/app/pages/admin/members/layout.js +++ /dev/null @@ -1,18 +0,0 @@ -// app/admin/members/layout.js - -export const metadata = { - title: "Manage Members | Admin", - description: "Add or view team members in the portal", - }; - - export default function MembersLayout({ children }) { - return ( -
-

Team Members

-
- {children} -
-
- ); - } - \ No newline at end of file diff --git a/src/app/pages/assignments/page.js b/src/app/pages/assignments/page.js index ea05c9a..1598dbb 100644 --- a/src/app/pages/assignments/page.js +++ b/src/app/pages/assignments/page.js @@ -1,120 +1,225 @@ "use client"; - import { useEffect, useState } from "react"; -import { databases, account } from "../../lib/appwrite"; +import { databases } from "../../lib/appwrite"; import { ID } from "appwrite"; +import { getCurrentUserWithRole } from "../../utils/auth"; export default function AssignmentsPage() { const [assignments, setAssignments] = useState([]); const [submissionLinks, setSubmissionLinks] = useState({}); - const [userId, setUserId] = useState(null); - const [loadingUser, setLoadingUser] = useState(true); // 🟡 Loading state + const [currentUser, setCurrentUser] = useState(null); + const [loading, setLoading] = useState(true); + const [newAssignment, setNewAssignment] = useState({ + title: "", + deadline: "" + }); - // Fetch assignments and user on mount + // Fetch assignments and user useEffect(() => { - const fetchAssignments = async () => { + const fetchData = async () => { try { - const response = await databases.listDocuments( - "67e1452b00016444b37f", - "67f0f285001a1614b4e8" - ); - setAssignments(response.documents); + const [user, assignmentsResponse] = await Promise.all([ + getCurrentUserWithRole(), + databases.listDocuments( + "67e1452b00016444b37f", + "67f0f285001a1614b4e8" + ) + ]); + setCurrentUser(user); + setAssignments(assignmentsResponse.documents); } catch (error) { - console.error("Error fetching assignments:", error); - } - }; - - const fetchUser = async () => { - try { - const user = await account.get(); - setUserId(user.$id); - } catch (error) { - console.error("User not logged in:", error); + console.error("Error:", error); } finally { - setLoadingUser(false); // ✅ Done loading + setLoading(false); } }; - fetchAssignments(); - fetchUser(); + fetchData(); }, []); - const handleInputChange = (assignmentId, value) => { - setSubmissionLinks({ - ...submissionLinks, - [assignmentId]: value, - }); - }; - - const handleSubmit = async (assignmentId) => { - if (!userId) { - alert("User not logged in. Cannot submit."); - return; - } - - const submissionLink = submissionLinks[assignmentId]; - if (!submissionLink) { - alert("Submission link is empty."); + const handleCreateAssignment = async () => { + if (!currentUser || currentUser.role !== 'admin') { + alert("Only admins can create assignments"); return; } try { await databases.createDocument( "67e1452b00016444b37f", - "67f5ea0a000393d31806", // ✅ Replace with correct ID + "67f0f285001a1614b4e8", ID.unique(), { - assignmentId, - userId, - submissionLink, + title: newAssignment.title, + deadline: newAssignment.deadline, + userId: currentUser.$id, // Add current user's ID + submitted: false, + submittedLate: false, + submittedLink: "" } ); - alert("Submitted!"); + + // Refresh assignments + const updated = await databases.listDocuments( + "67e1452b00016444b37f", + "67f0f285001a1614b4e8" + ); + setAssignments(updated.documents); + setNewAssignment({ title: "", deadline: "" }); + + alert("Assignment created successfully!"); } catch (error) { - console.error("Submission failed:", error); - alert("Submission failed: " + error.message); + console.error("Creation failed:", error); + alert(`Error: ${error.message}`); } }; - if (loadingUser) { - return
Loading...
; // Optional loading indicator - } + const handleSubmitAssignment = async (assignmentId) => { + const link = submissionLinks[assignmentId]; + if (!link) { + alert("Please provide a submission link"); + return; + } + + try { + const isLate = new Date() > new Date( + assignments.find(a => a.$id === assignmentId).deadline + ); + + await databases.updateDocument( + "67e1452b00016444b37f", + "67f0f285001a1614b4e8", + assignmentId, + { + submitted: true, + submittedLate: isLate, + submittedLink: link + } + ); + + // Refresh assignments + const updated = await databases.listDocuments( + "67e1452b00016444b37f", + "67f0f285001a1614b4e8" + ); + setAssignments(updated.documents); + setSubmissionLinks({...submissionLinks, [assignmentId]: ""}); + + alert(`Submission ${isLate ? 'completed (late)' : 'completed on time'}`); + } catch (error) { + console.error("Submission failed:", error); + alert("Submission failed"); + } + }; + + if (loading) return
Loading...
; return ( -
+

Assignments

+ + {currentUser && ( +

Role: {currentUser.role}

+ )} - {assignments.length === 0 ? ( -

No assignments available.

- ) : ( - assignments.map((assignment) => ( -
-

{assignment.title}

-

{assignment.description}

-

- Deadline: {assignment.deadline} -

- - - handleInputChange(assignment.$id, e.target.value) - } - /> - + {/* Admin-only creation form */} + {currentUser?.role === 'admin' && ( +
+

Create New Assignment

+
+
+ + setNewAssignment({...newAssignment, title: e.target.value})} + className="w-full border px-3 py-2 rounded" + required + /> +
+
+ + setNewAssignment({...newAssignment, deadline: e.target.value})} + className="border px-3 py-2 rounded" + required + /> +
- )) +
)} + + {/* Assignments list */} +
+ {assignments.length === 0 ? ( +

No assignments available.

+ ) : ( + assignments.map((assignment) => ( +
+
+
+

{assignment.title}

+

+ Deadline: {new Date(assignment.deadline).toLocaleString()} +

+
+ {assignment.submitted && ( + + {assignment.submittedLate ? 'Late' : 'Submitted'} + + )} +
+ + {!assignment.submitted && currentUser?.role !== 'admin' && ( +
+ setSubmissionLinks({ + ...submissionLinks, + [assignment.$id]: e.target.value + })} + className="w-full border px-3 py-2 rounded" + /> + +
+ )} + + {assignment.submitted && assignment.submittedLink && ( + + )} +
+ )) + )} +
); -} +} \ No newline at end of file diff --git a/src/app/pages/submissionPage/page.js b/src/app/pages/submissionPage/page.js new file mode 100644 index 0000000..5307fa7 --- /dev/null +++ b/src/app/pages/submissionPage/page.js @@ -0,0 +1,226 @@ +"use client"; +import { useEffect, useState } from "react"; +import { databases } from "../../lib/appwrite"; +import { ID, Query } from "appwrite"; +import { getCurrentUserWithRole } from "../../utils/auth"; + +export default function SubmissionPage() { + const [assignments, setAssignments] = useState([]); + const [submissionLink, setSubmissionLink] = useState(""); + const [selectedAssignment, setSelectedAssignment] = useState(""); + const [currentUser, setCurrentUser] = useState(null); + const [loading, setLoading] = useState(true); + + // Database IDs + const databaseId = "67e1452b00016444b37f"; + const assignmentsCollectionId = "67f0f285001a1614b4e8"; + const submissionsCollectionId = "67f5ea0a000393d31806"; + const employeeCollectionId = "67f143f00010e2cd2652"; // FSPL HR Employee Management + + useEffect(() => { + const fetchData = async () => { + try { + const [user, assignmentsResponse] = await Promise.all([ + getCurrentUserWithRole(), + databases.listDocuments(databaseId, assignmentsCollectionId, [ + Query.equal("submitted", false) + ]) + ]); + setCurrentUser(user); + setAssignments(assignmentsResponse.documents); + } catch (error) { + console.error("Error:", error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + + const handleSubmit = async () => { + if (!selectedAssignment || !submissionLink || !currentUser?.$id) { + alert("Please fill all required fields"); + return; + } + + try { + // 1. Create the submission document + await databases.createDocument( + databaseId, + submissionsCollectionId, + ID.unique(), + { + assignmentId: selectedAssignment, + userId: currentUser.$id, + submissionLink + } + ); + + // 2. Update the assignment status + const assignment = assignments.find(a => a.$id === selectedAssignment); + await databases.updateDocument( + databaseId, + assignmentsCollectionId, + selectedAssignment, + { + submitted: true, + submittedLate: new Date() > new Date(assignment.deadline), + submittedLink: submissionLink + } + ); + + // 3. Optionally update employee record if needed + try { + await databases.updateDocument( + databaseId, + employeeCollectionId, + currentUser.$id, // Assuming document ID matches user ID + { + lastSubmission: new Date().toISOString(), + lastSubmissionLink: submissionLink + } + ); + } catch (employeeError) { + console.log("Employee record not updated (optional):", employeeError); + } + + alert("Submission successful!"); + setSubmissionLink(""); + setSelectedAssignment(""); + + // Refresh assignments list + const updated = await databases.listDocuments( + databaseId, + assignmentsCollectionId, + [Query.equal("submitted", false)] + ); + setAssignments(updated.documents); + } catch (error) { + console.error("Submission failed:", error); + alert(`Submission failed: ${error.message}`); + } + }; + + if (loading) return
Loading...
; + + return ( +
+

Submit Assignment

+ +
+
+ + +
+ +
+ + setSubmissionLink(e.target.value)} + className="w-full border px-3 py-2 rounded" + required + /> +

GitHub, Gitea, or other repository link

+
+ + +
+ + {/* Previous Submissions Section */} +
+

Your Submissions

+
+ + + + + + + + + + {currentUser?.$id && ( + + )} + +
AssignmentSubmission LinkSubmitted At
+
+
+
+ ); +} + +// Separate component for fetching and displaying submissions +function SubmissionList({ userId, assignments }) { + const [submissions, setSubmissions] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchSubmissions = async () => { + try { + const response = await databases.listDocuments( + "67e1452b00016444b37f", + "67f5ea0a000393d31806", + [Query.equal("userId", userId)] + ); + setSubmissions(response.documents); + } catch (error) { + console.error("Error fetching submissions:", error); + } finally { + setLoading(false); + } + }; + + fetchSubmissions(); + }, [userId]); + + if (loading) return Loading submissions...; + if (submissions.length === 0) return No submissions found; + + return submissions.map((submission) => { + const assignment = assignments.find(a => a.$id === submission.assignmentId); + return ( + + {assignment?.title || submission.assignmentId} + + + View Submission + + + + {new Date(submission.$createdAt).toLocaleString()} + + + ); + }); +} \ No newline at end of file diff --git a/src/app/utils/auth.js b/src/app/utils/auth.js index 7aeb8f2..45f44ec 100644 --- a/src/app/utils/auth.js +++ b/src/app/utils/auth.js @@ -9,31 +9,52 @@ const databases = new Databases(client); export async function getCurrentUserWithRole() { try { + // 1. Get current user from Auth const user = await account.get(); if (!user || !user.$id) { throw new Error("User not authenticated"); } + // 2. Database constants const dbId = "67e1452b00016444b37f"; - const collectionId = "67f0f1200006897dc192"; + const collectionId = "67f0f1200006897dc192"; // Your employee collection - const response = await databases.listDocuments(dbId, collectionId, [ - Query.equal("userId", user.$id), - ]); + // 3. Try to get employee record + let role = "unknown"; // Default role if not found + let userDoc = null; + + try { + const response = await databases.listDocuments(dbId, collectionId, [ + Query.equal("userId", user.$id), + Query.limit(1) + ]); - const userDoc = response.documents[0]; + if (response.documents.length > 0) { + userDoc = response.documents[0]; + role = userDoc.role || "employee"; // Use "employee" if role exists but is empty + } + } catch (dbError) { + console.error("Database error:", dbError); + // Continue with default role + } + // 4. Return combined data return { - $id: user.$id, // Make sure to include this - name: user.name, - email: user.email, - role: userDoc?.role || "unknown", - // Include the document ID if needed - docId: userDoc?.$id + ...user, // All original user properties + role, // Determined role ("admin", "employee", or "unknown") + isAdmin: role === "admin", // Convenience boolean + employeeDoc: userDoc // Full employee document if available }; + } catch (error) { - console.error("Error fetching user with role:", error); - throw error; // Throw instead of returning null + console.error("Error in getCurrentUserWithRole:", error); + + // Convert Appwrite errors to more user-friendly messages + if (error.type === "general_unauthorized_scope") { + throw new Error("Please login to access this feature"); + } + + throw error; // Re-throw for components to handle } } \ No newline at end of file