From 0ef22db0580e3648e4a31265a2eabd7bd871e574 Mon Sep 17 00:00:00 2001 From: ayush00git Date: Thu, 25 Jun 2026 00:08:53 +0530 Subject: [PATCH 1/2] fix: useNavigate hook to auto navitage to posts --- app/eslint.config.js | 3 +++ app/src/components/ComplaintCard.tsx | 3 +-- app/src/pages/Landing.tsx | 28 ++------------------------- app/src/pages/admin/AdminPostView.tsx | 16 +++++++-------- app/src/pages/auth/VerifyAccount.tsx | 9 +++------ app/src/pages/post/CentreHeadPost.tsx | 5 +++++ app/src/pages/post/FacultyPost.tsx | 5 +++++ app/src/pages/post/PostView.tsx | 4 ++-- app/src/pages/post/WardenPost.tsx | 5 +++++ app/src/pages/profile/Profile.tsx | 23 ++++++++++++++++++---- 10 files changed, 53 insertions(+), 48 deletions(-) diff --git a/app/eslint.config.js b/app/eslint.config.js index ef614d2..c2a386b 100644 --- a/app/eslint.config.js +++ b/app/eslint.config.js @@ -18,5 +18,8 @@ export default defineConfig([ languageOptions: { globals: globals.browser, }, + rules: { + 'react-hooks/set-state-in-effect': 'off', + }, }, ]) diff --git a/app/src/components/ComplaintCard.tsx b/app/src/components/ComplaintCard.tsx index a423cb8..b93bfe0 100644 --- a/app/src/components/ComplaintCard.tsx +++ b/app/src/components/ComplaintCard.tsx @@ -2,7 +2,7 @@ import { useNavigate } from 'react-router-dom'; import { Zap, Hammer, Calendar, MapPin, BedDouble, MessageSquare, ChevronRight, } from 'lucide-react'; -import { POST_PLACES } from '../constants/models'; + // ── Types ───────────────────────────────────────────────────────────────────── @@ -74,7 +74,6 @@ function statusStyle(s: string): StatusStyle { return STATUS_CONFIG[norm] ?? { ...FALLBACK, label: s.replace(/_/g, ' ') }; } -const STAGES = ['XEN', 'AE', 'JE']; function formatDate(iso: string) { return new Date(iso).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); diff --git a/app/src/pages/Landing.tsx b/app/src/pages/Landing.tsx index dbc2518..99b6d3c 100644 --- a/app/src/pages/Landing.tsx +++ b/app/src/pages/Landing.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { ArrowRight, Search } from 'lucide-react'; +import { ArrowRight } from 'lucide-react'; import { Link, useNavigate } from 'react-router-dom'; import { MainLayout } from '../components/layout/MainLayout'; @@ -15,7 +15,6 @@ export function Landing() { const [profile, setProfile] = useState(null); const [isAuth, setIsAuth] = useState(null); const [showLoginMenu, setShowLoginMenu] = useState(false); - const [trackId, setTrackId] = useState(''); const menuRef = useRef(null); const navigate = useNavigate(); @@ -89,28 +88,6 @@ export function Landing() { - - - - - - {/* Track Bar */} -
-
- Quick Track -
- setTrackId(e.target.value)} - placeholder="Enter Complaint ID — e.g. CMS-1042" - className="flex-1 px-4 py-2.5 text-sm text-[#111111] placeholder-[#999999] outline-none bg-transparent" - /> -
@@ -158,8 +135,7 @@ export function Landing() { { step: '01', text: 'Select the correct category — Civil or Electrical — to ensure proper routing.' }, { step: '02', text: 'Provide the exact building and room number in the complaint description.' }, { step: '03', text: 'Only Wardens may file complaints for hostel common areas.' }, - { step: '04', text: 'Attach relevant photos to help the team assess the issue faster.' }, - { step: '05', text: 'Do not file duplicate complaints for the same issue.' }, + { step: '04', text: 'Do not file duplicate complaints for the same issue.' }, ].map(({ step, text }) => (
{step} diff --git a/app/src/pages/admin/AdminPostView.tsx b/app/src/pages/admin/AdminPostView.tsx index 2a8dc79..fa7cf80 100644 --- a/app/src/pages/admin/AdminPostView.tsx +++ b/app/src/pages/admin/AdminPostView.tsx @@ -294,7 +294,7 @@ export function AdminPostView() { .then(async (res) => { if (!res.ok) { let msg = `Server error (${res.status})`; - try { const b = await res.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await res.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } const err: Error & { status?: number } = new Error(msg); err.status = res.status; throw err; @@ -350,7 +350,7 @@ export function AdminPostView() { if (!res.ok) { let msg = `Failed to edit comment (${res.status})`; - try { const b = await res.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await res.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } @@ -384,7 +384,7 @@ export function AdminPostView() { if (!res.ok) { let msg = `Failed to delete comment (${res.status})`; - try { const b = await res.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await res.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } @@ -423,7 +423,7 @@ export function AdminPostView() { }); if (!commentRes.ok) { let msg = `Failed to post comment (${commentRes.status})`; - try { const b = await commentRes.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await commentRes.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } @@ -436,7 +436,7 @@ export function AdminPostView() { }); if (!statusRes.ok) { let msg = `Comment posted but status update failed (${statusRes.status})`; - try { const b = await statusRes.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await statusRes.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } @@ -480,7 +480,7 @@ export function AdminPostView() { }); if (!commentRes.ok) { let msg = `Failed to post comment (${commentRes.status})`; - try { const b = await commentRes.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await commentRes.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } @@ -493,7 +493,7 @@ export function AdminPostView() { }); if (!statusRes.ok) { let msg = `Comment posted but status update failed (${statusRes.status})`; - try { const b = await statusRes.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await statusRes.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } @@ -534,7 +534,7 @@ export function AdminPostView() { }); if (!commentRes.ok) { let msg = `Failed to post comment (${commentRes.status})`; - try { const b = await commentRes.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await commentRes.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } diff --git a/app/src/pages/auth/VerifyAccount.tsx b/app/src/pages/auth/VerifyAccount.tsx index aa0e4af..f0d5460 100644 --- a/app/src/pages/auth/VerifyAccount.tsx +++ b/app/src/pages/auth/VerifyAccount.tsx @@ -13,20 +13,17 @@ const roleLoginRoute: Record = { export function VerifyAccount() { const [searchParams] = useSearchParams(); const navigate = useNavigate(); + const token = searchParams.get('token'); - const [status, setStatus] = useState('loading'); - const [message, setMessage] = useState(''); + const [status, setStatus] = useState(token ? 'loading' : 'no-token'); + const [message, setMessage] = useState(token ? '' : 'No verification token found in the link.'); const [countdown, setCountdown] = useState(3); const intervalRef = useRef | null>(null); const controllerRef = useRef(null); useEffect(() => { - const token = searchParams.get('token'); - if (!token) { - setStatus('no-token'); - setMessage('No verification token found in the link.'); return; } diff --git a/app/src/pages/post/CentreHeadPost.tsx b/app/src/pages/post/CentreHeadPost.tsx index b81d654..8ecfa53 100644 --- a/app/src/pages/post/CentreHeadPost.tsx +++ b/app/src/pages/post/CentreHeadPost.tsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { MainLayout } from '../../components/layout/MainLayout'; import { POST_TYPES } from '../../constants/models'; export function CentreHeadPost() { + const navigate = useNavigate(); const [formData, setFormData] = useState({ type_of_post: '', title: '', @@ -39,6 +41,9 @@ export function CentreHeadPost() { setStatus('success'); setMessage(data.success || 'Complaint submitted successfully!'); setFormData({ type_of_post: '', title: '', description: '' }); + if (data.post?.id) { + navigate(`/post/centrehead/${data.post.id}`); + } } else { setStatus('error'); const errorMsg = data.error || Object.values(data)[0] || 'An error occurred'; diff --git a/app/src/pages/post/FacultyPost.tsx b/app/src/pages/post/FacultyPost.tsx index d879285..3349183 100644 --- a/app/src/pages/post/FacultyPost.tsx +++ b/app/src/pages/post/FacultyPost.tsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { MainLayout } from '../../components/layout/MainLayout'; import { POST_PLACES, POST_TYPES } from '../../constants/models'; export function FacultyPost() { + const navigate = useNavigate(); const [formData, setFormData] = useState({ place: '', type_of_post: '', @@ -40,6 +42,9 @@ export function FacultyPost() { setStatus('success'); setMessage(data.success || 'Complaint submitted successfully!'); setFormData({ place: '', type_of_post: '', title: '', description: '' }); + if (data.post?.id) { + navigate(`/post/faculty/${data.post.id}`); + } } else { setStatus('error'); const errorMsg = data.error || Object.values(data)[0] || 'An error occurred'; diff --git a/app/src/pages/post/PostView.tsx b/app/src/pages/post/PostView.tsx index 8361ee3..1dae508 100644 --- a/app/src/pages/post/PostView.tsx +++ b/app/src/pages/post/PostView.tsx @@ -3,7 +3,7 @@ import { useParams, useNavigate, Link } from 'react-router-dom'; import { Zap, Hammer, Trash2, Pencil, X, Check, Calendar, MapPin, BedDouble, MessageSquare, Wrench, ArrowLeft, Send, AlertCircle, - UserCircle, Clock, + Clock, } from 'lucide-react'; import { MainLayout } from '../../components/layout/MainLayout'; import { POST_PLACES } from '../../constants/models'; @@ -270,7 +270,7 @@ export function PostView() { }); if (!res.ok) { let msg = `Failed to post comment (${res.status})`; - try { const b = await res.json(); if (b?.error) msg = b.error; } catch {} + try { const b = await res.json(); if (b?.error) msg = b.error; } catch { /* ignore */ } throw new Error(msg); } setCommentText(''); diff --git a/app/src/pages/post/WardenPost.tsx b/app/src/pages/post/WardenPost.tsx index 7a33f0a..1385cc6 100644 --- a/app/src/pages/post/WardenPost.tsx +++ b/app/src/pages/post/WardenPost.tsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { MainLayout } from '../../components/layout/MainLayout'; import { POST_TYPES } from '../../constants/models'; export function WardenPost() { + const navigate = useNavigate(); const [formData, setFormData] = useState({ room_number: '', type_of_post: '', @@ -40,6 +42,9 @@ export function WardenPost() { setStatus('success'); setMessage(data.success || 'Complaint submitted successfully!'); setFormData({ room_number: '', type_of_post: '', title: '', description: '' }); + if (data.post?.id) { + navigate(`/post/warden/${data.post.id}`); + } } else { setStatus('error'); const errorMsg = data.error || Object.values(data)[0] || 'An error occurred'; diff --git a/app/src/pages/profile/Profile.tsx b/app/src/pages/profile/Profile.tsx index 4609c74..b8ae5a7 100644 --- a/app/src/pages/profile/Profile.tsx +++ b/app/src/pages/profile/Profile.tsx @@ -8,8 +8,21 @@ import { MainLayout } from '../../components/layout/MainLayout'; import { ComplaintCard } from '../../components/ComplaintCard'; import type { ComplaintPost, EditForm, Role } from '../../components/ComplaintCard'; +interface ProfileData { + name?: string; + email?: string; + is_verified?: boolean; + phone_number?: string; + department?: string; + house_number?: string; + block?: string; + type?: string; + hostel?: string; + building?: string; +} + export function Profile() { - const [profile, setProfile] = useState(null); + const [profile, setProfile] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const navigate = useNavigate(); @@ -40,7 +53,7 @@ export function Profile() { const fetchPosts = useCallback((silent = false) => { if (!profile) return; - let endpoint = ''; + let endpoint: string; if ('department' in profile) endpoint = '/api/post/faculty'; else if ('hostel' in profile) endpoint = '/api/post/warden'; else if ('building' in profile) endpoint = '/api/post/centrehead'; @@ -87,6 +100,8 @@ export function Profile() { ); } + if (!profile) return null; + const isFaculty = 'department' in profile; const isWarden = 'hostel' in profile; const isCentreHead = 'building' in profile; @@ -102,7 +117,7 @@ export function Profile() { const deleteBase = isFaculty ? '/api/post/faculty/delete' : isWarden ? '/api/post/warden/delete' : '/api/post/centrehead/delete'; const handleLogout = async () => { - try { await fetch('/api/auth/logout', { method: 'POST' }); } catch {} + try { await fetch('/api/auth/logout', { method: 'POST' }); } catch { /* ignore */ } window.location.href = '/'; }; @@ -200,7 +215,7 @@ export function Profile() { {/* Identity row */}
-

{profile.name || profile.email.split('@')[0]}

+

{profile.name || profile.email?.split('@')[0] || ''}

{profile.email}

From 757372ec64e4e548e102afbf28744bc54087620d Mon Sep 17 00:00:00 2001 From: ayush00git Date: Thu, 25 Jun 2026 00:18:42 +0530 Subject: [PATCH 2/2] fix: comment body and events audits --- app/src/pages/admin/AdminPostView.tsx | 402 ++++++++++++++------------ app/src/pages/post/PostView.tsx | 161 +++++++---- 2 files changed, 323 insertions(+), 240 deletions(-) diff --git a/app/src/pages/admin/AdminPostView.tsx b/app/src/pages/admin/AdminPostView.tsx index fa7cf80..6f1fb8b 100644 --- a/app/src/pages/admin/AdminPostView.tsx +++ b/app/src/pages/admin/AdminPostView.tsx @@ -17,6 +17,8 @@ import { Pencil, Trash2, Info, + Check, + Clock, } from 'lucide-react'; import { MainLayout } from '../../components/layout/MainLayout'; @@ -755,15 +757,56 @@ export function AdminPostView() { {/* Combined Timeline */} - {timelineItems.length === 0 ? ( -

No history or comments yet.

- ) : ( -
- {timelineItems.map((item, idx) => { +
+ {/* Connecting pipeline/line */} +
+ + {timelineItems.length === 0 ? ( +
+

No activity or responses yet.

+
+ ) : ( + timelineItems.map((item, idx) => { if (item.type === 'audit') { const audit = item.data; const normEvent = audit.event.toLowerCase(); - const dotColor = STATUS_DOT[normEvent] ?? 'bg-gray-400'; + + const auditStyle = (() => { + if (normEvent.includes('resolved')) { + return { + bg: 'bg-emerald-50 border-emerald-300 text-emerald-600', + icon: , + textCls: 'text-emerald-700 font-bold', + }; + } + if (normEvent === 'pending_xen') { + return { + bg: 'bg-amber-50 border-amber-300 text-amber-600', + icon: , + textCls: 'text-amber-700 font-bold', + }; + } + if (normEvent === 'pending_ae') { + return { + bg: 'bg-sky-50 border-sky-300 text-sky-600', + icon: , + textCls: 'text-sky-700 font-bold', + }; + } + if (normEvent === 'pending_je') { + return { + bg: 'bg-violet-50 border-violet-300 text-violet-600', + icon: , + textCls: 'text-violet-700 font-bold', + }; + } + return { + bg: 'bg-gray-50 border-gray-300 text-gray-500', + icon: , + textCls: 'text-gray-700 font-bold', + }; + })(); + const eventText = (() => { if (normEvent === 'pending_xen') return 'Sent to XEN for review.'; if (normEvent === 'pending_ae') return 'Sent to AE for review.'; @@ -775,17 +818,15 @@ export function AdminPostView() { })(); return ( -
- - - -
- +
+
+ {auditStyle.icon} +
+
+ {eventText} + {formatDateTime(audit.timestamp)} - - {eventText} -
); @@ -798,15 +839,15 @@ export function AdminPostView() { const editExpired = isEditWindowExpired(c.created_at); return ( -
- - - -
-
- {who} - {c.email && {c.email}} - +
+ {/* Comment Bubble in GitHub Style */} +
+
+ + {who} + {c.email && {c.email}} + + {formatDateTime(c.created_at)} {isMyComment && !isEditing && !editExpired && ( @@ -820,7 +861,7 @@ export function AdminPostView() { className="p-0.5 rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200/50 transition cursor-pointer" title="Edit comment" > - + )}
- {isEditing ? ( -
-