/** * Class model for Individual Interface */ import { reactive } from 'vue' import { generateUrid, generateKey } from "../utils/key-generator"; import type { IndividualAnniversary, IndividualCrypto, IndividualEmail, IndividualInterface, IndividualLanguage, IndividualMedia, IndividualName, IndividualNote, IndividualOrganization, IndividualPhone, IndividualPhysicalLocation, IndividualTitle, IndividualVirtualLocation } from "@/types/individual"; export class IndividualObject implements IndividualInterface { _data: IndividualInterface; constructor() { this._data = reactive({ type: 'individual', version: 1, urid: generateUrid(), created: null, modified: null, label: '', names: { family: null, given: null, additional: null, prefix: null, suffix: null, phoneticFamily: null, phoneticGiven: null, phoneticAdditional: null, aliases: [] }, titles: {}, anniversaries: [], physicalLocations: {}, phones: {}, emails: {}, virtualLocations: {}, media: {}, organizations: {}, tags: [], notes: {}, language: null, languages: [], crypto: {}, }); } fromJson(data: IndividualInterface) : IndividualObject { // Normalize arrays to Records for properties that should be Record types const normalized = { ...data, titles: Array.isArray(data.titles) ? {} : (data.titles || {}), physicalLocations: Array.isArray(data.physicalLocations) ? {} : (data.physicalLocations || {}), phones: Array.isArray(data.phones) ? {} : (data.phones || {}), emails: Array.isArray(data.emails) ? {} : (data.emails || {}), virtualLocations: Array.isArray(data.virtualLocations) ? {} : (data.virtualLocations || {}), media: Array.isArray(data.media) ? {} : (data.media || {}), notes: Array.isArray(data.notes) ? {} : (data.notes || {}), crypto: Array.isArray(data.crypto) ? {} : (data.crypto || {}), }; this._data = reactive(normalized); return this; } toJson(): IndividualInterface { return this._data; } clone(): IndividualObject { const cloned = new IndividualObject() cloned._data = JSON.parse(JSON.stringify(this._data)) return cloned } /** Immutable Properties */ get type(): string { return this._data.type; } get version(): number { return this._data.version; } get urid(): string | null { return this._data.urid; } /** Mutable Properties */ get created(): Date | null { return this._data.created; } set created(value: Date | null) { this._data.created = value; } get modified(): Date | null { return this._data.modified; } set modified(value: Date | null) { this._data.modified = value; } get label(): string | null { return this._data.label; } set label(value: string | null) { this._data.label = value; } get names(): IndividualName { return this._data.names; } set names(value: IndividualName) { this._data.names = value; } get titles(): Record { return this._data.titles; } set titles(value: Record) { this._data.titles = value; } get anniversaries(): IndividualAnniversary[] { return this._data.anniversaries; } set anniversaries(value: IndividualAnniversary[]) { this._data.anniversaries = value; } get physicalLocations(): Record { return this._data.physicalLocations; } set physicalLocations(value: Record) { this._data.physicalLocations = value; } get phones(): Record { return this._data.phones; } set phones(value: Record) { this._data.phones = value; } get emails(): Record { return this._data.emails; } set emails(value: Record) { this._data.emails = value; } get virtualLocations(): Record { return this._data.virtualLocations; } set virtualLocations(value: Record) { this._data.virtualLocations = value; } get media(): Record { return this._data.media; } set media(value: Record) { this._data.media = value; } get organizations(): Record { return this._data.organizations; } set organizations(value: Record) { this._data.organizations = value; } get tags(): string[] { return this._data.tags; } set tags(value: string[]) { this._data.tags = value; } get notes(): Record { return this._data.notes; } set notes(value: Record) { this._data.notes = value; } get language(): string | null { return this._data.language; } set language(value: string | null) { this._data.language = value; } get languages(): IndividualLanguage[] { return this._data.languages; } set languages(value: IndividualLanguage[]) { this._data.languages = value; } get crypto(): Record { return this._data.crypto; } set crypto(value: Record) { this._data.crypto = value; } /** * Mutation helpers for complex properties */ addTitle(initial?: Partial): string { if (!this._data.titles) { this._data.titles = {}; } const key = generateKey(); this._data.titles[key] = { kind: null, label: null, relation: null, context: null, priority: null, ...initial, }; return key; } removeTitle(key: string): void { if (this._data.titles) { delete this._data.titles[key]; } } addAnniversary(initial?: Partial): number { const anniversaries = this._data.anniversaries || (this._data.anniversaries = []); const entry: IndividualAnniversary = { type: null, when: null, location: null, ...initial, }; anniversaries.push(entry); return anniversaries.length - 1; } removeAnniversary(index: number): void { const anniversaries = this._data.anniversaries; if (index >= 0 && index < anniversaries.length) { anniversaries.splice(index, 1); } } addPhysicalLocation(initial?: Partial): string { const locations = this._data.physicalLocations || (this._data.physicalLocations = {}); const key = generateKey(); locations[key] = { box: null, unit: null, street: null, locality: null, region: null, code: null, country: null, label: null, coordinates: null, timeZone: null, context: null, priority: null, ...initial, }; return key; } removePhysicalLocation(key: string): void { if (this._data.physicalLocations) { delete this._data.physicalLocations[key]; } } addPhone(initial?: Partial): string { const phones = this._data.phones || (this._data.phones = {}); const key = generateKey(); phones[key] = { number: null, label: null, context: null, priority: null, ...initial, }; return key; } removePhone(key: string): void { if (this._data.phones) { delete this._data.phones[key]; } } addEmail(initial?: Partial): string { const emails = this._data.emails || (this._data.emails = {}); const key = generateKey(); emails[key] = { address: null, context: null, priority: null, ...initial, }; return key; } removeEmail(key: string): void { if (this._data.emails) { delete this._data.emails[key]; } } addVirtualLocation(initial?: Partial): string { const virtualLocations = this._data.virtualLocations || (this._data.virtualLocations = {}); const key = generateKey(); virtualLocations[key] = { location: null, label: null, context: null, priority: null, ...initial, }; return key; } removeVirtualLocation(key: string): void { if (this._data.virtualLocations) { delete this._data.virtualLocations[key]; } } addMedia(initial?: Partial): string { const media = this._data.media || (this._data.media = {}); const key = generateKey(); media[key] = { type: "photo", kind: "photo", uri: "", mediaType: null, contexts: null, pref: null, label: null, ...initial, }; return key; } removeMedia(key: string): void { if (this._data.media) { delete this._data.media[key]; } } addOrganization(initial?: Partial): string { const organizations = this._data.organizations || (this._data.organizations = {}); const key = generateKey(); organizations[key] = { Label: null, Units: [], sortName: null, context: null, priority: null, ...initial, }; return key; } removeOrganization(key: string): void { if (this._data.organizations) { delete this._data.organizations[key]; } } addNote(initial?: Partial): string { const notes = this._data.notes || (this._data.notes = {}); const key = generateKey(); notes[key] = { content: null, date: null, authorUri: null, authorName: null, context: null, priority: null, ...initial, }; return key; } removeNote(key: string): void { if (this._data.notes) { delete this._data.notes[key]; } } addLanguage(initial?: Partial): number { const languages = this._data.languages || (this._data.languages = []); const entry: IndividualLanguage = { Data: null, Id: null, Priority: null, Context: null, ...initial, }; languages.push(entry); return languages.length - 1; } removeLanguage(index: number): void { const languages = this._data.languages; if (index >= 0 && index < languages.length) { languages.splice(index, 1); } } addCrypto(initial?: Partial): string { const cryptoEntries = this._data.crypto || (this._data.crypto = {}); const key = generateKey(); cryptoEntries[key] = { data: null, type: null, context: null, priority: null, ...initial, }; return key; } removeCrypto(key: string): void { if (this._data.crypto) { delete this._data.crypto[key]; } } addTag(value: string): void { const tags = this._data.tags || (this._data.tags = []); tags.push(value); } removeTag(value: string): void { const tags = this._data.tags; const index = tags.indexOf(value); if (index >= 0) { tags.splice(index, 1); } } }