| |
| <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 to Info Page |
| </button> |
| <button class="logout-btn" (click)="logout()"> |
| <span class="logout-icon">⎋</span> Logout |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <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> |
| <select class="record-dropdown"> |
| <option>Recently Viewed</option> |
| <option>All Records</option> |
| </select> |
| </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"> </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"> </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"> </div> |
| </div> |
| <div class="summary-icon icon-green"><i class="fas fa-check-circle fa-spin"></i></div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="record-meta" style="padding:8px 24px 0 24px; color: #6b7280; font-size:0.98em;"> |
| {{ rows.length }} items • Updated a few seconds ago |
| </div> |
|
|
| |
| <div class="filter-bar"> |
| <span style="margin-right:8px;"><i class="fas fa-filter"></i></span> |
| <select [(ngModel)]="filterCrimeType"> |
| <option value="">Crime Type</option> |
| <option *ngFor="let type of crimeTypes">{{ type }}</option> |
| </select> |
| <select [(ngModel)]="filterStatus"> |
| <option value="">Select Status</option> |
| <option *ngFor="let status of statusTypes">{{ status }}</option> |
| </select> |
| <select [(ngModel)]="filterLocation"> |
| <option value="">Location</option> |
| <option *ngFor="let loc of locations">{{ loc }}</option> |
| </select> |
| <select [(ngModel)]="filterOfficer"> |
| <option value="">Officer</option> |
| <option *ngFor="let officer of officers">{{ officer }}</option> |
| </select> |
| <button (click)="applyFilters()"><i class="fas fa-check"></i> Apply</button> |
| <button (click)="resetFilters()"><i class="fas fa-undo"></i> Reset</button> |
| </div> |
|
|
| <table class="record-table"> |
| <thead> |
| <tr> |
| <th class="select-col"><i class="fas fa-list-ol"></i> Sl. No</th> |
| <th (click)="setSort('caseId')" [attr.aria-sort]="ariaSort('caseId')" style="cursor:pointer;"> |
| <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')" style="cursor:pointer;"> |
| <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')" style="cursor:pointer;"> |
| <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')" style="cursor:pointer;"> |
| <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')" style="cursor:pointer;"> |
| <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" style="text-align:left;"><i class="fas fa-cogs"></i> Actions</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr *ngFor="let c of rows, let i = index"> |
| <td class="select-col">{{ (currentPage -1) * pageSize + i +1 }}</td> |
| <td><a (click)="navigateToCaseDetails(c)" style="cursor:pointer">{{ c.caseId || '—' }}</a></td> |
| <td> |
| <span class="status-label" |
| [ngClass]="{ |
| 'status-open': c.status === 'Open', |
| 'status-under': c.status === 'Under Investigation', |
| 'status-closed': c.status === 'Closed' |
| }"> |
| <span class="status-dot"></span>{{ c.status || '—' }} |
| </span> |
| </td> |
| <td>{{ c.crime || '—' }}</td> |
| <td>{{ c.police?.name || '—' }}</td> |
| <td>{{ c.dateTime ? (c.dateTime | date:'M/d/yyyy HH:mm') : '—' }}</td> |
| <td class="actions-col" style="text-align:left;"> |
| <button class="icon-btn view" (click)="navigateToCaseDetails(c)" title="View"> |
| <i class="fas fa-eye"></i> |
| </button> |
| <button class="icon-btn edit" (click)="editCase(c, i)" title="Edit"> |
| <i class="fas fa-edit"></i> |
| </button> |
| <button class="icon-btn delete" (click)="deleteCase(i)" title="Delete"> |
| <i class="fas fa-trash"></i> |
| </button> |
| </td> |
| </tr> |
| <tr *ngIf="rows.length ===0"> |
| <td colspan="7" class="empty">No records found.</td> |
| </tr> |
| </tbody> |
| </table> |
|
|
| |
| <div class="pagination-controls" style="display:flex;justify-content:center;align-items:center;margin:5px 0;gap:10px;"> |
| <style> |
| .pagination-controls button { |
| border: none; |
| background: #f3f4f6; |
| color: #333; |
| border-radius: 8px; |
| padding: 016px; |
| min-width: 40px; |
| min-height: 40px; |
| font-size: 1.1em; |
| font-weight: 500; |
| box-shadow: 0 2px 8px rgba(0,0,0,0.04); |
| transition: background 0.2s, color 0.2s, transform 0.2s; |
| cursor: pointer; |
| outline: none; |
| } |
| |
| .pagination-controls button:hover:not(:disabled), |
| .pagination-controls button:focus:not(:disabled) { |
| background: #e3eafe; |
| color: #1976d2; |
| transform: scale(1.08); |
| } |
| |
| .pagination-controls button.active { |
| background: #1976d2; |
| color: #fff; |
| font-weight: bold; |
| box-shadow: 0002px #90caf9; |
| animation: pulseActive1s infinite; |
| } |
| |
| @keyframes pulseActive { |
| 0% { |
| box-shadow: 0002px #90caf9; |
| } |
| |
| 50% { |
| box-shadow: 0006px #90caf9; |
| } |
| |
| 100% { |
| box-shadow: 0002px #90caf9; |
| } |
| } |
| |
| .pagination-controls span { |
| font-size: 1.2em; |
| color: #888; |
| padding: 08px; |
| } |
| </style> |
| <button (click)="prevPage()" [disabled]="currentPage ===1">«</button> |
| <ng-container *ngFor="let page of getPagination()"> |
| <button *ngIf="page !== '...'" (click)="goToPage(page)" [class.active]="currentPage === page">{{ page }}</button> |
| <span *ngIf="page === '...'">...</span> |
| </ng-container> |
| <button (click)="nextPage()" [disabled]="currentPage === totalPages">»</button> |
| </div> |
| </div> |
|
|
| |
| <div style="display:flex;align-items:center;justify-content:flex-start;gap:24px;margin-bottom:12px;"> |
| <span style="font-size:1.1em;"><i class="fas fa-list-ol"></i> Results: {{ resultsStart }} - {{ resultsEnd }} of {{ resultsTotal }}</span> |
| <select [(ngModel)]="pageSize" (change)="onPageSizeChange(pageSize)" style="padding:4px 12px;border-radius:8px;font-size:1em;"> |
| <option *ngFor="let size of pageSizeOptions" [value]="size">{{ size }}</option> |
| </select> |
| </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> |
| </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" style="margin:10px 0 4px 0;font-weight:600;color:#1976d2;"> |
| <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" style="display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid #f3f4f6;"> |
| <span style="font-weight:500;color:#333;"><i class="fas fa-tag"></i> {{ field }}</span> |
| <span style="color:#444;">{{ 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)" style="display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid #f3f4f6;"> |
| <span style="font-weight:500;color:#333;"><i class="fas fa-tag"></i> {{ key }}</span> |
| <span style="color:#444;">{{ getValue(sc, key) }}</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="modal-footer"> |
| <button type="button" class="btn" (click)="closeDetails()"><i class="fas fa-times"></i> Close</button> |
| </div> |
| </div> |
|
|
| <footer> |
| <p>© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p> |
| </footer> |
|
|
|
|
|
|