Py-detect / src /app /case-details-page /case-details-page.component.html
RajalashmiNagarajan
adminpage update
86b4aeb
<!-- Modern UI header with logo and PyDetect title -->
<div class="site-header">
<div class="header-inner">
<div class="logo-cluster">
<span (click)="navigateHome()" style="cursor:pointer;display:flex;align-items:center;">
<img src="/assets/pykara-logo.png" alt="PyDetect Logo" class="logo-img-header" />
</span>
<div class="py-detect-title-header">
<span class="py-letter p">P</span>
<span class="py-letter y">Y</span>
<span class="py-shape"></span>
<span class="py-letter d">D</span>
<span class="py-letter e">E</span>
<span class="py-letter t">T</span>
<span class="py-letter e2">E</span>
<span class="py-letter c">C</span>
<span class="py-letter t2">T</span>
</div>
</div>
<div class="header-actions-right">
<button class="back-small" *ngIf="selectedCase" (click)="closeAndReturn()">← Back to {{ getReturnLabel() }}</button>
<!-- Profile display: initials and dropdown with name/email (matches infopage) -->
<div #profileDisplay class="profile-display" [class.open]="showProfileMenu" style="margin-left:12px; display:flex;align-items:center; cursor:pointer; position:relative;" (click)="toggleProfileMenu($event)">
<div class="profile-avatar" style="width:2.2em; height:2.2em; border-radius:50%; background: linear-gradient(135deg, #1E3A8A, #2563eb); color: #fff; display: flex; align-items: center; justify-content: center; font-weight:700; font-size:0.95em; overflow: hidden;">
<img *ngIf="currentUser?.avatarUrl" [src]="currentUser?.avatarUrl" alt="avatar" style="width:100%;height:100%;object-fit:cover;" />
<span *ngIf="!currentUser?.avatarUrl">{{ getUserInitials() }}</span>
</div>
<div *ngIf="showProfileMenu" class="profile-menu" [ngStyle]="profileMenuStyle" style="background:#222;color:#fff;border-radius:8px;padding:8px10px;min-width:220px;box-shadow:06px18px rgba(0,0,0,0.25);">
<div style="display:flex;align-items:center;gap:10px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,0.06);margin-bottom:8px;">
<div style="width:42px;height:42px;border-radius:50%;background:#2b6ea6;display:flex;align-items:center;justify-content:center;font-weight:700;color:#fff;overflow:hidden;">
<img *ngIf="currentUser?.avatarUrl" [src]="currentUser?.avatarUrl" alt="avatar" style="width:100%;height:100%;object-fit:cover;" />
<span *ngIf="!currentUser?.avatarUrl">{{ getUserInitials() }}</span>
</div>
<div style="font-size:0.95em;">
<div>{{ currentUser?.name }}</div>
<div style="font-size:0.8em;opacity:0.9;">{{ currentUser?.email }}</div>
</div>
</div>
<button (click)="logout()" style="width:100%;display:flex;align-items:center;gap:8px;padding:8px10px;border:none;border-radius:6px;background:transparent;color:#fff;cursor:pointer;">
<i class="fas fa-sign-out-alt" style="width:18px;text-align:center;"></i>
Logout
</button>
</div>
</div>
<button class="logout-btn" (click)="logout()">
<span class="logout-icon"></span> Logout
</button>
</div>
</div>
</div>
<!-- Main Content Area -->
<div class="main-content">
<div class="content-container">
<!-- Record Card -->
<div class="record-card">
<div class="analytics-panel">
<div class="analytics-blue">
<div class="record-header">
<div class="record-title-group">
<span class="record-title"><i class="fas fa-database"></i> Police Investigation Records</span>
</div>
<div class="record-header-actions">
<span style="position:relative;">
<i class="fas fa-search" style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#b0b0b0;"></i>
<input class="record-search" type="text" [(ngModel)]="q" (ngModelChange)="applyFilters()" placeholder="Search this list..." style="padding-left:32px;" />
</span>
</div>
</div>
<div class="analytics-cards">
<div class="summary-card total">
<div class="summary-left">
<div class="summary-label">Total Cases</div>
<div class="summary-value blue">{{ totalCases }}</div>
<div class="summary-sub">&nbsp;</div>
</div>
<div class="summary-icon icon-indigo"><i class="fas fa-folder-open fa-bounce"></i></div>
</div>
<div class="summary-card open">
<div class="summary-left">
<div class="summary-label">Open</div>
<div class="summary-value green">{{ openCases }}</div>
<div class="summary-sub">&nbsp;</div>
</div>
<div class="summary-icon icon-blue"><i class="fas fa-exclamation-circle fa-beat"></i></div>
</div>
<div class="summary-card closed">
<div class="summary-left">
<div class="summary-label">Closed</div>
<div class="summary-value red">{{ closedCases }}</div>
<div class="summary-sub">&nbsp;</div>
</div>
<div class="summary-icon icon-green"><i class="fas fa-check-circle fa-spin"></i></div>
</div>
<div class="summary-card review">
<div class="summary-left">
<div class="summary-label">Pending Review</div>
<div class="summary-value blue">{{ reviewCases }}</div>
<div class="summary-sub">&nbsp;</div>
</div>
<div class="summary-icon icon-yellow"><i class="fas fa-hourglass-half"></i></div>
</div>
</div>
</div>
<div class="record-meta" style="padding:8px24px024px; color: #6b7280; font-size:0.98em;">
{{ filteredCases.length }} items • Updated a few seconds ago
</div>
</div>
<div class="filter-bar">
<span class="filter-icon"><i class="fas fa-filter"></i></span>
<select [(ngModel)]="filterStatus">
<option value="">Status</option>
<option *ngFor="let status of statusTypes">{{ status }}</option>
</select>
<select [(ngModel)]="filterCrimeType">
<option value="">Crime Type</option>
<option *ngFor="let type of crimeTypes">{{ type }}</option>
</select>
<button (click)="applyFilters()">Apply</button>
<button (click)="resetFilters()">Reset</button>
</div>
<!-- Table/list view always visible -->
<!-- Table Container -->
<div class="table-container">
<div class="table-wrapper">
<table class="record-table">
<thead>
<tr>
<th>#</th>
<th (click)="setSort('caseId')" [attr.aria-sort]="ariaSort('caseId')" style="cursor:pointer;">
Case ID
<span class="sort" [ngClass]="{'asc': isAsc('caseId'), 'desc': isDesc('caseId'), 'neutral': !isAsc('caseId') && !isDesc('caseId')}"></span>
</th>
<th (click)="setSort('priority')" [attr.aria-sort]="ariaSort('priority')" style="cursor:pointer;">
Priority
<span class="sort" [ngClass]="{'asc': isAsc('priority'), 'desc': isDesc('priority'), 'neutral': !isAsc('priority') && !isDesc('priority')}"></span>
</th>
<th (click)="setSort('status')" [attr.aria-sort]="ariaSort('status')" style="cursor:pointer;">
Status
<span class="sort" [ngClass]="{'asc': isAsc('status'), 'desc': isDesc('status'), 'neutral': !isAsc('status') && !isDesc('status')}"></span>
</th>
<th (click)="setSort('crime')" [attr.aria-sort]="ariaSort('crime')" style="cursor:pointer;">
Crime Type
<span class="sort" [ngClass]="{'asc': isAsc('crime'), 'desc': isDesc('crime'), 'neutral': !isAsc('crime') && !isDesc('crime')}"></span>
</th>
<th (click)="setSort('dateTime')" [attr.aria-sort]="ariaSort('dateTime')" style="cursor:pointer;">
Date &amp; Time
<span class="sort" [ngClass]="{'asc': isAsc('dateTime'), 'desc': isDesc('dateTime'), 'neutral': !isAsc('dateTime') && !isDesc('dateTime')}"></span>
</th>
<th>Location</th>
<th>Suspect Name</th>
<th>Last Updated</th>
<th class="progress-col">Progress</th>
<th class="next-action-col">Next Action</th>
<th class="actions">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let c of rows, let i = index">
<td>{{ (currentPage -1) * pageSize + i +1 }}</td>
<td class="mono">
<a (click)="openDetails(c)" style="cursor:pointer; color:#2563eb; text-decoration:underline;">
{{ c.caseId || '—' }}
</a>
</td>
<td>
<ng-container [ngSwitch]="getCasePriority(c)">
<span *ngSwitchCase="'High'" class="priority-pill priority-high" title="High Priority">
🔴 High
</span>
<span *ngSwitchCase="'Medium'" class="priority-pill priority-medium" title="Medium Priority">
🟡 Medium
</span>
<span *ngSwitchCase="'Low'" class="priority-pill priority-low" title="Low Priority">
🟢 Low
</span>
<span *ngSwitchDefault style="color:#6b7280;"></span>
</ng-container>
</td>
<td>
<span class="status-label"
[ngClass]="{
'status-open': c.status === 'Open',
'status-under': c.status === 'Under Investigation',
'status-closed': c.status === 'Closed'
}">
{{ c.status || '—' }}
</span>
</td>
<td>{{ c.crime || '—' }}</td>
<td>{{ c.dateTime ? (c.dateTime | date:'M/d/yyyy HH:mm') : '—' }}</td>
<td>{{ c.police?.address || '—' }}</td>
<td>{{ c.accused?.name || '—' }}</td>
<td>{{ c.lastUpdated ? (c.lastUpdated | date:'M/d/yyyy HH:mm') : '—' }}</td>
<td class="progress-col">
<ng-container [ngSwitch]="getProgressValue(c)">
<ng-container *ngSwitchCase="100">
<span class="progress-check">&#x2714;</span>
<span class="progress-value">100%</span>
</ng-container>
<ng-container *ngSwitchCase="75">
<span class="progress-dot green"></span>
<span class="progress-value">75%</span>
</ng-container>
<ng-container *ngSwitchDefault>
<span class="progress-dot blue"></span>
<span class="progress-value">{{ getProgressValue(c) }}%</span>
</ng-container>
</ng-container>
</td>
<td class="next-action-col">
{{ getNextActionMessage(c) }}
</td>
<td class="actions">
<button type="button" class="icon-btn view" (click)="openDetails(c)" title="View Case Details" aria-label="View Case Details">
<i class="fas fa-eye"></i>
</button>
<button type="button" class="detect-btn"
[disabled]="!c.caseId"
(click)="c.caseId && goToDetectWithMetadata(c)"
title="Go Detect" aria-label="Go Detect">
Go Detect
</button>
<button *ngIf="isInvestigator()" type="button" class="icon-btn upload" (click)="showEvidencePanel(c)" title="Upload Evidence" aria-label="Upload Evidence">
<i class="fas fa-upload"></i>
</button>
</td>
</tr>
<tr *ngIf="rows.length ===0">
<td colspan="12" class="empty">No records found.</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Bottom Controls -->
<div class="bottom-controls">
<div class="results-summary">
<div class="results-info">
<i class="fas fa-list-ol"></i>
<span>Results: {{ resultsStart }} - {{ resultsEnd }} of {{ resultsTotal }}</span>
</div>
<div class="page-size-selector">
<span>Show:</span>
<select [(ngModel)]="pageSize" (change)="onPageSizeChange(pageSize)">
<option *ngFor="let size of pageSizeOptions" [value]="size">{{ size }}</option>
</select>
</div>
</div>
<div class="pagination-controls">
<button class="page-btn prev" (click)="prevPage()" [disabled]="currentPage ===1">
<i class="fas fa-chevron-left"></i>
</button>
<div class="page-numbers">
<ng-container *ngFor="let page of getPagination()">
<button *ngIf="page !== '...'"
class="page-number"
[class.active]="currentPage === page"
(click)="goToPage(page)">
{{ page }}
</button>
<span *ngIf="page === '...'" class="page-ellipsis">...</span>
</ng-container>
</div>
<button class="page-btn next" (click)="nextPage()" [disabled]="currentPage === totalPages">
<i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
</div>
<!-- Evidence Upload Section for Investigators only, below the table -->
<div *ngIf="isInvestigator() && selectedCase" class="evidence-upload-section">
<h3>Upload Evidence for Case: {{ selectedCase.caseId }}</h3>
<input type="file" multiple (change)="onEvidenceUpload($event)" />
<div class="evidence-list" *ngIf="uploadedEvidence.length">
<div *ngFor="let file of uploadedEvidence" class="evidence-file">
<i class="fas fa-file-upload"></i> {{ file.name }}
</div>
</div>
</div>
<!-- Evidence Upload Panel for selected case -->
<div *ngIf="isInvestigator() && evidencePanelCase && evidencePanelCase.caseId" class="evidence-upload-section">
<hr class="evidence-hr" />
<div class="evidence-title">
<i class="fas fa-folder-open evidence-folder"></i>
Evidence Upload for Case: {{ evidencePanelCase.caseId }}
</div>
<hr class="evidence-hr" />
<div class="evidence-type-tabs">
<button [class.active]="evidenceType === 'Document'" (click)="setEvidenceType('Document')">Document</button>
<button [class.active]="evidenceType === 'Photo'" (click)="setEvidenceType('Photo')">Photo</button>
</div>
<div class="evidence-actions">
<label class="evidence-file-label">
[Choose File]
<input type="file" multiple (change)="onEvidenceFileSelectType($event)" style="display:none;" />
</label>
</div>
<hr class="evidence-hr" />
<div class="evidence-list-block">
<div class="evidence-list-title">Uploaded Evidence ({{ evidenceType }}):</div>
<div *ngFor="let file of evidenceFiles[evidencePanelCase.caseId][evidenceType]" class="evidence-file-row">
<i [ngClass]="getEvidenceIcon(file.name)" class="evidence-file-icon"></i>
<span class="evidence-file-name">{{ file.name }}</span>
<a class="evidence-view-link" href="#" (click)="viewEvidenceFile(file)">(View)</a>
</div>
</div>
<hr class="evidence-hr" />
</div>
<!-- Full-page overlay for details (no evidence upload here) -->
<div *ngIf="selectedCase" class="fullpage-popup-overlay">
<div class="fullpage-popup-content">
<div class="case-details-title">Case Details</div>
<div class="details-sections">
<ng-container *ngFor="let sectionKey of ['crime', 'suspect', 'notes']">
<div class="details-section-card">
<div class="section-title">{{ sections[sectionKey].title }}</div>
<div class="subgroup-pills">
<button *ngFor="let subgroup of getSubgroups(sectionKey)"
[class.active]="selectedSubgroup[sectionKey] === subgroup"
(click)="selectSubgroup(sectionKey, subgroup)">
{{ subgroup }}
</button>
</div>
<div class="fields-table-2col">
<div class="fields-col fields-col-labels">
<div class="field-label" *ngFor="let field of getFieldsForSubgroup(sectionKey, selectedSubgroup[sectionKey])">
{{ field }}
</div>
</div>
<div class="fields-col fields-col-values">
<div class="field-value" *ngFor="let field of getFieldsForSubgroup(sectionKey, selectedSubgroup[sectionKey])">
{{ getFieldValue(selectedCase, sectionKey, field) }}
</div>
</div>
</div>
</div>
</ng-container>
<div class="details-section-card">
<div style="display:flex;align-items:center;justify-content:space-between;">
<div class="section-title">All Entered Fields (Raw Form Data)</div>
<div style="display:flex;gap:8px;align-items:center;">
<button class="small-btn" (click)="copyFormData()" title="Copy JSON">Copy JSON</button>
<span *ngIf="copySuccess" style="color:green;font-weight:600;">Copied!</span>
</div>
</div>
<div class="raw-formdata-table" style="max-height:420px;overflow:auto;padding:8px;">
<div *ngIf="getFormDataArray(selectedCase)?.length; else noRaw">
<table style="width:100%;border-collapse:collapse;">
<tr *ngFor="let kv of getFormDataArray(selectedCase)">
<td style="padding:8px;border-bottom:1px solid #eee;font-weight:600;width:35%;vertical-align:top;">{{ kv.key }}</td>
<td style="padding:8px;border-bottom:1px solid #eee;vertical-align:top;">
<pre style="white-space:pre-wrap;margin:0;font-family:inherit;">{{ formatFormValue(kv.value) }}</pre>
</td>
</tr>
</table>
</div>
<ng-template #noRaw>
<div style="color:#888;">No raw form data saved for this case.</div>
</ng-template>
</div>
</div>
</div>
<div style="display:flex;gap:8px;position:relative;">
<button class="btn edit-btn" (click)="editCase()" title="Edit Case">Edit</button>
<button class="btn close-btn-bottom" (click)="closeAndReturn()" title="Close">&times;</button>
</div>
</div>
</div>
<!-- Footer from provided design -->
<footer>
<p>©2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p>
</footer>
<!-- End of record-card -->