Skip to content

Commit 7b2e33f

Browse files
Merge pull request #214 from UtkarshUmap/POR_Profile
Create POR profile page.
2 parents b9c6a81 + 6c7a49f commit 7b2e33f

File tree

2 files changed

+207
-1
lines changed

2 files changed

+207
-1
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import React, { useEffect, useState } from "react";
2+
import {
3+
User,
4+
GraduationCap,
5+
Calendar,
6+
Users,
7+
ClipboardList,
8+
Briefcase,
9+
} from "lucide-react";
10+
import axios from "axios";
11+
import { useProfile } from "../../hooks/useProfile";
12+
import api from "../../utils/api"
13+
14+
const PORProfile = () => {
15+
const [position, setPosition] = useState(null);
16+
const [holder, setHolder] = useState(null);
17+
const [loading, setLoading] = useState(true);
18+
19+
const { profile, loading: profileLoading } = useProfile();
20+
const userId = profile?._id;
21+
22+
useEffect(() => {
23+
if (!userId) return;
24+
25+
const fetchData = async () => {
26+
try {
27+
const [positionsRes, holdersRes] = await Promise.all([
28+
api.get("/api/positions/get-all"),
29+
api.get("/api/positions/get-all-position-holder"),
30+
]);
31+
32+
const positions = positionsRes.data;
33+
const holders = holdersRes.data;
34+
35+
const porHolder = holders.find((h) => h.user_id?._id === userId);
36+
37+
if (!porHolder) {
38+
setLoading(false);
39+
return;
40+
}
41+
42+
setHolder(porHolder);
43+
44+
const fullPosition = positions.find(
45+
(p) => p._id === porHolder.position_id._id,
46+
);
47+
48+
setPosition(fullPosition);
49+
} catch (err) {
50+
console.error(err);
51+
} finally {
52+
setLoading(false);
53+
}
54+
};
55+
56+
fetchData();
57+
}, [userId]);
58+
59+
if (loading || profileLoading) {
60+
return (
61+
<div className="min-h-screen flex items-center justify-center bg-[#FBF8E6]">
62+
<div className="animate-spin rounded-full h-10 w-10 border-2 border-blue-300 border-t-blue-600"></div>
63+
</div>
64+
);
65+
}
66+
67+
if (!holder || !position) {
68+
return (
69+
<div className="min-h-screen flex items-center justify-center bg-[#FBF8E6]">
70+
<p className="text-gray-600 text-lg">No POR assigned.</p>
71+
</div>
72+
);
73+
}
74+
75+
const student = holder.user_id;
76+
77+
return (
78+
<div className="min-h-screen bg-[#FBF8E6] px-8 py-6">
79+
{/* Page Title */}
80+
81+
{/* Profile Header */}
82+
<div className="bg-white rounded-2xl border border-gray-200 p-6 flex items-center gap-6 shadow-sm">
83+
<img
84+
src={student?.personal_info?.profilePic || "/default-profile.png"}
85+
alt="profile"
86+
className="w-24 h-24 rounded-full object-cover border"
87+
/>
88+
<div>
89+
<h2 className="text-2xl font-semibold text-gray-900">
90+
{student.personal_info.name || "-"}
91+
</h2>
92+
<p className="text-gray-500">{student.username || ""}</p>
93+
<span className="inline-block mt-2 text-xs bg-blue-100 text-blue-700 px-3 py-1 rounded-full">
94+
{position.title || "N/A"}
95+
</span>
96+
</div>
97+
</div>
98+
99+
{/* Analytics Cards */}
100+
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mt-6">
101+
<StatCard label="Position Type" value={position.position_type || "N/A"} />
102+
<StatCard label="Tenure Year" value={holder.tenure_year || "N/A"} />
103+
<StatCard label="Unit / Club" value={position.unit_id.name || "N/A"} />
104+
</div>
105+
106+
{/* Info Sections */}
107+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
108+
<Section title="Student Information">
109+
<InfoRow
110+
icon={GraduationCap}
111+
label="Batch"
112+
value={profile.academic_info?.batch_year || "-"}
113+
/>
114+
<InfoRow
115+
icon={Users}
116+
label="Branch"
117+
value={profile.academic_info?.branch || "-"}
118+
/>
119+
</Section>
120+
121+
<Section title="Position Information">
122+
<InfoRow
123+
icon={Briefcase}
124+
label="Position Title"
125+
value={position.title}
126+
/>
127+
<InfoRow
128+
icon={Calendar}
129+
label="Tenure Year"
130+
value={holder.tenure_year}
131+
/>
132+
<InfoRow
133+
icon={ClipboardList}
134+
label="Position Type"
135+
value={position.position_type}
136+
/>
137+
</Section>
138+
</div>
139+
140+
{/* Description */}
141+
<div className="mt-6">
142+
<Section title="Position Description">
143+
<p className="text-sm text-gray-700 leading-relaxed">
144+
{position.description}
145+
</p>
146+
</Section>
147+
</div>
148+
149+
{/* Responsibilities */}
150+
<div className="mt-6">
151+
<Section title="Responsibilities">
152+
<ul className="list-disc ml-5 space-y-2 text-sm text-gray-700">
153+
{position.responsibilities?.map((r, i) => (
154+
<li key={i}>{r}</li>
155+
))}
156+
</ul>
157+
</Section>
158+
</div>
159+
</div>
160+
);
161+
};
162+
163+
/* ---------- Reusable Components ---------- */
164+
165+
const Section = ({ title, children }) => (
166+
<div className="bg-white p-5 rounded-xl shadow-sm border border-gray-200">
167+
<h3 className="text-lg font-semibold text-gray-900 mb-3 pb-1 border-b border-gray-200">
168+
{title}
169+
</h3>
170+
{children}
171+
</div>
172+
);
173+
174+
const InfoRow = ({ icon: Icon, label, value }) => (
175+
<div className="flex items-center gap-4 mb-3 bg-gray-50 p-3 rounded-lg border border-gray-200">
176+
{Icon && <Icon className="w-5 h-5 text-blue-600" />}
177+
<div>
178+
<p className="text-xs text-gray-500">{label}</p>
179+
<p className="text-sm font-medium text-gray-900">{value}</p>
180+
</div>
181+
</div>
182+
);
183+
184+
const StatCard = ({ label, value }) => (
185+
<div className="bg-white border border-gray-200 rounded-xl p-4 shadow-sm">
186+
<p className="text-xs text-gray-500">{label}</p>
187+
<p className="text-lg font-semibold text-gray-900 mt-1">{value}</p>
188+
</div>
189+
);
190+
191+
export default PORProfile;
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
import React from "react";
22
import StudentProfile from "./ProfilePage";
3+
import { useAuth } from "../../hooks/useAuth";
4+
5+
import PORProfile from "./PORProfile";
36

47
const Profile = () => {
5-
return <StudentProfile />;
8+
const { userRole, isLoading } = useAuth();
9+
10+
if(isLoading){
11+
return (
12+
<div className="flex justify-center items-center h-full">
13+
Loading...
14+
</div>
15+
);
16+
}
17+
18+
if (userRole === "STUDENT") return <StudentProfile />;
19+
20+
return <PORProfile />;
621
};
722

823
export default Profile;

0 commit comments

Comments
 (0)