Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions app-frontend/employer-panel/src/pages/EmployerDashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
113 changes: 113 additions & 0 deletions app-frontend/employer-panel/src/pages/EmployerDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@ export default function EmployerDashboard() {
const reviewScroller = useRef(null);
const [showCreateModal, setShowCreateModal] = useState(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: ""
}
]);

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" },
Expand All @@ -76,6 +95,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 (
<div className="ss-page">

Expand Down Expand Up @@ -159,6 +185,33 @@ export default function EmployerDashboard() {
<button className="ss-arrow ss-arrow--right" onClick={() => scrollByAmount(overviewScroller, 320)}>›</button>
</div>

{/* Incident Reports */}
<h2 className="ss-h1 ss-h1--spaced">Incident Reports</h2>
<div className="ss-overview">
<div style={{ width: "44px" }}></div>
<div className="ss-panel">
<div className="ss-shifts ss-shifts--list">
{incidents.map((inc, i) => (
<div className="ss-row" key={i}>
<div className="ss-col ss-role"><b>{inc.guard}</b></div>
<div className="ss-col ss-company">{inc.shift}</div>
<div className="ss-col ss-incident-id">{inc.id}</div>
<div className="ss-col ss-date">
<IconCalendar className="ss-ico" /> {inc.date}
</div>
<div className={`ss-col ss-status ss-status--${inc.status.toLowerCase()}`}>
{inc.status}
</div>
<div className="ss-col ss-time" style={{ textAlign: "right" }}>
<button className="ss-secondary" style={{ width: '100px' }} onClick={() => setSelectedIncident(inc)}>Review</button>
</div>
</div>
))}
</div>
</div>
<div style={{ width: "44px" }}></div>
</div>

{/* Reviews */}
<h2 className="ss-h1 ss-h1--spaced">Recent Review</h2>
<div className="ss-reviews">
Expand All @@ -183,6 +236,66 @@ export default function EmployerDashboard() {
<button className="ss-arrow ss-arrow--right" onClick={() => scrollByAmount(reviewScroller, 300)}>›</button>
</div>
</main>

{/* Incident Detail Modal */}
{selectedIncident && (
<div className="create-shift-modal-backdrop" onClick={() => setSelectedIncident(null)}>
<div className="create-shift-card" onClick={(e) => e.stopPropagation()} style={{ maxWidth: '700px' }}>
<div className="create-shift-header">
<div>
<h1>Incident Details (<span className="ss-incident-id">{selectedIncident.id}</span>)</h1>
<p style={{ margin: 0, fontSize: '14px', color: '#666' }}>
Recorded on: {selectedIncident.date} at {selectedIncident.time}
</p>
</div>
<span className={`ss-status ss-status--${selectedIncident.status.toLowerCase()}`}>
{selectedIncident.status}
</span>
</div>

<div className="form-grid" style={{ marginBottom: '20px' }}>
<div className="form-group">
<label>Reported By</label>
<div className="ss-input-static" style={{ padding: '10px', borderRadius: '4px' }}>{selectedIncident.guard}</div>
</div>
<div className="form-group">
<label>Assign Severity Level</label>
<select defaultValue={selectedIncident.severity} id="severitySelect" style={{ padding: '10px', border: '1px solid #ddd', borderRadius: '4px' }}>
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
</select>
</div>
</div>

<div className="form-group" style={{ marginBottom: '20px' }}>
<label>Guard's Description</label>
<div className="ss-incident-description">{selectedIncident.description}</div>
</div>

<div className="form-group" style={{ marginBottom: '20px' }}>
<label>Evidence Photos</label>
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
{selectedIncident.photos.map((url, idx) => (
<img key={idx} src={url} alt="incident evidence" className="ss-evidence-img" />
))}
</div>
</div>

<div className="form-group">
<label>Employer Comments</label>
<textarea id="employerComments" placeholder="Add internal notes..." defaultValue={selectedIncident.comments} style={{ border: '1px solid #ddd', borderRadius: '4px', padding: '10px' }} rows={4} />
</div>

<div className="actions" style={{ marginTop: '30px' }}>
<button className="primary" onClick={() => updateIncident(selectedIncident.id, "Resolved", document.getElementById('severitySelect').value, document.getElementById('employerComments').value)}>Mark as Resolved</button>
<button className="secondary" onClick={() => updateIncident(selectedIncident.id, "Pending", document.getElementById('severitySelect').value, document.getElementById('employerComments').value)}>Save as Pending</button>
<button className="secondary" style={{ color: '#666' }} onClick={() => setSelectedIncident(null)}>Close</button>
</div>
</div>
</div>
)}

{showCreateModal && (
<CreateShift
isModal
Expand Down