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());
  }
}