Add multiple network maps, graph UX improvements, and full backup import.
Support per-map contact membership with scoped graph views, relation editing on edges, layout caching, and automatic detection of full JSON backups so contacts and relations import together. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { localDb, DEFAULT_MAP_NAME } from '../db/localDb'
|
||||
import { generateId } from '../../lib/uuid'
|
||||
|
||||
function nowIso() {
|
||||
return new Date().toISOString()
|
||||
}
|
||||
|
||||
function withDefaults(payload = {}) {
|
||||
return {
|
||||
name: '',
|
||||
description: '',
|
||||
workspaceId: 'personal',
|
||||
...payload,
|
||||
}
|
||||
}
|
||||
|
||||
async function findActiveMap(id) {
|
||||
const direct = await localDb.networkMaps.get(id)
|
||||
if (direct && !direct.deletedAt) return direct
|
||||
const sid = String(id)
|
||||
const all = await localDb.networkMaps.toArray()
|
||||
return all.find((m) => !m.deletedAt && String(m.id) === sid) || null
|
||||
}
|
||||
|
||||
async function membershipsCount(mapId) {
|
||||
const all = await localDb.networkMapMemberships.toArray()
|
||||
return all.filter((m) => !m.deletedAt && String(m.mapId) === String(mapId)).length
|
||||
}
|
||||
|
||||
async function hydrate(map) {
|
||||
if (!map || map.deletedAt) return null
|
||||
return {
|
||||
...map,
|
||||
memberships_count: await membershipsCount(map.id),
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureDefaultMap() {
|
||||
const all = await localDb.networkMaps.toArray()
|
||||
const active = all.filter((m) => !m.deletedAt)
|
||||
if (active.length) return active.sort((a, b) => a.name.localeCompare(b.name, 'ru'))[0]
|
||||
|
||||
const ts = nowIso()
|
||||
const id = generateId()
|
||||
await localDb.networkMaps.put({
|
||||
id,
|
||||
name: DEFAULT_MAP_NAME,
|
||||
description: '',
|
||||
workspaceId: 'personal',
|
||||
version: 1,
|
||||
createdAt: ts,
|
||||
updatedAt: ts,
|
||||
deletedAt: null,
|
||||
})
|
||||
return findActiveMap(id)
|
||||
}
|
||||
|
||||
export const localNetworkMapRepository = {
|
||||
async list() {
|
||||
const all = await localDb.networkMaps.toArray()
|
||||
const filtered = all
|
||||
.filter((m) => !m.deletedAt)
|
||||
.sort((a, b) => a.name.localeCompare(b.name, 'ru'))
|
||||
if (!filtered.length) {
|
||||
const defaultMap = await ensureDefaultMap()
|
||||
return [await hydrate(defaultMap)]
|
||||
}
|
||||
const hydrated = await Promise.all(filtered.map(hydrate))
|
||||
return hydrated.filter(Boolean)
|
||||
},
|
||||
|
||||
async getById(id) {
|
||||
const map = await findActiveMap(id)
|
||||
return hydrate(map)
|
||||
},
|
||||
|
||||
async create(payload) {
|
||||
const ts = nowIso()
|
||||
const record = withDefaults(payload)
|
||||
const id = generateId()
|
||||
await localDb.networkMaps.put({
|
||||
...record,
|
||||
id,
|
||||
version: 1,
|
||||
createdAt: ts,
|
||||
updatedAt: ts,
|
||||
deletedAt: null,
|
||||
})
|
||||
return this.getById(id)
|
||||
},
|
||||
|
||||
async update(id, payload) {
|
||||
const existing = await findActiveMap(id)
|
||||
if (!existing) {
|
||||
throw new Error('Карта не найдена')
|
||||
}
|
||||
await localDb.networkMaps.update(existing.id, {
|
||||
...payload,
|
||||
updatedAt: nowIso(),
|
||||
version: Number(existing.version || 1) + 1,
|
||||
})
|
||||
return this.getById(existing.id)
|
||||
},
|
||||
|
||||
async remove(id) {
|
||||
const map = await findActiveMap(id)
|
||||
if (!map) {
|
||||
throw new Error('Карта не найдена')
|
||||
}
|
||||
const ts = nowIso()
|
||||
await localDb.networkMaps.update(map.id, {
|
||||
deletedAt: ts,
|
||||
updatedAt: ts,
|
||||
version: Number(map.version || 1) + 1,
|
||||
})
|
||||
const memberships = await localDb.networkMapMemberships
|
||||
.filter((m) => !m.deletedAt && String(m.mapId) === String(map.id))
|
||||
.toArray()
|
||||
await Promise.all(
|
||||
memberships.map((m) =>
|
||||
localDb.networkMapMemberships.update(m.id, {
|
||||
deletedAt: ts,
|
||||
updatedAt: ts,
|
||||
version: Number(m.version || 1) + 1,
|
||||
})
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user