diff --git a/app-frontend/employer-panel/src/pages/EmployerDashboard.css b/app-frontend/employer-panel/src/pages/EmployerDashboard.css index 31808fe8..9a3f3d89 100644 --- a/app-frontend/employer-panel/src/pages/EmployerDashboard.css +++ b/app-frontend/employer-panel/src/pages/EmployerDashboard.css @@ -277,4 +277,123 @@ body { } .ss-card__createtext { font-weight:600; +} + +/* Incident Management */ + +.ss-row .ss-incident-id { + color: #000; + font-weight: 700; +} + +.ss-status--resolved { + color: #2e7d32; +} + +/* Modal Layout */ +.create-shift-modal-backdrop { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(10, 43, 102, 0.5); + display: grid; + place-items: center; + z-index: 1000; + padding: 20px; +} + +.create-shift-card { + background: #fff; + border-radius: var(--radius-lg); + padding: 30px; + width: 100%; + box-shadow: var(--shadow); + animation: modalSlideUp 0.3s ease-out; +} + +@keyframes modalSlideUp { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +.create-shift-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 24px; + border-bottom: 1px solid var(--ss-border); + padding-bottom: 16px; +} + +.form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.form-group label { + font-weight: 700; + font-size: 13px; + text-transform: uppercase; + color: var(--ss-muted); +} + +.ss-input-static { + background: #f5f5f5; + border: 1px solid var(--ss-border); + color: var(--ss-text); + font-weight: 600; +} + +.ss-incident-description { + background: #fff9e6; + padding: 15px; + border-radius: 8px; + border: 1px solid #ffeeba; + font-size: 14px; + line-height: 1.6; +} + +.ss-evidence-img { + width: 120px; + height: 100px; + object-fit: cover; + border-radius: 8px; + border: 1px solid var(--ss-border); + transition: transform 0.2s; +} + +.ss-evidence-img:hover { + transform: scale(1.05); +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 12px; +} + +.actions .primary { + background: var(--ss-blue-800); + color: #fff; + border: none; + padding: 10px 24px; + border-radius: 999px; + font-weight: 600; + cursor: pointer; +} + +.actions .secondary { + background: #f0f2f7; + color: var(--ss-blue-800); + border: none; + padding: 10px 24px; + border-radius: 999px; + font-weight: 600; + cursor: pointer; } \ No newline at end of file diff --git a/app-frontend/employer-panel/src/pages/EmployerDashboard.js b/app-frontend/employer-panel/src/pages/EmployerDashboard.js index a9cb0f48..1c4c3578 100644 --- a/app-frontend/employer-panel/src/pages/EmployerDashboard.js +++ b/app-frontend/employer-panel/src/pages/EmployerDashboard.js @@ -61,67 +61,32 @@ export default function EmployerDashboard() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - useEffect(() => { - async function fetchShifts() { - setLoading(true); - setError(null); - try { - const token = localStorage.getItem("token"); - const res = await fetch("http://localhost:5000/api/v1/shifts", { - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - }); - if (!res.ok) throw new Error("Failed to fetch shifts"); - const data = await res.json(); - // Map backend data to dashboard format - const apiShifts = Array.isArray(data) ? data : (data.shifts || data.items || []); - setShifts(apiShifts.map(s => ({ - role: s.title || "-", - company: s.siteName || s.company || "-", - venue: s.location?.street || s.venue || "-", - rate: s.payRate || s.price || 0, - status: mapStatus(s.status), - date: formatDate(s.date), - time: formatTime(s.startTime, s.endTime), - }))); - } catch (err) { - setError(err.message); - } finally { - setLoading(false); - } + // States for Incident Management + const [selectedIncident, setSelectedIncident] = useState(null); + const [incidents, setIncidents] = useState([ + { + id: "INC-9921", + guard: "John Doe", + shift: "Crowd Control - Marvel", + date: "09-08-2025", + time: "10:45 PM", + status: "Pending", + severity: "High", + description: "A patron was found attempting to bypass security with restricted items. Incident was recorded and patron escorted out.", + + // Demo Image + photos: ["https://images.unsplash.com/photo-1582139329536-e7284fece509?auto=format&fit=crop&w=300&q=80"], + comments: "" } - fetchShifts(); - }, []); - - function mapStatus(status) { - if (!status) return { text: "Open", tone: "confirmed" }; - const s = status.toLowerCase(); - if (s === "pending") return { text: "Pending", tone: "pending" }; - if (s === "rejected") return { text: "Rejected", tone: "rejected" }; - if (s === "completed") return { text: "Completed", tone: "completed" }; - if (s === "confirmed") return { text: "Confirmed", tone: "confirmed" }; - return { text: status, tone: "confirmed" }; - } - function formatDate(date) { - if (!date) return "-"; - const d = new Date(date); - if (isNaN(d)) return date; - return d.toLocaleDateString("en-GB"); - } - function formatTime(start, end) { - if (!start || !end) return "-"; - return `${to12hr(start)} - ${to12hr(end)}`; - } - function to12hr(t) { - if (!t) return "-"; - let [h, m] = t.split(":"); - h = parseInt(h, 10); - const ampm = h >= 12 ? "pm" : "am"; - h = h % 12 || 12; - return `${h}:${m} ${ampm}`; - } + ]); + + const shifts = useMemo(() => [ + { role: "Crowd Control", company: "AIG Solutions", venue: "Marvel Stadium", rate: 55, status: { text: "Confirmed", tone: "confirmed" }, date: "09-08-2025", time: "5:00 pm - 1:00 am" }, + { role: "Shopping Centre Security", company: "Vicinity Centres", venue: "Chadstone Shopping Centre", rate: 75, status: { text: "Pending", tone: "pending" }, date: "03-08-2025", time: "1:00 pm - 9:00 pm" }, + { role: "Crowd Control", company: "AIG Solutions", venue: "Marvel Stadium", rate: 55, status: { text: "Rejected", tone: "rejected" }, date: "09-08-2025", time: "5:00 pm - 1:00 am" }, + { role: "Crowd Control", company: "AIG Solutions", venue: "Marvel Stadium", rate: 55, status: { text: "Completed (Unrated)", tone: "completed" }, date: "01-08-2025", time: "5:00 pm - 1:00 am" }, + { role: "Crowd Control", company: "AIG Solutions", venue: "Marvel Stadium", rate: 55, status: { text: "Completed (Rated)", tone: "completed" }, date: "31-07-2025", time: "5:00 pm - 1:00 am" }, + ], []); const reviews = useMemo(() => [ { name: "John Smith", role: "Crowd Control", stars: 5 }, @@ -134,6 +99,13 @@ export default function EmployerDashboard() { ref.current.scrollBy({ left: amt, behavior: "smooth" }); }; + const updateIncident = (id, newStatus, newSeverity, newComments) => { + setIncidents(prev => prev.map(inc => + inc.id === id ? { ...inc, status: newStatus, severity: newSeverity, comments: newComments } : inc + )); + setSelectedIncident(null); + }; + return (
@@ -219,6 +191,33 @@ export default function EmployerDashboard() {
+ {/* Incident Reports */} +

Incident Reports

+
+
+
+
+ {incidents.map((inc, i) => ( +
+
{inc.guard}
+
{inc.shift}
+
{inc.id}
+
+ {inc.date} +
+
+ {inc.status} +
+
+ +
+
+ ))} +
+
+
+
+ {/* Reviews */}

Recent Review

@@ -243,6 +242,66 @@ export default function EmployerDashboard() {
+ + {/* Incident Detail Modal */} + {selectedIncident && ( +
setSelectedIncident(null)}> +
e.stopPropagation()} style={{ maxWidth: '700px' }}> +
+
+

Incident Details ({selectedIncident.id})

+

+ Recorded on: {selectedIncident.date} at {selectedIncident.time} +

+
+ + {selectedIncident.status} + +
+ +
+
+ +
{selectedIncident.guard}
+
+
+ + +
+
+ +
+ +
{selectedIncident.description}
+
+ +
+ +
+ {selectedIncident.photos.map((url, idx) => ( + incident evidence + ))} +
+
+ +
+ +