|
|
<div class="app-container"> |
|
|
|
|
|
<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-btn" (click)="navigateBackToInfoPage()"> |
|
|
<span class="back-icon">←</span> Back |
|
|
</button> |
|
|
<button class="logout-btn" (click)="logout()"> |
|
|
<span class="logout-icon">⎋</span> Logout |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="main-content"> |
|
|
<div class="content-container"> |
|
|
|
|
|
<div class="record-card"> |
|
|
|
|
|
<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> |
|
|
<select class="record-dropdown"> |
|
|
<option>Recently Viewed</option> |
|
|
<option>All Records</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="record-search-container"> |
|
|
<span style="position:relative;"> |
|
|
<i class="fas fa-search" style="position:absolute;left:16px;top:50%;transform:translateY(-50%);color:rgba(255,255,255,0.7);"></i> |
|
|
<input class="record-search" type="text" [(ngModel)]="q" (ngModelChange)="applyFilters()" placeholder="Search this list..." /> |
|
|
</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> |
|
|
<div class="summary-icon"><i class="fas fa-folder-open"></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> |
|
|
<div class="summary-icon"><i class="fas fa-exclamation-circle"></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> |
|
|
<div class="summary-icon"><i class="fas fa-check-circle"></i></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="record-meta"> |
|
|
{{ rows.length }} items • Updated a few seconds ago |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="filter-bar"> |
|
|
<span class="filter-icon"><i class="fas fa-filter"></i></span> |
|
|
<select [(ngModel)]="filterCrimeType" class="filter-select" (change)="applyFilters()"> |
|
|
<option value="">Crime Type</option> |
|
|
<option *ngFor="let type of crimeTypes">{{ type }}</option> |
|
|
</select> |
|
|
<select [(ngModel)]="filterStatus" class="filter-select" (change)="applyFilters()"> |
|
|
<option value="">Select Status</option> |
|
|
<option *ngFor="let status of statusTypes">{{ status }}</option> |
|
|
</select> |
|
|
<select [(ngModel)]="filterLocation" class="filter-select" (change)="applyFilters()"> |
|
|
<option value="">Location</option> |
|
|
<option *ngFor="let loc of locations">{{ loc }}</option> |
|
|
</select> |
|
|
<select [(ngModel)]="filterOfficer" class="filter-select" (change)="applyFilters()"> |
|
|
<option value="">Officer</option> |
|
|
<option *ngFor="let officer of officers">{{ officer }}</option> |
|
|
</select> |
|
|
<button class="filter-btn apply" (click)="applyFilters()"> |
|
|
<i class="fas fa-check"></i> Apply |
|
|
</button> |
|
|
<button class="filter-btn reset" (click)="resetFilters()"> |
|
|
<i class="fas fa-undo"></i> Reset |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="table-container"> |
|
|
<div class="table-wrapper"> |
|
|
<table class="record-table"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th class="sl-no-col"> |
|
|
<i class="fas fa-list-ol"></i> Sl. No |
|
|
</th> |
|
|
<th (click)="setSort('caseId')" [attr.aria-sort]="ariaSort('caseId')" class="sortable"> |
|
|
<i class="fas fa-id-badge"></i> Case ID |
|
|
<span class="sort" [ngClass]="{'asc': isAsc('caseId'), 'desc': isDesc('caseId'), 'neutral': !isAsc('caseId') && !isDesc('caseId')}"></span> |
|
|
</th> |
|
|
<th (click)="setSort('status')" [attr.aria-sort]="ariaSort('status')" class="sortable"> |
|
|
<i class="fas fa-info-circle"></i> 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')" class="sortable"> |
|
|
<i class="fas fa-gavel"></i> Crime Type |
|
|
<span class="sort" [ngClass]="{'asc': isAsc('crime'), 'desc': isDesc('crime'), 'neutral': !isAsc('crime') && !isDesc('crime')}"></span> |
|
|
</th> |
|
|
<th (click)="setSort('Investigation Officer')" [attr.aria-sort]="ariaSort('Investigation Officer')" class="sortable"> |
|
|
<i class="fas fa-user-tie"></i> Investigator |
|
|
<span class="sort" [ngClass]="{'asc': isAsc('Investigation Officer'), 'desc': isDesc('Investigation Officer'), 'neutral': !isAsc('Investigation Officer') && !isDesc('Investigation Officer')}"></span> |
|
|
</th> |
|
|
<th (click)="setSort('dateTime')" [attr.aria-sort]="ariaSort('dateTime')" class="sortable"> |
|
|
<i class="fas fa-calendar-alt"></i> Date & Time |
|
|
<span class="sort" [ngClass]="{'asc': isAsc('dateTime'), 'desc': isDesc('dateTime'), 'neutral': !isAsc('dateTime') && !isDesc('dateTime')}"></span> |
|
|
</th> |
|
|
<th class="actions-col"> |
|
|
<i class="fas fa-cogs"></i> Actions |
|
|
</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
<tr *ngFor="let c of rows; let i = index"> |
|
|
<td class="sl-no-col">{{ (currentPage - 1) * pageSize + i + 1 }}</td> |
|
|
<td> |
|
|
<a (click)="navigateToCaseDetails(c)" class="case-id-link">{{ c.caseId || 'Not Assigned' }}</a> |
|
|
</td> |
|
|
<td> |
|
|
<span class="status-badge" [ngClass]="{ |
|
|
'status-open': c.status === 'Open', |
|
|
'status-under': c.status === 'Under Investigation', |
|
|
'status-closed': c.status === 'Closed' |
|
|
}"> |
|
|
<span class="status-dot"></span> |
|
|
{{ c.status || 'Not Assigned' }} |
|
|
</span> |
|
|
</td> |
|
|
<td [class.empty-value]="!c.crime">{{ c.crime || 'Not Assigned' }}</td> |
|
|
<td [class.empty-value]="!c.police?.name">{{ c.police?.name || 'Not Assigned' }}</td> |
|
|
<td [class.empty-value]="!c.dateTime">{{ c.dateTime ? (c.dateTime | date:'M/d/yyyy HH:mm') : 'Not Assigned' }}</td> |
|
|
<td class="actions-col"> |
|
|
<div class="action-buttons"> |
|
|
<button class="action-btn view" (click)="navigateToCaseDetails(c)" title="View Case"> |
|
|
<i class="fas fa-eye"></i> |
|
|
</button> |
|
|
<button class="action-btn edit" (click)="editCase(c, i)" title="Edit Case"> |
|
|
<i class="fas fa-edit"></i> |
|
|
</button> |
|
|
<button class="action-btn delete" (click)="deleteCase(i)" title="Delete Case"> |
|
|
<i class="fas fa-trash"></i> |
|
|
</button> |
|
|
</div> |
|
|
</td> |
|
|
</tr> |
|
|
<tr *ngIf="rows.length === 0"> |
|
|
<td colspan="7" class="empty"> |
|
|
<i class="fas fa-inbox"></i> No records found. |
|
|
</td> |
|
|
</tr> |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<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> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="modal-blur-overlay" *ngIf="showDetails"></div> |
|
|
<div class="modal-backdrop" *ngIf="showDetails" (click)="closeDetails()"></div> |
|
|
<div class="modal" *ngIf="showDetails" role="dialog" aria-modal="true" aria-labelledby="detailsTitle"> |
|
|
<div class="modal-header"> |
|
|
<h2 id="detailsTitle"><i class="fas fa-info-circle"></i> Case Details</h2> |
|
|
<button class="modal-close" (click)="closeDetails()"><i class="fas fa-times"></i></button> |
|
|
</div> |
|
|
|
|
|
<div class="modal-body" *ngIf="selectedCase as sc"> |
|
|
<div class="modal-sections-grid"> |
|
|
<ng-container *ngFor="let sectionKey of ['crime', 'suspect', 'notes']"> |
|
|
<div class="section-block"> |
|
|
<div class="section-title"> |
|
|
<i *ngIf="sectionKey === 'crime'" class="fas fa-gavel"></i> |
|
|
<i *ngIf="sectionKey === 'suspect'" class="fas fa-user-secret"></i> |
|
|
<i *ngIf="sectionKey === 'notes'" class="fas fa-file-alt"></i> |
|
|
{{ sectionKey === 'crime' ? 'Crime Details' : sectionKey === 'suspect' ? 'Suspect Details' : 'Evidence and Documents' }} |
|
|
</div> |
|
|
<ng-container *ngFor="let subgroup of getSubgroups(sectionKey)"> |
|
|
<div class="subgroup-title"> |
|
|
<i class="fas fa-folder"></i> {{ subgroup }} |
|
|
</div> |
|
|
<div class="fields-grid"> |
|
|
<ng-container *ngFor="let field of getFieldsForSubgroup(sectionKey, subgroup)"> |
|
|
<div class="field-card"> |
|
|
<span class="field-label"><i class="fas fa-tag"></i> {{ field }}</span> |
|
|
<span class="field-value">{{ getFieldValue(sc, sectionKey, field) }}</span> |
|
|
</div> |
|
|
</ng-container> |
|
|
</div> |
|
|
</ng-container> |
|
|
</div> |
|
|
</ng-container> |
|
|
|
|
|
<div class="section-block"> |
|
|
<div class="section-title"><i class="fas fa-list"></i> All Entered Information</div> |
|
|
<div class="fields-grid"> |
|
|
<div class="field-card" *ngFor="let key of objectKeys(sc)"> |
|
|
<span class="field-label"><i class="fas fa-tag"></i> {{ key }}</span> |
|
|
<span class="field-value">{{ getValue(sc, key) }}</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-close" (click)="closeDetails()"><i class="fas fa-times"></i> Close</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<footer> |
|
|
<p>©2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p> |
|
|
</footer> |
|
|
|