submission from employee side done.
This commit is contained in:
parent
a7b6b78890
commit
26663b1c58
@ -4,57 +4,103 @@ import { useEffect, useState } from "react";
|
|||||||
import { databases } from "../lib/appwrite";
|
import { databases } from "../lib/appwrite";
|
||||||
import { databaseId, membersCollectionId } from "../lib/appwrite";
|
import { databaseId, membersCollectionId } from "../lib/appwrite";
|
||||||
import { getCurrentUserWithRole } from "../utils/auth";
|
import { getCurrentUserWithRole } from "../utils/auth";
|
||||||
|
import { Query } from "appwrite";
|
||||||
|
|
||||||
export default function MemberList() {
|
export default function MemberList() {
|
||||||
const [members, setMembers] = useState([]);
|
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(() => {
|
useEffect(() => {
|
||||||
getCurrentUserWithRole().then(setUser);
|
const fetchData = async () => {
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const getMembers = async () => {
|
|
||||||
try {
|
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(
|
const res = await databases.listDocuments(
|
||||||
databaseId,
|
databaseId,
|
||||||
membersCollectionId
|
membersCollectionId,
|
||||||
|
[Query.orderDesc("$createdAt")] // Optional: sort by creation date
|
||||||
);
|
);
|
||||||
setMembers(res.documents); // <-- Add this line
|
|
||||||
|
setMembers(res.documents);
|
||||||
} catch (err) {
|
} 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 (
|
if (loading) {
|
||||||
<div className="mt-8">
|
return <div className="p-4">Loading members...</div>;
|
||||||
<h2 className="text-xl font-semibold mb-4">All Members</h2>
|
}
|
||||||
<table className="w-full border">
|
|
||||||
<thead>
|
|
||||||
<tr className="bg-gray-100">
|
|
||||||
<th className="p-2 text-left">Name</th>
|
|
||||||
<th className="p-2">Phone</th>
|
|
||||||
<th className="p-2">Join Date</th>
|
|
||||||
<th className="p-2">Role</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{members.map((m) => (
|
|
||||||
<tr key={m.$id} className="border-t">
|
|
||||||
<td className="p-2">{m.name}</td>
|
|
||||||
<td className="p-2">{m.phone}</td>
|
|
||||||
<td className="p-2">{m.joinDate}</td>
|
|
||||||
<td className="p-2">{m.role}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{/* Render role only if user is loaded */}
|
if (error) {
|
||||||
{user ? <p className="mt-4">Role: {user.role}</p> : <p>Loading role...</p>}
|
return <div className="p-4 text-red-500">{error}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-8 p-4">
|
||||||
|
<h2 className="text-xl font-semibold mb-4">All Members</h2>
|
||||||
|
|
||||||
|
{/* Current user info */}
|
||||||
|
{currentUser && (
|
||||||
|
<div className="mb-6 p-4 bg-blue-50 rounded-lg">
|
||||||
|
<p className="font-medium">You are logged in as: {currentUser.name}</p>
|
||||||
|
<p>Your role: <span className="capitalize font-semibold">{currentUser.role}</span></p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Members table */}
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full border">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gray-100">
|
||||||
|
<th className="p-3 text-left">Name</th>
|
||||||
|
<th className="p-3">Email</th>
|
||||||
|
<th className="p-3">Phone</th>
|
||||||
|
<th className="p-3">Join Date</th>
|
||||||
|
<th className="p-3">Role</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{members.length > 0 ? (
|
||||||
|
members.map((member) => (
|
||||||
|
<tr key={member.$id} className="border-t hover:bg-gray-50">
|
||||||
|
<td className="p-3">{member.name || "N/A"}</td>
|
||||||
|
<td className="p-3">{member.email || "N/A"}</td>
|
||||||
|
<td className="p-3">{member.phone || "N/A"}</td>
|
||||||
|
<td className="p-3">
|
||||||
|
{member.joinDate ? new Date(member.joinDate).toLocaleDateString() : "N/A"}
|
||||||
|
</td>
|
||||||
|
<td className="p-3 capitalize">
|
||||||
|
<span className={`px-2 py-1 rounded-full text-xs ${
|
||||||
|
member.role === "admin"
|
||||||
|
? "bg-purple-100 text-purple-800"
|
||||||
|
: "bg-blue-100 text-blue-800"
|
||||||
|
}`}>
|
||||||
|
{member.role || "unknown"}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan="5" className="p-4 text-center text-gray-500">
|
||||||
|
No members found
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ client
|
|||||||
const account = new Account(client);
|
const account = new Account(client);
|
||||||
const databases = new Databases(client);
|
const databases = new Databases(client);
|
||||||
|
|
||||||
// ✅ Import from .env
|
// Import from .env
|
||||||
const databaseId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID;
|
const databaseId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID;
|
||||||
const membersCollectionId = process.env.NEXT_PUBLIC_COLLECTION_MEMBERS_ID;
|
const membersCollectionId = process.env.NEXT_PUBLIC_COLLECTION_MEMBERS_ID;
|
||||||
|
|
||||||
|
@ -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 (
|
|
||||||
<section className="p-6">
|
|
||||||
<h1 className="text-2xl font-bold mb-4">Team Members</h1>
|
|
||||||
<div className="bg-white shadow-md rounded-lg p-4">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -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 (
|
|
||||||
<section className="p-6">
|
|
||||||
<h1 className="text-2xl font-bold mb-4">Team Members</h1>
|
|
||||||
<div className="bg-white shadow-md rounded-lg p-4">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,120 +1,225 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { databases, account } from "../../lib/appwrite";
|
import { databases } from "../../lib/appwrite";
|
||||||
import { ID } from "appwrite";
|
import { ID } from "appwrite";
|
||||||
|
import { getCurrentUserWithRole } from "../../utils/auth";
|
||||||
|
|
||||||
export default function AssignmentsPage() {
|
export default function AssignmentsPage() {
|
||||||
const [assignments, setAssignments] = useState([]);
|
const [assignments, setAssignments] = useState([]);
|
||||||
const [submissionLinks, setSubmissionLinks] = useState({});
|
const [submissionLinks, setSubmissionLinks] = useState({});
|
||||||
const [userId, setUserId] = useState(null);
|
const [currentUser, setCurrentUser] = useState(null);
|
||||||
const [loadingUser, setLoadingUser] = useState(true); // 🟡 Loading state
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [newAssignment, setNewAssignment] = useState({
|
||||||
|
title: "",
|
||||||
|
deadline: ""
|
||||||
|
});
|
||||||
|
|
||||||
// Fetch assignments and user on mount
|
// Fetch assignments and user
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAssignments = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await databases.listDocuments(
|
const [user, assignmentsResponse] = await Promise.all([
|
||||||
"67e1452b00016444b37f",
|
getCurrentUserWithRole(),
|
||||||
"67f0f285001a1614b4e8"
|
databases.listDocuments(
|
||||||
);
|
"67e1452b00016444b37f",
|
||||||
setAssignments(response.documents);
|
"67f0f285001a1614b4e8"
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
setCurrentUser(user);
|
||||||
|
setAssignments(assignmentsResponse.documents);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching assignments:", error);
|
console.error("Error:", error);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchUser = async () => {
|
|
||||||
try {
|
|
||||||
const user = await account.get();
|
|
||||||
setUserId(user.$id);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("User not logged in:", error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingUser(false); // ✅ Done loading
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchAssignments();
|
fetchData();
|
||||||
fetchUser();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleInputChange = (assignmentId, value) => {
|
const handleCreateAssignment = async () => {
|
||||||
setSubmissionLinks({
|
if (!currentUser || currentUser.role !== 'admin') {
|
||||||
...submissionLinks,
|
alert("Only admins can create assignments");
|
||||||
[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.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await databases.createDocument(
|
await databases.createDocument(
|
||||||
"67e1452b00016444b37f",
|
"67e1452b00016444b37f",
|
||||||
"67f5ea0a000393d31806", // ✅ Replace with correct ID
|
"67f0f285001a1614b4e8",
|
||||||
ID.unique(),
|
ID.unique(),
|
||||||
{
|
{
|
||||||
assignmentId,
|
title: newAssignment.title,
|
||||||
userId,
|
deadline: newAssignment.deadline,
|
||||||
submissionLink,
|
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) {
|
} catch (error) {
|
||||||
console.error("Submission failed:", error);
|
console.error("Creation failed:", error);
|
||||||
alert("Submission failed: " + error.message);
|
alert(`Error: ${error.message}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loadingUser) {
|
const handleSubmitAssignment = async (assignmentId) => {
|
||||||
return <div>Loading...</div>; // Optional loading indicator
|
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 <div>Loading...</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="p-4">
|
||||||
<h1 className="text-2xl font-bold mb-4">Assignments</h1>
|
<h1 className="text-2xl font-bold mb-4">Assignments</h1>
|
||||||
|
|
||||||
|
{currentUser && (
|
||||||
|
<p className="mb-4">Role: <span className="capitalize font-semibold">{currentUser.role}</span></p>
|
||||||
|
)}
|
||||||
|
|
||||||
{assignments.length === 0 ? (
|
{/* Admin-only creation form */}
|
||||||
<p>No assignments available.</p>
|
{currentUser?.role === 'admin' && (
|
||||||
) : (
|
<div className="mb-6 p-4 border rounded-lg bg-gray-50">
|
||||||
assignments.map((assignment) => (
|
<h2 className="text-xl font-semibold mb-4">Create New Assignment</h2>
|
||||||
<div key={assignment.$id} className="mb-6 border p-4 rounded-lg">
|
<div className="space-y-4">
|
||||||
<h2 className="text-xl font-semibold">{assignment.title}</h2>
|
<div>
|
||||||
<p className="text-gray-600">{assignment.description}</p>
|
<label className="block text-sm font-medium mb-1">Title*</label>
|
||||||
<p className="text-sm text-gray-400">
|
<input
|
||||||
Deadline: {assignment.deadline}
|
type="text"
|
||||||
</p>
|
value={newAssignment.title}
|
||||||
|
onChange={(e) => setNewAssignment({...newAssignment, title: e.target.value})}
|
||||||
<input
|
className="w-full border px-3 py-2 rounded"
|
||||||
type="text"
|
required
|
||||||
className="mt-2 border px-2 py-1 w-full"
|
/>
|
||||||
placeholder="Paste your GitHub/Firebase/Netlify link"
|
</div>
|
||||||
value={submissionLinks[assignment.$id] || ""}
|
<div>
|
||||||
onChange={(e) =>
|
<label className="block text-sm font-medium mb-1">Deadline*</label>
|
||||||
handleInputChange(assignment.$id, e.target.value)
|
<input
|
||||||
}
|
type="datetime-local"
|
||||||
/>
|
value={newAssignment.deadline}
|
||||||
|
onChange={(e) => setNewAssignment({...newAssignment, deadline: e.target.value})}
|
||||||
|
className="border px-3 py-2 rounded"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubmit(assignment.$id)}
|
onClick={handleCreateAssignment}
|
||||||
className="mt-2 bg-blue-600 text-white px-4 py-1 rounded"
|
className="bg-green-600 text-white px-4 py-2 rounded disabled:bg-gray-400"
|
||||||
disabled={!submissionLinks[assignment.$id]}
|
disabled={!newAssignment.title || !newAssignment.deadline}
|
||||||
>
|
>
|
||||||
Submit
|
Create Assignment
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Assignments list */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
{assignments.length === 0 ? (
|
||||||
|
<p>No assignments available.</p>
|
||||||
|
) : (
|
||||||
|
assignments.map((assignment) => (
|
||||||
|
<div key={assignment.$id} className="border p-4 rounded-lg">
|
||||||
|
<div className="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold">{assignment.title}</h2>
|
||||||
|
<p className="text-sm text-gray-600 mt-1">
|
||||||
|
Deadline: {new Date(assignment.deadline).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{assignment.submitted && (
|
||||||
|
<span className={`px-2 py-1 text-xs rounded-full ${
|
||||||
|
assignment.submittedLate
|
||||||
|
? 'bg-red-100 text-red-800'
|
||||||
|
: 'bg-green-100 text-green-800'
|
||||||
|
}`}>
|
||||||
|
{assignment.submittedLate ? 'Late' : 'Submitted'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!assignment.submitted && currentUser?.role !== 'admin' && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Paste submission link"
|
||||||
|
value={submissionLinks[assignment.$id] || ""}
|
||||||
|
onChange={(e) => setSubmissionLinks({
|
||||||
|
...submissionLinks,
|
||||||
|
[assignment.$id]: e.target.value
|
||||||
|
})}
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => handleSubmitAssignment(assignment.$id)}
|
||||||
|
className="mt-2 bg-blue-600 text-white px-4 py-2 rounded"
|
||||||
|
disabled={!submissionLinks[assignment.$id]}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{assignment.submitted && assignment.submittedLink && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<a
|
||||||
|
href={assignment.submittedLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
View Submission
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
226
src/app/pages/submissionPage/page.js
Normal file
226
src/app/pages/submissionPage/page.js
Normal file
@ -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 <div className="p-4">Loading...</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h1 className="text-2xl font-bold mb-6">Submit Assignment</h1>
|
||||||
|
|
||||||
|
<div className="space-y-4 max-w-md">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-1">Select Assignment*</label>
|
||||||
|
<select
|
||||||
|
value={selectedAssignment}
|
||||||
|
onChange={(e) => setSelectedAssignment(e.target.value)}
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="">-- Select Assignment --</option>
|
||||||
|
{assignments.map((assignment) => (
|
||||||
|
<option key={assignment.$id} value={assignment.$id}>
|
||||||
|
{assignment.title} (Due: {new Date(assignment.deadline).toLocaleDateString()})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-1">Submission Link*</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
placeholder="https://github.com/your-repo"
|
||||||
|
value={submissionLink}
|
||||||
|
onChange={(e) => setSubmissionLink(e.target.value)}
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">GitHub, Gitea, or other repository link</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700 disabled:bg-gray-400"
|
||||||
|
disabled={!selectedAssignment || !submissionLink}
|
||||||
|
>
|
||||||
|
Submit Assignment
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Previous Submissions Section */}
|
||||||
|
<div className="mt-8">
|
||||||
|
<h2 className="text-xl font-semibold mb-4">Your Submissions</h2>
|
||||||
|
<div className="border rounded-lg overflow-hidden">
|
||||||
|
<table className="w-full">
|
||||||
|
<thead className="bg-gray-100">
|
||||||
|
<tr>
|
||||||
|
<th className="p-3 text-left">Assignment</th>
|
||||||
|
<th className="p-3 text-left">Submission Link</th>
|
||||||
|
<th className="p-3 text-left">Submitted At</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{currentUser?.$id && (
|
||||||
|
<SubmissionList
|
||||||
|
userId={currentUser.$id}
|
||||||
|
assignments={assignments}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 <tr><td colSpan="3" className="p-4 text-center">Loading submissions...</td></tr>;
|
||||||
|
if (submissions.length === 0) return <tr><td colSpan="3" className="p-4 text-center">No submissions found</td></tr>;
|
||||||
|
|
||||||
|
return submissions.map((submission) => {
|
||||||
|
const assignment = assignments.find(a => a.$id === submission.assignmentId);
|
||||||
|
return (
|
||||||
|
<tr key={submission.$id} className="border-t hover:bg-gray-50">
|
||||||
|
<td className="p-3">{assignment?.title || submission.assignmentId}</td>
|
||||||
|
<td className="p-3">
|
||||||
|
<a
|
||||||
|
href={submission.submissionLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
View Submission
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td className="p-3">
|
||||||
|
{new Date(submission.$createdAt).toLocaleString()}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -9,31 +9,52 @@ const databases = new Databases(client);
|
|||||||
|
|
||||||
export async function getCurrentUserWithRole() {
|
export async function getCurrentUserWithRole() {
|
||||||
try {
|
try {
|
||||||
|
// 1. Get current user from Auth
|
||||||
const user = await account.get();
|
const user = await account.get();
|
||||||
|
|
||||||
if (!user || !user.$id) {
|
if (!user || !user.$id) {
|
||||||
throw new Error("User not authenticated");
|
throw new Error("User not authenticated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Database constants
|
||||||
const dbId = "67e1452b00016444b37f";
|
const dbId = "67e1452b00016444b37f";
|
||||||
const collectionId = "67f0f1200006897dc192";
|
const collectionId = "67f0f1200006897dc192"; // Your employee collection
|
||||||
|
|
||||||
const response = await databases.listDocuments(dbId, collectionId, [
|
// 3. Try to get employee record
|
||||||
Query.equal("userId", user.$id),
|
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 {
|
return {
|
||||||
$id: user.$id, // Make sure to include this
|
...user, // All original user properties
|
||||||
name: user.name,
|
role, // Determined role ("admin", "employee", or "unknown")
|
||||||
email: user.email,
|
isAdmin: role === "admin", // Convenience boolean
|
||||||
role: userDoc?.role || "unknown",
|
employeeDoc: userDoc // Full employee document if available
|
||||||
// Include the document ID if needed
|
|
||||||
docId: userDoc?.$id
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching user with role:", error);
|
console.error("Error in getCurrentUserWithRole:", error);
|
||||||
throw error; // Throw instead of returning null
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user