| |
| <div class="site-header"> |
| <div class="header-inner"> |
| <div class="logo-cluster"> |
| <span [routerLink]="'/'" 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-progress"> |
| <span class="pykara-analysis-label">Progress:</span> |
| <div class="pykara-progress-bar"> |
| <div class="pykara-progress-bar-inner" [style.width.%]="progressPercentage"></div> |
| </div> |
| <span class="pykara-progress-percentage">{{ progressPercentage }}%</span> |
| </div> |
| <div class="header-actions autosave-right"> |
| <div class="autosave-indicator" [class.saving]="isAutoSaving"> |
| <i class="fas fa-save"></i> |
| <span>{{ autoSaveStatus }}</span> |
| </div> |
| |
| <div class="view-records-btn" style="margin-left: 16px; vertical-align: middle; color: #fff; font-size: 1.5em; width: 2em; height: 2em; display: flex; align-items: center; justify-content: center; cursor: pointer; position: relative; margin-bottom: 15px;" |
| type="button" |
| (click)="goToRecords()" |
| (mouseenter)="showViewRecordsTooltip = true" |
| (mouseleave)="showViewRecordsTooltip = false"> |
| <i class="fas fa-folder-open"></i> |
| <span *ngIf="showViewRecordsTooltip" style="position:absolute;top:100%;left:0%;transform:translateX(-50%);background:#222;color:#fff;padding:4px 12px;border-radius:6px;font-size:0.6em;white-space:nowrap;z-index:10;box-shadow:0 2px 8px rgba(0,0,0,0.12);">View Records</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="section-navigation" [@fadeIn]> |
| <div class="ai-neural-bg"></div> |
| <div class="section-pills"> |
| <button class="section-pill main-section-pill ai-enhanced" |
| *ngFor="let section of sectionKeys; let i = index" |
| [class.active]="currentSection === section" |
| [class.completed]="isSectionCompleted(section)" |
| [attr.data-step]="i + 1" |
| (click)="showSection(section)" |
| [attr.tabindex]="0"> |
| <div class="pill-neural-core"></div> |
| <i [class]="sectionIcons[section]"></i> |
| <span>{{ sections[section].title }}</span> |
| <div class="ai-completion-orb" *ngIf="isSectionCompleted(section)"> |
| <div class="orb-pulse"></div> |
| <i class="fas fa-check"></i> |
| </div> |
| <div class="pill-scanner"></div> |
| </button> |
| </div> |
| <div class="section-ai-grid"></div> |
| </div> |
|
|
| |
| <div class="subgroup-pills" |
| [class.crime-section]="currentSection === 'crime'" |
| [class.suspect-section]="currentSection === 'suspect'" |
| [class.notes-section]="currentSection === 'notes'" |
| [@fadeIn]> |
| <div class="neural-mesh"></div> |
| <div class="pill ai-subgroup-pill" |
| *ngFor="let key of getSubgroups(); let i = index" |
| [class.active]="key === currentSubgroup" |
| [class.completed]="isSubgroupCompleted(key)" |
| (click)="setSubgroup(key)" |
| [attr.tabindex]="0"> |
| <div class="pill-ai-core"></div> |
| <i [class]="subgroupIcons[key] || 'fas fa-circle'"></i> |
| <span>{{ key }}</span> |
| <div class="ai-completion-badge" *ngIf="isSubgroupCompleted(key)"> |
| <div class="badge-glow"></div> |
| <i class="fas fa-check-circle"></i> |
| </div> |
| <div class="pill-ai-scanner"></div> |
| </div> |
| </div> |
|
|
| |
| <div class="investigation-container"> |
|
|
| |
| <div class="form-card primary-card" [@cardSlide] #formCard1> |
| <div class="card-header compact-card-header"> |
| <div class="card-header-main"> |
| <h2 class="compact-title"> |
| <i [class]="subgroupIcons[currentSubgroup] || 'fas fa-circle'"></i> |
| {{ currentSubgroup }} |
| <span class="field-counter compact-field-counter">{{ getSelectedFieldCount() }} of {{ getTotalAvailableFieldsCount() }}</span> |
| </h2> |
| </div> |
| <div class="modern-field-selector-btn compact-selector-btn" style="position:relative;"> |
| <button class="field-selector-btn modern-selector-animated compact-selector-btn-inner" |
| [class.active]="showFieldSelector === (currentSection + '-' + currentSubgroup)" |
| (click)="toggleFieldSelector($event)" |
| type="button" |
| title="Select fields to display"> |
| <i class="fas fa-list-check"></i> |
| <span class="field-selector-counter">{{ getSelectedFieldCount() }}/{{ getDynamicMaxSelectable() }}</span> |
| </button> |
| |
| <div class="modern-field-selector-popup" |
| *ngIf="showFieldSelector === (currentSection + '-' + currentSubgroup)" |
| (click)="$event.stopPropagation()"> |
| <div class="popup-header"> |
| <span><i class="fas fa-list-check"></i> Select Fields to Display</span> |
| <button class="popup-close-btn" (click)="closeFieldSelector()" type="button"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="popup-fields-list"> |
| <div *ngFor="let field of getAvailableFields(); trackBy: trackByField" class="popup-field-row"> |
| <label class="popup-field-label"> |
| <input type="checkbox" |
| [checked]="isFieldSelected(field)" |
| (click)="$event.stopPropagation()" |
| (change)="toggleFieldSelection(field, $event)" /> |
| <span class="popup-field-text">{{ field }}</span> |
| </label> |
| </div> |
| </div> |
| <div class="popup-actions"> |
| <button class="popup-action-btn clear-btn" (click)="resetFieldSelection($event)" type="button"> |
| <i class="fas fa-times-circle"></i> Clear All |
| </button> |
| <button class="popup-action-btn selectall-btn" (click)="selectAllFields($event)" type="button"> |
| <i class="fas fa-check-double"></i> Select All |
| </button> |
| <button class="popup-action-btn save-btn" (click)="closeFieldSelector()" type="button"> |
| <i class="fas fa-save"></i> Save |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
|
|
| <div class="card-content" [class.minimized]="isCardMinimized.primary"> |
| <div class="section-description" [@fadeIn]> |
| <strong>{{ sections[currentSection].title }} - {{ currentSubgroup }}:</strong> {{ getSectionDescription(currentSection) }} |
| </div> |
|
|
| <div class="fields-grid"> |
| <div *ngFor="let field of getPrimaryFields(); let i = index; trackBy: trackByField" |
| class="field-container" |
| [class.filled]="formData[field] && formData[field].toString().trim() !== ''" |
| [attr.data-field]="field" |
| [@fieldAnimation]> |
|
|
| <label class="field-label"> |
| {{ field }} |
| <span class="required-indicator" *ngIf="isFieldRequired(field)">*</span> |
| <button type="button" |
| class="info-btn glossy-info-btn" |
| (click)="toggleFieldInfo(field, $event)" |
| [attr.aria-label]="'Info for ' + field"> |
| <i class="fas fa-info"></i> |
| </button> |
| |
| <ng-container *ngIf="field === 'Remark'"> |
| <span style="display:inline-flex;align-items:center;position:relative;"> |
| <button type="button" |
| (mouseenter)="showMicPopup = true" |
| (mouseleave)="showMicPopup = false" |
| (click)="toggleRecording()" |
| style="background:none;border:none;outline:none;cursor:pointer;padding:0;margin-left:6px;"> |
| <i class="fas fa-microphone" |
| [ngStyle]="{ |
| color: isRecording ? '#e74c3c' : '#3498db', |
| animation: 'micPulse 1s infinite', |
| fontSize: '1.2em', |
| transition: 'color 0.2s' |
| }" |
| style="transition:color 0.2s;"> |
| </i> |
| </button> |
| |
| <div *ngIf="showMicPopup" |
| style="position:absolute;top:120%;left:0;z-index:10;background:#fff;border:1px solid #ccc;padding:8px 12px;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.12);white-space:nowrap;min-width:160px;"> |
| Click to record your remark.<br>Release to stop.<br>Recording is {{ isRecording ? 'ON' : 'OFF' }}. |
| </div> |
| |
| <style> |
| @keyframes micPulse { |
| 0% { |
| transform: scale(1); |
| filter: drop-shadow(0 0 0 #e74c3c); |
| } |
| |
| 50% { |
| transform: scale(1.2); |
| filter: drop-shadow(0 0 8px #e74c3c); |
| } |
| |
| 100% { |
| transform: scale(1); |
| filter: drop-shadow(0 0 0 #e74c3c); |
| } |
| } |
| </style> |
| </span> |
| </ng-container> |
| </label> |
|
|
| |
| <div class="input-container"> |
| |
| <div *ngIf="fileFields.has(field); else nonFileField" class="file-upload-block"> |
| <div class="file-drop-zone" |
| (dragover)="onDragOver($event)" |
| (dragleave)="onDragLeave($event)" |
| (drop)="onFileDrop(field, $event)" |
| [class.drag-over]="isDragOver"> |
| <input type="file" |
| (change)="onFileChange(field, $event)" |
| multiple |
| [accept]="getAcceptedFileTypes(field)" |
| class="file-input" /> |
| <div class="drop-zone-content"> |
| <i class="fas fa-cloud-upload-alt"></i> |
| <p>Drop files here or click to browse</p> |
| </div> |
| </div> |
| <div class="file-list" *ngIf="uploadedFiles[field]?.length"> |
| <div *ngFor="let f of uploadedFiles[field]" class="file-chip"> |
| <i [class]="getFileIcon(f.name)"></i> |
| <span [title]="f.name">{{ f.name | slice:0:15 }}{{ f.name.length > 15 ? '...' : '' }}</span> |
| <button class="remove-file" (click)="removeFile(field, f)"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| <ng-template #nonFileField> |
| |
| <ng-container *ngIf="dateTimeFields.has(field); else nonDateTime"> |
| <input type="datetime-local" |
| class="field-input" |
| [class.compact]="isCompactField(field)" |
| [(ngModel)]="formData[field]" |
| (input)="onFieldChange(field)" /> |
| </ng-container> |
|
|
| <ng-template #nonDateTime> |
| <ng-container *ngIf="dateFields.has(field); else nonDate"> |
| <input type="date" |
| class="field-input" |
| [class.compact]="isCompactField(field)" |
| [(ngModel)]="formData[field]" |
| (input)="onFieldChange(field)" /> |
| </ng-container> |
|
|
| <ng-template #nonDate> |
| |
| <ng-container *ngIf="field === 'Country' || field === 'State' || field === 'District'; else standardField"> |
| <select class="field-input" |
| [class.compact]="isCompactField(field)" |
| [(ngModel)]="formData[field]" |
| (change)="onSelectChange(field, $event)"> |
| <option value="">-- Select {{ field }} --</option> |
| <option *ngFor="let opt of getOptions(field)" [value]="opt">{{ opt }}</option> |
| </select> |
| </ng-container> |
|
|
| <ng-template #standardField> |
| |
| <select *ngIf="getOptions(field)?.length; else textInput" |
| class="field-input" |
| [class.compact]="isCompactField(field)" |
| [(ngModel)]="formData[field]" |
| (change)="onSelectChange(field, $event)"> |
| <option value="">-- Select Option --</option> |
| <option *ngFor="let opt of getOptions(field)" [value]="opt">{{ opt }}</option> |
| </select> |
|
|
| <ng-template #textInput> |
| |
| <textarea *ngIf="field.toLowerCase().includes('description') || field === 'Remark'; else regularInput" |
| class="field-input auto-scroll-textarea" |
| [class.compact]="isCompactField(field)" |
| [(ngModel)]="formData[field]" |
| (input)="onFieldChange(field)" |
| [placeholder]="getFieldPlaceholder(field)" |
| [attr.maxlength]="field === 'Brief Description' ? null : getMaxLength(field)" |
| rows="3"></textarea> |
| |
| <div *ngIf="field === 'Remark' && isRecording" style="margin-top:4px;color:#e74c3c;font-size:0.95em;"> |
| Recording... Speak now. |
| </div> |
| <ng-template #regularInput> |
| <input [type]="getInputType(field)" |
| class="field-input" |
| [class.compact]="isCompactField(field)" |
| [(ngModel)]="formData[field]" |
| (input)="onFieldChange(field)" |
| [placeholder]="getFieldPlaceholder(field)" |
| [maxlength]="getMaxLength(field)" /> |
| </ng-template> |
| </ng-template> |
| </ng-template> |
| </ng-template> |
| </ng-template> |
| </ng-template> |
| </div> |
|
|
| |
| <div class="field-help" *ngIf="showHelpFor === field" [@helpAnimation]> |
| <strong>Field Information:</strong><br> |
| {{ fieldDescriptions[field] || 'Additional details for this field are being prepared.' }} |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| </div> |
| </div> |
|
|
| |
| <div class="modern-floating-nav-btn"> |
| |
| <button *ngIf="!(isLastSubgroup() && currentSection === 'notes' && currentSubgroup === 'Remark')" type="button" class="modern-round-btn next-btn-animated" (click)="nextSubgroup()" [disabled]="!canNextSubgroup()" title="Next"> |
| <i class="fas fa-arrow-right"></i> |
| </button> |
| |
| <button *ngIf="isLastSubgroup() && currentSection === 'notes' && currentSubgroup === 'Remark'" type="button" class="modern-round-btn submit-btn-animated" (click)="submitCurrentSection()" title="Submit"> |
| <i class="fas fa-paper-plane"></i> |
| </button> |
| </div> |
|
|
| |
| <div *ngIf="showSubmitPopup" class="submit-popup-backdrop"> |
| <div class="submit-popup-modal"> |
| <div class="submit-popup-content" (click)="onSubmitPopupClose()" style="cursor:pointer;"> |
| <i class="fas fa-check-circle submit-popup-icon"></i> |
| <h3>Submission Successful!</h3> |
| <p>Your information has been submitted.</p> |
| <button class="submit-popup-btn">OK</button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <footer> |
| <p>© 2025 Pykara Technologies Pvt. Ltd. All rights reserved.</p> |
| </footer> |
|
|
|
|
|
|
|
|