File size: 10,310 Bytes
463dbc3 73566f6 463dbc3 6f093ab 73566f6 463dbc3 73566f6 463dbc3 0dcd2ca 6f093ab 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 6f093ab 73566f6 6f093ab 73566f6 6f093ab 463dbc3 73566f6 463dbc3 73566f6 463dbc3 6f093ab 73566f6 86b4aeb 6f093ab 73566f6 6f093ab 73566f6 6f093ab 73566f6 86b4aeb 6f093ab 73566f6 6f093ab 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 73566f6 463dbc3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
export interface PoliceCase {
// Optional metadata used by your UI
caseId?: string;
dateTime?: string;
status?: 'Open' | 'Under Investigation' | 'Closed' | 'Archived';
crime?: string;
police?: {
name?: string;
station?: string;
address?: string;
pincode?: string;
dutyPerson?: string;
modeOfCrime?: string;
information?: string;
};
accused?: {
name?: string;
age?: string | number;
gender?: string;
address?: string;
occupation?: string;
};
lastUpdated?: string;
nextAction?: string;
casePriority?: 'High' | 'Medium' | 'Low';
reportedBy?: string;
verifiedBy?: string;
briefDescription?: string;
caseCategory?: string;
// Store raw form data from infopage so all entered fields can be displayed
// Use array of key/value pairs for safe rendering
formData?: Array<{ key: string; value: any }> | Record<string, any>;
selected?: boolean; // <-- Add this line for table selection
}
@Injectable({ providedIn: 'root' })
export class CaseStoreService {
private readonly storageKey = 'py_detect_police_cases';
private cases: PoliceCase[] = [];
// Observable subject to broadcast changes
private casesSubject = new BehaviorSubject<PoliceCase[]>([]);
/** Observable for components to subscribe to case list updates */
getCases$(): Observable<PoliceCase[]> {
return this.casesSubject.asObservable();
}
constructor() {
this.load();
}
/** Create (newest first) */
private getNextCaseId(): string {
const max = this.cases
.map(c => c.caseId)
.map(id => parseInt((id || '').replace('CASE-', ''),10))
.filter(n => !isNaN(n))
.reduce((a, b) => Math.max(a, b),0);
return `CASE-${(max +1).toString().padStart(3, '0')}`;
}
addPoliceCase(c: PoliceCase): void {
if (!c.caseId) {
c.caseId = this.getNextCaseId();
}
c.lastUpdated = new Date().toISOString();
c.verifiedBy = c.verifiedBy || '';
this.cases.unshift(c);
this.save();
this.debugLogCasesOperation('addPoliceCase', c);
this.casesSubject.next(this.cases.slice());
}
/** Read */
getPoliceCases(): PoliceCase[] {
return this.cases;
}
/** Update by array index */
updatePoliceCaseAt(index: number, updated: PoliceCase): void {
if (index >=0 && index < this.cases.length) {
updated.lastUpdated = new Date().toISOString();
this.cases[index] = updated;
this.save();
this.casesSubject.next(this.cases.slice());
}
}
/** Delete by array index */
deletePoliceCaseAt(index: number): void {
if (index >=0 && index < this.cases.length) {
this.cases.splice(index,1);
this.save();
this.casesSubject.next(this.cases.slice());
}
}
/** Update by caseId */
updatePoliceCaseById(caseId: string, updated: PoliceCase): void {
const idx = this.cases.findIndex(c => c.caseId === caseId);
if (idx !== -1) {
updated.lastUpdated = new Date().toISOString();
this.cases[idx] = updated;
this.save();
this.casesSubject.next(this.cases.slice());
}
}
/**
* Convenience: map Info page reactive-form value to PoliceCase and store it.
* Call with the whole this.form.value from InfopageComponent.
*/
addFromInfoForm(formValue: any): void {
const crime = (formValue && formValue.crime) || {};
const suspect = (formValue && formValue.suspect) || {};
const notes = (formValue && formValue.notes) || {};
// Merge raw inputs so we capture everything entered on the Info page
const mergedRaw = {
...(formValue && formValue.formData ? formValue.formData : (formValue || {})),
...crime,
...suspect,
...notes
};
const mapped: PoliceCase = {
caseId: crime.caseId || '',
dateTime: crime.dateTime || '',
status: notes.status || 'Open',
crime: crime.crimeType || 'Unknown',
police: {
name: notes.officerInCharge || '—',
station: '—', // not captured on Info page
address: crime.location || '—',
pincode: '', // not captured on Info page
dutyPerson: notes.officerInCharge || '—',
modeOfCrime: crime.crimeType || '—',
information: notes.initialFindings || ''
},
accused: {
name: suspect.fullName || '—',
age: suspect.age || '—',
gender: suspect.gender || '—',
address: suspect.address || '—',
occupation: suspect.alias || ''
},
// store raw form data so UI can display all entered fields
formData: this.convertToKeyValue(mergedRaw || {})
};
this.addPoliceCase(mapped);
}
/** Add or update from Info page form */
addOrUpdateFromInfoForm(formValue: any): void {
const crime = (formValue && formValue.crime) || {};
const suspect = (formValue && formValue.suspect) || {};
const notes = (formValue && formValue.notes) || {};
const mapped: PoliceCase = {
caseId: crime.caseId || (formValue && formValue['Case ID']) || '',
dateTime: crime.dateTime || '' ,
// mark when infopage submission occurred
lastUpdated: new Date().toISOString(),
status: notes.status || 'Open',
crime: crime.crimeType || 'Unknown',
police: {
name: notes.officerInCharge || '—',
station: '—',
address: crime.location || '—',
pincode: '',
dutyPerson: notes.officerInCharge || '—',
modeOfCrime: crime.crimeType || '—',
information: notes.initialFindings || ''
},
accused: {
name: suspect.fullName || '—',
age: suspect.age || '—',
gender: suspect.gender || '—',
address: suspect.address || '—',
occupation: suspect.alias || ''
},
reportedBy: crime.reportedBy || '',
verifiedBy: notes.verifiedBy || '',
briefDescription: crime.briefDescription || '',
// Save full raw form data. Merge wrapper fields so everything entered is preserved.
formData: this.convertToKeyValue(
{
...(formValue && formValue.formData ? formValue.formData : (formValue || {})),
...crime,
...suspect,
...notes,
...(formValue && formValue.legal ? formValue.legal : {})
}
)
};
const idx = this.cases.findIndex(c => c.caseId === mapped.caseId);
if (idx !== -1) {
// Merge existing object to preserve other metadata where possible
this.cases[idx] = { ...this.cases[idx], ...mapped };
// Ensure lastUpdated reflects this infopage update
this.cases[idx].lastUpdated = mapped.lastUpdated || new Date().toISOString();
this.save();
this.debugLogCasesOperation('updatePoliceCase', mapped);
this.casesSubject.next(this.cases.slice());
} else {
this.addPoliceCase(mapped);
}
}
/**
* Convert an object (or already key/value array) into an array of {key, value}
*/
private convertToKeyValue(data: any): Array<{ key: string; value: any }> {
if (!data) return [];
if (Array.isArray(data)) {
// assume already in the desired shape
return data.map(item => {
if (item && typeof item === 'object' && 'key' in item) return item;
return { key: String(item), value: item };
});
}
const result: Array<{ key: string; value: any }> = [];
const isPlainObject = (v: any) => v && typeof v === 'object' && !(v instanceof Date) && !(v instanceof File) && !Array.isArray(v);
const recurse = (obj: any, prefix = '') => {
if (obj === null || obj === undefined) return;
if (typeof obj !== 'object' || obj instanceof Date || obj instanceof File) {
result.push({ key: prefix || 'value', value: obj });
return;
}
if (Array.isArray(obj)) {
// store arrays as JSON string for readability
result.push({ key: prefix || 'value', value: JSON.stringify(obj) });
return;
}
for (const k of Object.keys(obj)) {
const v = obj[k];
const newKey = prefix ? `${prefix}.${k}` : k;
if (isPlainObject(v)) {
recurse(v, newKey);
} else if (Array.isArray(v)) {
// arrays -> stringify
result.push({ key: newKey, value: JSON.stringify(v) });
} else {
result.push({ key: newKey, value: v });
}
}
};
if (typeof data === 'object') {
recurse(data, '');
return result;
}
// primitive
return [{ key: 'value', value: data }];
}
// Simple debug helper to log saved cases (can be removed later)
private debugLogCasesOperation(label: string, mapped?: PoliceCase) {
try {
console.log(`[CaseStore] ${label}`, mapped || null);
console.log('[CaseStore] current cases:', JSON.parse(localStorage.getItem(this.storageKey) || '[]'));
} catch { }
}
/** Persist to localStorage (safe to keep; remove if not needed) */
private save(): void {
try { localStorage.setItem(this.storageKey, JSON.stringify(this.cases)); } catch { }
}
/** Load from localStorage */
private load(): void {
try {
const raw = localStorage.getItem(this.storageKey);
this.cases = raw ? (JSON.parse(raw) as PoliceCase[]) : [];
} catch {
this.cases = [];
}
// Seed a default case if none exist (for development/testing)
if (this.cases.length ===0) {
this.cases = [
{
caseId: 'CASE-001',
dateTime: new Date().toISOString(),
status: 'Open',
crime: 'Theft',
police: {
name: 'Officer John Doe',
station: 'Central Station',
address: '123 Main St, City',
pincode: '123456',
dutyPerson: 'Officer John Doe',
modeOfCrime: 'Burglary',
information: 'Initial investigation started.'
},
accused: {
name: 'Jane Smith',
age:30,
gender: 'Female',
address: '456 Side Rd, City',
occupation: 'Unemployed'
},
formData: []
}
];
this.save();
}
// Emit current cases to subscribers
this.casesSubject.next(this.cases.slice());
}
}
|