refactor: standardize provider #3
@@ -2,7 +2,7 @@
|
|||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useServicesStore } from '@PeopleManager/stores/servicesStore'
|
import { useServicesStore } from '@PeopleManager/stores/servicesStore'
|
||||||
import { CollectionObject } from '@PeopleManager/models/collection'
|
import { CollectionObject } from '@PeopleManager/models/collection'
|
||||||
import { ServiceObject } from '@PeopleManager/models/service'
|
import type { ServiceObject } from '@PeopleManager/models/service'
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
const servicesStore = useServicesStore()
|
const servicesStore = useServicesStore()
|
||||||
@@ -51,7 +51,7 @@ const dialogOpen = computed({
|
|||||||
// Functions
|
// Functions
|
||||||
const onOpen = async () => {
|
const onOpen = async () => {
|
||||||
if (services.value.length === 0) {
|
if (services.value.length === 0) {
|
||||||
services.value = await servicesStore.list()
|
services.value = Object.values(await servicesStore.list())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props.collection) {
|
if (!props.collection) {
|
||||||
@@ -61,14 +61,14 @@ const onOpen = async () => {
|
|||||||
// Clone the collection to avoid mutating the original
|
// Clone the collection to avoid mutating the original
|
||||||
editingCollection.value = props.collection.clone()
|
editingCollection.value = props.collection.clone()
|
||||||
|
|
||||||
if (props.collection.id !== null) {
|
if (props.collection.identifier) {
|
||||||
// Edit mode - find the service
|
// Edit mode - find the service
|
||||||
editingCollectionService.value = services.value.find(s =>
|
editingCollectionService.value = services.value.find(s =>
|
||||||
s.provider === props.collection!.provider && s.id === props.collection!.service
|
s.provider === props.collection!.provider && s.identifier === props.collection!.service
|
||||||
) || null
|
) || null
|
||||||
} else {
|
} else {
|
||||||
// Create mode - use first service that can create
|
// Create mode - use first service that can create
|
||||||
editingCollectionService.value = services.value.filter(s => s.capabilities?.CollectionCreate)[0] || null
|
editingCollectionService.value = services.value.filter(s => s.capable('CollectionCreate'))[0] || null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ const onColorSelect = (color: string | null, closeMenu = true) => {
|
|||||||
if (!editingCollection.value) {
|
if (!editingCollection.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editingCollection.value.color = color
|
editingCollection.value.properties.color = color
|
||||||
if (closeMenu) {
|
if (closeMenu) {
|
||||||
colorMenuOpen.value = false
|
colorMenuOpen.value = false
|
||||||
}
|
}
|
||||||
@@ -132,9 +132,9 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
<v-combobox v-show="mode === 'create' && services.length > 1"
|
<v-combobox v-show="mode === 'create' && services.length > 1"
|
||||||
v-model="editingCollectionService"
|
v-model="editingCollectionService"
|
||||||
label="Service"
|
label="Service"
|
||||||
:items="services.filter(s => s.capabilities?.CollectionCreate)"
|
:items="services.filter(s => s.capable('CollectionCreate'))"
|
||||||
item-title="label"
|
item-title="label"
|
||||||
item-value="id"
|
item-value="identifier"
|
||||||
required
|
required
|
||||||
:rules="[(v: ServiceObject) => !!v || 'Service is required']"
|
:rules="[(v: ServiceObject) => !!v || 'Service is required']"
|
||||||
/>
|
/>
|
||||||
@@ -142,7 +142,7 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
<div class="mb-4"><strong>Service </strong> {{ editingCollection.service }}</div>
|
<div class="mb-4"><strong>Service </strong> {{ editingCollection.service }}</div>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="editingCollection.label"
|
v-model="editingCollection.properties.label"
|
||||||
label="Label"
|
label="Label"
|
||||||
required
|
required
|
||||||
:rules="[(v: string) => !!v || 'Label is required']"
|
:rules="[(v: string) => !!v || 'Label is required']"
|
||||||
@@ -160,7 +160,7 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
icon
|
icon
|
||||||
variant="text"
|
variant="text"
|
||||||
size="small"
|
size="small"
|
||||||
:style="{ color: editingCollection.color || 'var(--v-theme-on-surface)' }"
|
:style="{ color: editingCollection.properties.color || 'var(--v-theme-on-surface)' }"
|
||||||
aria-label="Select book color"
|
aria-label="Select book color"
|
||||||
title="Select color"
|
title="Select color"
|
||||||
>
|
>
|
||||||
@@ -192,11 +192,11 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
variant="flat"
|
variant="flat"
|
||||||
size="small"
|
size="small"
|
||||||
class="color-menu-body__presets--swatch"
|
class="color-menu-body__presets--swatch"
|
||||||
:class="{ 'color-menu-body__presets--swatch--active': editingCollection.color === color }"
|
:class="{ 'color-menu-body__presets--swatch--active': editingCollection.properties.color === color }"
|
||||||
:style="{ backgroundColor: color }"
|
:style="{ backgroundColor: color }"
|
||||||
@click="onColorSelect(color)">
|
@click="onColorSelect(color)">
|
||||||
<v-icon
|
<v-icon
|
||||||
v-if="editingCollection.color === color"
|
v-if="editingCollection.properties.color === color"
|
||||||
icon="mdi-check"
|
icon="mdi-check"
|
||||||
size="x-small"
|
size="x-small"
|
||||||
color="white"
|
color="white"
|
||||||
@@ -207,7 +207,7 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
|
|
||||||
<div class="color-menu-body__picker">
|
<div class="color-menu-body__picker">
|
||||||
<v-color-picker
|
<v-color-picker
|
||||||
v-model="editingCollection.color"
|
v-model="editingCollection.properties.color"
|
||||||
mode="hex"
|
mode="hex"
|
||||||
hide-canvas
|
hide-canvas
|
||||||
width="100%"
|
width="100%"
|
||||||
@@ -221,20 +221,10 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
</v-text-field>
|
</v-text-field>
|
||||||
|
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-model="editingCollection.description"
|
v-model="editingCollection.properties.description"
|
||||||
label="Description"
|
label="Description"
|
||||||
rows="2"
|
rows="2"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col v-if="mode === 'edit'" cols="6">
|
|
||||||
<v-switch
|
|
||||||
v-model="editingCollection.enabled"
|
|
||||||
label="Enabled"
|
|
||||||
color="primary"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
@@ -243,7 +233,7 @@ watch(() => props.modelValue, async (newValue) => {
|
|||||||
<v-card-actions class="justify-space-between align-center">
|
<v-card-actions class="justify-space-between align-center">
|
||||||
<div>
|
<div>
|
||||||
<v-btn
|
<v-btn
|
||||||
v-if="mode === 'edit' && editingCollectionService?.capabilities?.CollectionDestroy"
|
v-if="mode === 'edit' && editingCollectionService?.capable('CollectionDestroy')"
|
||||||
color="error"
|
color="error"
|
||||||
variant="text"
|
variant="text"
|
||||||
@click="onDelete"
|
@click="onDelete"
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
|
||||||
import { useCollectionsStore } from '@PeopleManager/stores/collectionsStore'
|
|
||||||
import { CollectionObject } from '@PeopleManager/models/collection';
|
import { CollectionObject } from '@PeopleManager/models/collection';
|
||||||
|
|
||||||
// Store
|
|
||||||
const collectionsStore = useCollectionsStore()
|
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
collections: CollectionObject[]
|
||||||
|
loading?: boolean
|
||||||
selectedCollection?: CollectionObject | null
|
selectedCollection?: CollectionObject | null
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -17,43 +14,14 @@ const emit = defineEmits<{
|
|||||||
'edit': [collection: CollectionObject]
|
'edit': [collection: CollectionObject]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// State
|
|
||||||
const loading = ref(false)
|
|
||||||
const collections = ref<CollectionObject[]>([])
|
|
||||||
|
|
||||||
// Lifecycle
|
|
||||||
onMounted(async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
collections.value = await collectionsStore.list()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[People] - Failed to load collections:', error)
|
|
||||||
}
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
const onCollectionSelect = (collection: CollectionObject) => {
|
const onCollectionSelect = (collection: CollectionObject) => {
|
||||||
console.log('[People] - Collection selected', collection)
|
|
||||||
emit('select', collection)
|
emit('select', collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCollectionEdit = (collection: CollectionObject) => {
|
const onCollectionEdit = (collection: CollectionObject) => {
|
||||||
emit('edit', collection)
|
emit('edit', collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose refresh method
|
|
||||||
defineExpose({
|
|
||||||
async refresh() {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
collections.value = await collectionsStore.list()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[People] - Failed to load collections:', error)
|
|
||||||
}
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -73,18 +41,18 @@ defineExpose({
|
|||||||
<v-list v-else density="compact" nav class="pa-0">
|
<v-list v-else density="compact" nav class="pa-0">
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="collection in collections"
|
v-for="collection in collections"
|
||||||
:key="collection.id"
|
:key="collection.identifier"
|
||||||
:value="collection.id"
|
:value="collection.identifier"
|
||||||
:active="selectedCollection?.id === collection.id"
|
:active="selectedCollection?.identifier === collection.identifier"
|
||||||
@click="onCollectionSelect(collection)"
|
@click="onCollectionSelect(collection)"
|
||||||
rounded="lg"
|
rounded="lg"
|
||||||
class="mb-1"
|
class="mb-1"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<v-icon :color="collection.color || 'primary'" icon="mdi-book-outline" size="small" />
|
<v-icon :color="collection.properties.color || 'primary'" icon="mdi-book-outline" size="small" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<v-list-item-title>{{ collection.label }}</v-list-item-title>
|
<v-list-item-title>{{ collection.properties.label || 'Unnamed' }}</v-list-item-title>
|
||||||
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<v-btn
|
<v-btn
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ const saving = ref(false)
|
|||||||
// Computed
|
// Computed
|
||||||
const mode = computed(() => props.mode)
|
const mode = computed(() => props.mode)
|
||||||
const entity = computed(() => props.selectedEntity || null)
|
const entity = computed(() => props.selectedEntity || null)
|
||||||
const entityObject = computed(() => entity.value?.data ?? null)
|
const entityObject = computed(() => entity.value?.properties ?? null)
|
||||||
const entityFresh = computed(() => entity.value?.id === null || entity.value?.id === undefined)
|
const entityFresh = computed(() => !entity.value?.identifier)
|
||||||
const entityType = computed(() => entityObject.value?.type || 'individual')
|
const entityType = computed(() => (entityObject.value as any)?.type || 'individual')
|
||||||
|
|
||||||
// Determine which sections to show based on entity type
|
// Determine which sections to show based on entity type
|
||||||
const showNames = computed(() => entityType.value === 'individual')
|
const showNames = computed(() => entityType.value === 'individual')
|
||||||
@@ -194,7 +194,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
<div class="people-editor-header d-flex justify-space-between pa-4">
|
<div class="people-editor-header d-flex justify-space-between pa-4">
|
||||||
<div>
|
<div>
|
||||||
<v-icon :icon="entityIcon" class="mr-2" />
|
<v-icon :icon="entityIcon" class="mr-2" />
|
||||||
{{ entity?.data?.label || 'Nothing Selected' }}
|
{{ entity?.properties?.label || 'Nothing Selected' }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!loading && entity">
|
<div v-if="!loading && entity">
|
||||||
@@ -236,20 +236,20 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
<PersonEditorLabel
|
<PersonEditorLabel
|
||||||
v-if="showLabel"
|
v-if="showLabel"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:label="entity.data!.label"
|
:label="(entityObject as any)?.label"
|
||||||
@update:label="entity.data!.label = $event"
|
@update:label="(entityObject as any).label = $event"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PersonEditorNames
|
<PersonEditorNames
|
||||||
v-if="showNames"
|
v-if="showNames"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:names="entity.data!.names"
|
:names="(entityObject as any)?.names"
|
||||||
@update:names="entity.data!.names = $event"
|
@update:names="(entityObject as any).names = $event"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PersonEditorTitles
|
<PersonEditorTitles
|
||||||
v-if="showTitles"
|
v-if="showTitles"
|
||||||
:titles="(entity.data as any)!.titles || {}"
|
:titles="(entityObject as any)?.titles || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-title="addTitle"
|
@add-title="addTitle"
|
||||||
@remove-title="removeTitle"
|
@remove-title="removeTitle"
|
||||||
@@ -257,7 +257,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorAnniversaries
|
<PersonEditorAnniversaries
|
||||||
v-if="showAnniversaries"
|
v-if="showAnniversaries"
|
||||||
:anniversaries="(entity.data as any)!.anniversaries || []"
|
:anniversaries="(entityObject as any)?.anniversaries || []"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-anniversary="addAnniversary"
|
@add-anniversary="addAnniversary"
|
||||||
@remove-anniversary="removeAnniversary"
|
@remove-anniversary="removeAnniversary"
|
||||||
@@ -266,7 +266,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorPhysicalLocations
|
<PersonEditorPhysicalLocations
|
||||||
v-if="showPhysicalLocations"
|
v-if="showPhysicalLocations"
|
||||||
:physical-locations="(entity.data as any)!.physicalLocations || {}"
|
:physical-locations="(entityObject as any)?.physicalLocations || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-physical-location="addPhysicalLocation"
|
@add-physical-location="addPhysicalLocation"
|
||||||
@remove-physical-location="removePhysicalLocation"
|
@remove-physical-location="removePhysicalLocation"
|
||||||
@@ -274,7 +274,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorPhones
|
<PersonEditorPhones
|
||||||
v-if="showPhones"
|
v-if="showPhones"
|
||||||
:phones="(entity.data as any)!.phones || {}"
|
:phones="(entityObject as any)?.phones || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-phone="addPhone"
|
@add-phone="addPhone"
|
||||||
@remove-phone="removePhone"
|
@remove-phone="removePhone"
|
||||||
@@ -282,7 +282,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorEmails
|
<PersonEditorEmails
|
||||||
v-if="showEmails"
|
v-if="showEmails"
|
||||||
:emails="(entity.data as any)!.emails || {}"
|
:emails="(entityObject as any)?.emails || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-email="addEmail"
|
@add-email="addEmail"
|
||||||
@remove-email="removeEmail"
|
@remove-email="removeEmail"
|
||||||
@@ -290,7 +290,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorVirtualLocations
|
<PersonEditorVirtualLocations
|
||||||
v-if="showVirtualLocations"
|
v-if="showVirtualLocations"
|
||||||
:virtual-locations="(entity.data as any)!.virtualLocations || {}"
|
:virtual-locations="(entityObject as any)?.virtualLocations || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-virtual-location="addVirtualLocation"
|
@add-virtual-location="addVirtualLocation"
|
||||||
@remove-virtual-location="removeVirtualLocation"
|
@remove-virtual-location="removeVirtualLocation"
|
||||||
@@ -298,7 +298,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorOrganizations
|
<PersonEditorOrganizations
|
||||||
v-if="showOrganizations"
|
v-if="showOrganizations"
|
||||||
:organizations="(entity.data as any)!.organizations || {}"
|
:organizations="(entityObject as any)?.organizations || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-organization="addOrganization"
|
@add-organization="addOrganization"
|
||||||
@remove-organization="removeOrganization"
|
@remove-organization="removeOrganization"
|
||||||
@@ -306,7 +306,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorMembers
|
<PersonEditorMembers
|
||||||
v-if="showMembers"
|
v-if="showMembers"
|
||||||
:members="(entity.data as any)!.members || {}"
|
:members="(entityObject as any)?.members || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-member="addMember"
|
@add-member="addMember"
|
||||||
@remove-member="removeMember"
|
@remove-member="removeMember"
|
||||||
@@ -314,7 +314,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorNotes
|
<PersonEditorNotes
|
||||||
v-if="showNotes"
|
v-if="showNotes"
|
||||||
:notes="entity.data!.notes || {}"
|
:notes="(entityObject as any)?.notes || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-note="addNote"
|
@add-note="addNote"
|
||||||
@remove-note="removeNote"
|
@remove-note="removeNote"
|
||||||
@@ -322,7 +322,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorLanguages
|
<PersonEditorLanguages
|
||||||
v-if="showLanguages"
|
v-if="showLanguages"
|
||||||
:languages="(entity.data as any)!.languages || []"
|
:languages="(entityObject as any)?.languages || []"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-language="addLanguage"
|
@add-language="addLanguage"
|
||||||
@remove-language="removeLanguage"
|
@remove-language="removeLanguage"
|
||||||
@@ -330,7 +330,7 @@ const removeVirtualLocation = (key: string) => {
|
|||||||
|
|
||||||
<PersonEditorMedia
|
<PersonEditorMedia
|
||||||
v-if="showMedia"
|
v-if="showMedia"
|
||||||
:media="entity.data!.media || {}"
|
:media="(entityObject as any)?.media || {}"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@add-media="addMedia"
|
@add-media="addMedia"
|
||||||
@remove-media="removeMedia"
|
@remove-media="removeMedia"
|
||||||
|
|||||||
@@ -31,8 +31,15 @@ const filteredEntities = computed(() => {
|
|||||||
}
|
}
|
||||||
const query = searchQuery.value.toLowerCase()
|
const query = searchQuery.value.toLowerCase()
|
||||||
return collectionEntities.value.filter(entity => {
|
return collectionEntities.value.filter(entity => {
|
||||||
const name = `${entity.data?.names.family || ''} ${entity.data?.names.given || ''}`.toLowerCase()
|
const props = (entity.properties as any)
|
||||||
const email = (entity.data?.emails?.[0]?.address || '').toLowerCase()
|
const given = props?.names?.given || ''
|
||||||
|
const family = props?.names?.family || ''
|
||||||
|
const full = props?.names?.full || ''
|
||||||
|
const label = props?.label || ''
|
||||||
|
const name = `${given} ${family} ${full} ${label}`.toLowerCase()
|
||||||
|
const emails = props?.emails
|
||||||
|
const firstEmail = emails ? Object.values(emails)[0] as any : null
|
||||||
|
const email = (firstEmail?.address || '').toLowerCase()
|
||||||
return name.includes(query) || email.includes(query)
|
return name.includes(query) || email.includes(query)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -42,13 +49,17 @@ watch(() => props.selectedCollection, async (newCollection) => {
|
|||||||
if (newCollection) {
|
if (newCollection) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
collectionEntities.value = await entitiesStore.list(
|
const sources = {
|
||||||
newCollection.provider,
|
[newCollection.provider]: {
|
||||||
newCollection.service,
|
[String(newCollection.service)]: {
|
||||||
newCollection.id
|
[String(newCollection.identifier)]: true as const,
|
||||||
);
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const result = await entitiesStore.list(sources)
|
||||||
|
collectionEntities.value = Object.values(result)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[People] - Failed to load contacts:', error);
|
console.error('[People] - Failed to load contacts:', error)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -56,11 +67,11 @@ watch(() => props.selectedCollection, async (newCollection) => {
|
|||||||
collectionEntities.value = []
|
collectionEntities.value = []
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}, { immediate: true });
|
}, { immediate: true })
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
function entityType(entity: EntityObject): string {
|
function entityType(entity: EntityObject): string {
|
||||||
return entity.data?.type || 'individual'
|
return (entity.properties as any)?.type || 'individual'
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityIcon(entity: EntityObject): string {
|
function entityIcon(entity: EntityObject): string {
|
||||||
@@ -76,62 +87,65 @@ function entityIcon(entity: EntityObject): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function entityPhoto(entity: EntityObject): string | null {
|
function entityPhoto(entity: EntityObject): string | null {
|
||||||
// TODO: Implement photo retrieval from entity data
|
// TODO: Implement photo retrieval from entity properties
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityInitials(entity: EntityObject): string {
|
function entityInitials(entity: EntityObject): string {
|
||||||
const type = entityType(entity)
|
const type = entityType(entity)
|
||||||
|
const props = (entity.properties as any)
|
||||||
|
|
||||||
if (type === 'organization') {
|
if (type === 'organization') {
|
||||||
const name = (entity.data as any)?.names?.full || entity.data?.label || ''
|
const name = props?.names?.full || props?.label || ''
|
||||||
return name.substring(0, 2).toUpperCase() || 'OR'
|
return name.substring(0, 2).toUpperCase() || 'OR'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'group') {
|
if (type === 'group') {
|
||||||
const name = (entity.data as any)?.names?.full || entity.data?.label || ''
|
const name = props?.names?.full || props?.label || ''
|
||||||
return name.substring(0, 2).toUpperCase() || 'GR'
|
return name.substring(0, 2).toUpperCase() || 'GR'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Individual
|
// Individual
|
||||||
const given = (entity.data as any)?.names?.given
|
const given = props?.names?.given
|
||||||
const family = (entity.data as any)?.names?.family
|
const family = props?.names?.family
|
||||||
const initials = `${given?.[0] || ''}${family?.[0] || ''}`.toUpperCase()
|
const initials = `${given?.[0] || ''}${family?.[0] || ''}`.toUpperCase()
|
||||||
return initials || '?'
|
return initials || '?'
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityLabel(entity: EntityObject): string {
|
function entityLabel(entity: EntityObject): string {
|
||||||
const type = entityType(entity)
|
const type = entityType(entity)
|
||||||
|
const props = (entity.properties as any)
|
||||||
|
|
||||||
if (type === 'organization' || type === 'group') {
|
if (type === 'organization' || type === 'group') {
|
||||||
return entity.data?.label || (entity.data as any)?.names?.full || 'Unknown'
|
return props?.label || props?.names?.full || 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Individual
|
// Individual
|
||||||
return entity.data?.label || `${(entity.data as any)?.names?.given || ''} ${(entity.data as any)?.names?.family || ''}`.trim() || 'Unknown'
|
const given = props?.names?.given || ''
|
||||||
|
const family = props?.names?.family || ''
|
||||||
|
return props?.label || `${given} ${family}`.trim() || 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityEmail(entity: EntityObject): string | null {
|
function entityEmail(entity: EntityObject): string | null {
|
||||||
const emails = (entity.data as any)?.emails
|
const emails = (entity.properties as any)?.emails
|
||||||
if (!emails) return null
|
if (!emails) return null
|
||||||
const emailEntries = Object.values(emails)
|
const emailEntries = Object.values(emails) as any[]
|
||||||
if (emailEntries.length === 0) return null
|
if (emailEntries.length === 0) return null
|
||||||
// Sort by priority (assuming lower number = higher priority)
|
emailEntries.sort((a, b) => (a.priority || 999) - (b.priority || 999))
|
||||||
emailEntries.sort((a: any, b: any) => (a.priority || 999) - (b.priority || 999))
|
return emailEntries[0].address || null
|
||||||
return (emailEntries[0] as any).address || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityOrganization(entity: EntityObject): string | null {
|
function entityOrganization(entity: EntityObject): string | null {
|
||||||
const organizations = (entity.data as any)?.organizations
|
const organizations = (entity.properties as any)?.organizations
|
||||||
if (!organizations || Object.keys(organizations).length === 0) return null
|
if (!organizations || Object.keys(organizations).length === 0) return null
|
||||||
const orgEntries = Object.values(organizations)
|
const orgEntries = Object.values(organizations) as any[]
|
||||||
return (orgEntries[0] as any).Label || null
|
return orgEntries[0].label || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityMemberCount(entity: EntityObject): number | null {
|
function entityMemberCount(entity: EntityObject): number | null {
|
||||||
const type = entityType(entity)
|
const type = entityType(entity)
|
||||||
if (type !== 'group') return null
|
if (type !== 'group') return null
|
||||||
const members = (entity.data as any)?.members
|
const members = (entity.properties as any)?.members
|
||||||
if (!members) return 0
|
if (!members) return 0
|
||||||
return Object.keys(members).length
|
return Object.keys(members).length
|
||||||
}
|
}
|
||||||
@@ -179,8 +193,8 @@ function entityAvatarColor(entity: EntityObject): string {
|
|||||||
<v-list v-else density="compact" nav>
|
<v-list v-else density="compact" nav>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="entity in filteredEntities"
|
v-for="entity in filteredEntities"
|
||||||
:key="entity.id"
|
:key="entity.identifier"
|
||||||
:value="entity.id"
|
:value="entity.identifier"
|
||||||
@click="$emit('select', entity)">
|
@click="$emit('select', entity)">
|
||||||
|
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import { useModuleStore } from '@KTXC/stores/moduleStore'
|
import { useModuleStore } from '@KTXC/stores/moduleStore'
|
||||||
import { useCollectionsStore } from '@PeopleManager/stores/collectionsStore'
|
import { usePeopleStore } from '@/stores/peopleStore'
|
||||||
import { useEntitiesStore } from '@PeopleManager/stores/entitiesStore'
|
|
||||||
import { ServiceObject } from '@PeopleManager/models/service';
|
|
||||||
import { CollectionObject } from '@PeopleManager/models/collection';
|
|
||||||
import { EntityObject } from '@PeopleManager/models/entity';
|
|
||||||
import CollectionList from '@/components/CollectionList.vue'
|
import CollectionList from '@/components/CollectionList.vue'
|
||||||
import CollectionEditor from '@/components/CollectionEditor.vue'
|
import CollectionEditor from '@/components/CollectionEditor.vue'
|
||||||
import PersonList from '@/components/PersonList.vue'
|
import PersonList from '@/components/PersonList.vue'
|
||||||
@@ -15,148 +12,50 @@ import PersonEditor from '@/components/PersonEditor.vue'
|
|||||||
// Vuetify display
|
// Vuetify display
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
|
|
||||||
// View state
|
|
||||||
const sidebarVisible = ref(true)
|
|
||||||
|
|
||||||
// Check if people manager is available
|
// Check if people manager is available
|
||||||
const moduleStore = useModuleStore()
|
const moduleStore = useModuleStore()
|
||||||
|
|
||||||
const isPeopleManagerAvailable = computed(() => {
|
const isPeopleManagerAvailable = computed(() => {
|
||||||
return moduleStore.has('people_manager') || moduleStore.has('PeopleManager')
|
return moduleStore.has('people_manager') || moduleStore.has('PeopleManager')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Local state for UI selections
|
// Store
|
||||||
const collectionsStore = useCollectionsStore()
|
const peopleStore = usePeopleStore()
|
||||||
const entitiesStore = useEntitiesStore()
|
|
||||||
const selectedCollection = ref<CollectionObject | null>(null)
|
|
||||||
const selectedEntity = ref<EntityObject | null>(null)
|
|
||||||
const entityEditorMode = ref<'edit' | 'view'>('view')
|
|
||||||
|
|
||||||
// Collection editor state
|
const {
|
||||||
const showCollectionEditor = ref(false)
|
sidebarVisible,
|
||||||
const editingCollection = ref<CollectionObject | null>(null)
|
loading,
|
||||||
const collectionEditorMode = ref<'create' | 'edit'>('create')
|
selectedCollection,
|
||||||
const CollectionListRef = ref<InstanceType<typeof CollectionList> | null>(null)
|
selectedEntity,
|
||||||
|
entityEditorMode,
|
||||||
|
showCollectionEditor,
|
||||||
|
editingCollection,
|
||||||
|
collectionEditorMode,
|
||||||
|
collections,
|
||||||
|
} = storeToRefs(peopleStore)
|
||||||
|
|
||||||
// Handlers
|
const {
|
||||||
const handleEditorEdit = () => {
|
initialize,
|
||||||
console.log('[People] - Editor editing started')
|
selectCollection,
|
||||||
entityEditorMode.value = 'edit'
|
openEditCollection,
|
||||||
}
|
openCreateCollection,
|
||||||
|
selectEntity,
|
||||||
|
createEntity,
|
||||||
|
startEditingEntity,
|
||||||
|
cancelEditingEntity,
|
||||||
|
closeEntityEditor,
|
||||||
|
saveEntity,
|
||||||
|
deleteEntity,
|
||||||
|
saveCollection,
|
||||||
|
deleteCollection,
|
||||||
|
} = peopleStore
|
||||||
|
|
||||||
const handleEditorCancel = () => {
|
onMounted(async () => {
|
||||||
console.log('[People] - Editor editing cancelled')
|
try {
|
||||||
entityEditorMode.value = 'view'
|
await initialize()
|
||||||
}
|
} catch (error) {
|
||||||
|
console.error('[People] - Failed to load data from PeopleManager:', error)
|
||||||
const handleEditorClose = () => {
|
|
||||||
console.log('[People] - Editor closed')
|
|
||||||
selectedEntity.value = null
|
|
||||||
entityEditorMode.value = 'view'
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollectionSelect = (collection: CollectionObject) => {
|
|
||||||
console.log('[People] - Collection selected', collection)
|
|
||||||
selectedCollection.value = collection ?? null
|
|
||||||
selectedEntity.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollectionEdit = (collection: CollectionObject) => {
|
|
||||||
console.log('[People] - Collection edit', collection)
|
|
||||||
editingCollection.value = collection
|
|
||||||
collectionEditorMode.value = 'edit'
|
|
||||||
showCollectionEditor.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onCollectionFresh = () => {
|
|
||||||
console.log('[People] - Collection new')
|
|
||||||
editingCollection.value = collectionsStore.fresh()
|
|
||||||
collectionEditorMode.value = 'create'
|
|
||||||
showCollectionEditor.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollectionSave = async (collection: CollectionObject, service: ServiceObject) => {
|
|
||||||
console.log('[People] - Collection save', collection)
|
|
||||||
if (collectionEditorMode.value === 'create') {
|
|
||||||
await handleCollectionCreate(collection, service)
|
|
||||||
} else {
|
|
||||||
await handleCollectionModify(collection)
|
|
||||||
}
|
}
|
||||||
await CollectionListRef.value?.refresh()
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollectionCreate = async (collection: CollectionObject, service: ServiceObject) => {
|
|
||||||
console.log('[People] - Collection created', collection)
|
|
||||||
const result = await collectionsStore.create(service, collection)
|
|
||||||
|
|
||||||
if (result instanceof CollectionObject) {
|
|
||||||
selectedCollection.value = result
|
|
||||||
} else {
|
|
||||||
selectedCollection.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedEntity.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollectionModify = async (collection: CollectionObject) => {
|
|
||||||
console.log('[People] - Collection modified', collection)
|
|
||||||
await collectionsStore.modify(collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollectionDelete = async (collection: CollectionObject) => {
|
|
||||||
console.log('[People] - Collection deleted', collection)
|
|
||||||
await collectionsStore.destroy(collection)
|
|
||||||
selectedCollection.value = null
|
|
||||||
selectedEntity.value = null
|
|
||||||
await CollectionListRef.value?.refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEntitySelect = (entity: EntityObject) => {
|
|
||||||
console.log('[People] - Entity selected', entity)
|
|
||||||
selectedEntity.value = entity
|
|
||||||
entityEditorMode.value = 'view'
|
|
||||||
}
|
|
||||||
|
|
||||||
const onEntityFresh = (type: string) => {
|
|
||||||
console.log('[People] - Entity create', type)
|
|
||||||
selectedEntity.value = entitiesStore.fresh(type)
|
|
||||||
entityEditorMode.value = 'edit'
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEntitySave = async (entity: EntityObject, collection?: CollectionObject | null) => {
|
|
||||||
console.log('[People] - Entity save', entity)
|
|
||||||
if (!(collection instanceof CollectionObject)) {
|
|
||||||
collection = selectedCollection.value
|
|
||||||
}
|
|
||||||
if (!collection) {
|
|
||||||
console.warn('[People] - Invalid collection object cannot save entity')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (entity.id === null) {
|
|
||||||
console.log('[People] - Entity create', entity)
|
|
||||||
entity.data.created = new Date();
|
|
||||||
selectedEntity.value = await entitiesStore.create(collection, entity)
|
|
||||||
} else if (entity.id !== null) {
|
|
||||||
console.log('[People] - Entity modify', entity)
|
|
||||||
entity.data.modified = new Date();
|
|
||||||
selectedEntity.value = await entitiesStore.modify(collection, entity)
|
|
||||||
}
|
|
||||||
entityEditorMode.value = 'view'
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEntityDelete = async (entity: EntityObject, collection?: CollectionObject | null) => {
|
|
||||||
console.log('[People] - Entity deleted', entity)
|
|
||||||
if (!(collection instanceof CollectionObject)) {
|
|
||||||
collection = selectedCollection.value
|
|
||||||
}
|
|
||||||
if (!collection) {
|
|
||||||
console.warn('[People] - Invalid collection object cannot delete entity')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await entitiesStore.destroy(collection, entity)
|
|
||||||
selectedEntity.value = null
|
|
||||||
entityEditorMode.value = 'view'
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -191,10 +90,11 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
>
|
>
|
||||||
<div class="pa-4 d-flex flex-column h-100">
|
<div class="pa-4 d-flex flex-column h-100">
|
||||||
<CollectionList
|
<CollectionList
|
||||||
ref="CollectionListRef"
|
:collections="collections"
|
||||||
|
:loading="loading"
|
||||||
:selected-collection="selectedCollection"
|
:selected-collection="selectedCollection"
|
||||||
@select="handleCollectionSelect"
|
@select="selectCollection"
|
||||||
@edit="handleCollectionEdit"
|
@edit="openEditCollection"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-divider class="my-2" />
|
<v-divider class="my-2" />
|
||||||
@@ -211,7 +111,7 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
block
|
block
|
||||||
class="justify-start"
|
class="justify-start"
|
||||||
:disabled="!selectedCollection"
|
:disabled="!selectedCollection"
|
||||||
@click="onEntityFresh('individual')"
|
@click="createEntity('individual')"
|
||||||
>
|
>
|
||||||
Individual
|
Individual
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -222,7 +122,7 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
block
|
block
|
||||||
class="justify-start"
|
class="justify-start"
|
||||||
:disabled="!selectedCollection"
|
:disabled="!selectedCollection"
|
||||||
@click="onEntityFresh('organization')"
|
@click="createEntity('organization')"
|
||||||
>
|
>
|
||||||
Organization
|
Organization
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -233,7 +133,7 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
block
|
block
|
||||||
class="justify-start"
|
class="justify-start"
|
||||||
:disabled="!selectedCollection"
|
:disabled="!selectedCollection"
|
||||||
@click="onEntityFresh('group')"
|
@click="createEntity('group')"
|
||||||
>
|
>
|
||||||
Group
|
Group
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -243,7 +143,7 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
size="small"
|
size="small"
|
||||||
block
|
block
|
||||||
class="justify-start"
|
class="justify-start"
|
||||||
@click="onCollectionFresh"
|
@click="openCreateCollection"
|
||||||
>
|
>
|
||||||
Address Book
|
Address Book
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -281,8 +181,8 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
<PersonList
|
<PersonList
|
||||||
:selected-collection="selectedCollection"
|
:selected-collection="selectedCollection"
|
||||||
:selected-entity="selectedEntity"
|
:selected-entity="selectedEntity"
|
||||||
@select="handleEntitySelect"
|
@select="selectEntity"
|
||||||
@fresh="onEntityFresh"
|
@fresh="createEntity"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="people-editor-panel">
|
<div class="people-editor-panel">
|
||||||
@@ -290,11 +190,11 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
:mode="entityEditorMode"
|
:mode="entityEditorMode"
|
||||||
:selected-collection="selectedCollection"
|
:selected-collection="selectedCollection"
|
||||||
:selected-entity="selectedEntity"
|
:selected-entity="selectedEntity"
|
||||||
@save="handleEntitySave"
|
@save="saveEntity"
|
||||||
@delete="handleEntityDelete"
|
@delete="deleteEntity"
|
||||||
@edit="handleEditorEdit"
|
@edit="startEditingEntity"
|
||||||
@cancel="handleEditorCancel"
|
@cancel="cancelEditingEntity"
|
||||||
@close="handleEditorClose"
|
@close="closeEntityEditor"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -306,8 +206,8 @@ const handleEntityDelete = async (entity: EntityObject, collection?: CollectionO
|
|||||||
v-model="showCollectionEditor"
|
v-model="showCollectionEditor"
|
||||||
:collection="editingCollection"
|
:collection="editingCollection"
|
||||||
:mode="collectionEditorMode"
|
:mode="collectionEditorMode"
|
||||||
@save="handleCollectionSave"
|
@save="saveCollection"
|
||||||
@delete="handleCollectionDelete"
|
@delete="deleteCollection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
227
src/stores/peopleStore.ts
Normal file
227
src/stores/peopleStore.ts
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
import { computed, ref, shallowRef } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { CollectionObject } from '@PeopleManager/models/collection'
|
||||||
|
import { EntityObject } from '@PeopleManager/models/entity'
|
||||||
|
import { IndividualObject } from '@PeopleManager/models/individual'
|
||||||
|
import { OrganizationObject } from '@PeopleManager/models/organization'
|
||||||
|
import { GroupObject } from '@PeopleManager/models/group'
|
||||||
|
import type { ServiceObject } from '@PeopleManager/models/service'
|
||||||
|
import { useCollectionsStore } from '@PeopleManager/stores/collectionsStore'
|
||||||
|
import { useEntitiesStore } from '@PeopleManager/stores/entitiesStore'
|
||||||
|
|
||||||
|
export const usePeopleStore = defineStore('peopleStore', () => {
|
||||||
|
const collectionsStore = useCollectionsStore()
|
||||||
|
const entitiesStore = useEntitiesStore()
|
||||||
|
|
||||||
|
// UI state
|
||||||
|
const sidebarVisible = ref(true)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// Selection state
|
||||||
|
const selectedCollection = shallowRef<CollectionObject | null>(null)
|
||||||
|
const selectedEntity = shallowRef<EntityObject | null>(null)
|
||||||
|
|
||||||
|
// Entity editor state
|
||||||
|
const entityEditorMode = ref<'edit' | 'view'>('view')
|
||||||
|
|
||||||
|
// Collection editor state
|
||||||
|
const showCollectionEditor = ref(false)
|
||||||
|
const editingCollection = shallowRef<CollectionObject | null>(null)
|
||||||
|
const collectionEditorMode = ref<'create' | 'edit'>('create')
|
||||||
|
|
||||||
|
// Derived state
|
||||||
|
const collections = computed(() => collectionsStore.collections)
|
||||||
|
const entities = computed(() => entitiesStore.entities)
|
||||||
|
|
||||||
|
// --- Collection actions ---
|
||||||
|
|
||||||
|
function selectCollection(collection: CollectionObject) {
|
||||||
|
selectedCollection.value = collection
|
||||||
|
selectedEntity.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditCollection(collection: CollectionObject) {
|
||||||
|
editingCollection.value = collection
|
||||||
|
collectionEditorMode.value = 'edit'
|
||||||
|
showCollectionEditor.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreateCollection() {
|
||||||
|
editingCollection.value = new CollectionObject()
|
||||||
|
collectionEditorMode.value = 'create'
|
||||||
|
showCollectionEditor.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCollection(collection: CollectionObject, service: ServiceObject) {
|
||||||
|
if (collectionEditorMode.value === 'create') {
|
||||||
|
const created = await collectionsStore.create(
|
||||||
|
service.provider,
|
||||||
|
service.identifier || '',
|
||||||
|
null,
|
||||||
|
collection.properties,
|
||||||
|
)
|
||||||
|
selectedCollection.value = created
|
||||||
|
} else {
|
||||||
|
await collectionsStore.update(
|
||||||
|
collection.provider,
|
||||||
|
collection.service,
|
||||||
|
collection.identifier,
|
||||||
|
collection.properties,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
showCollectionEditor.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCollection(collection: CollectionObject) {
|
||||||
|
await collectionsStore.delete(collection.provider, collection.service, collection.identifier)
|
||||||
|
|
||||||
|
if (selectedCollection.value?.identifier === collection.identifier) {
|
||||||
|
selectedCollection.value = null
|
||||||
|
selectedEntity.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
showCollectionEditor.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Entity actions ---
|
||||||
|
|
||||||
|
function createEntity(type: string) {
|
||||||
|
const entity = new EntityObject()
|
||||||
|
|
||||||
|
if (type === 'organization') {
|
||||||
|
entity.properties = new OrganizationObject()
|
||||||
|
} else if (type === 'group') {
|
||||||
|
entity.properties = new GroupObject()
|
||||||
|
} else {
|
||||||
|
entity.properties = new IndividualObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedEntity.value = entity
|
||||||
|
entityEditorMode.value = 'edit'
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectEntity(entity: EntityObject) {
|
||||||
|
selectedEntity.value = entity
|
||||||
|
entityEditorMode.value = 'view'
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEditingEntity() {
|
||||||
|
entityEditorMode.value = 'edit'
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEditingEntity() {
|
||||||
|
entityEditorMode.value = 'view'
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEntityEditor() {
|
||||||
|
selectedEntity.value = null
|
||||||
|
entityEditorMode.value = 'view'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveEntity(entity: EntityObject, collection?: CollectionObject | null) {
|
||||||
|
const targetCollection = collection instanceof CollectionObject
|
||||||
|
? collection
|
||||||
|
: selectedCollection.value
|
||||||
|
|
||||||
|
if (!targetCollection) {
|
||||||
|
throw new Error('[People] - No collection selected, cannot save entity')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.identifier) {
|
||||||
|
// Create
|
||||||
|
entity.properties.created = new Date()
|
||||||
|
selectedEntity.value = await entitiesStore.create(
|
||||||
|
targetCollection.provider,
|
||||||
|
targetCollection.service,
|
||||||
|
targetCollection.identifier,
|
||||||
|
entity.properties,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Update
|
||||||
|
entity.properties.modified = new Date()
|
||||||
|
selectedEntity.value = await entitiesStore.update(
|
||||||
|
targetCollection.provider,
|
||||||
|
targetCollection.service,
|
||||||
|
targetCollection.identifier,
|
||||||
|
entity.identifier,
|
||||||
|
entity.properties,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedCollection.value = targetCollection
|
||||||
|
entityEditorMode.value = 'view'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteEntity(entity: EntityObject, collection?: CollectionObject | null) {
|
||||||
|
const targetCollection = collection instanceof CollectionObject
|
||||||
|
? collection
|
||||||
|
: selectedCollection.value
|
||||||
|
|
||||||
|
if (!targetCollection) {
|
||||||
|
throw new Error('[People] - No collection selected, cannot delete entity')
|
||||||
|
}
|
||||||
|
|
||||||
|
await entitiesStore.delete(
|
||||||
|
targetCollection.provider,
|
||||||
|
targetCollection.service,
|
||||||
|
targetCollection.identifier,
|
||||||
|
entity.identifier,
|
||||||
|
)
|
||||||
|
|
||||||
|
closeEntityEditor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Lifecycle ---
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await collectionsStore.list()
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectedCollection.value
|
||||||
|
&& !collections.value.find(c => c.identifier === selectedCollection.value?.identifier)
|
||||||
|
) {
|
||||||
|
selectedCollection.value = null
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// State
|
||||||
|
sidebarVisible,
|
||||||
|
loading,
|
||||||
|
selectedCollection,
|
||||||
|
selectedEntity,
|
||||||
|
entityEditorMode,
|
||||||
|
showCollectionEditor,
|
||||||
|
editingCollection,
|
||||||
|
collectionEditorMode,
|
||||||
|
|
||||||
|
// Derived
|
||||||
|
collections,
|
||||||
|
entities,
|
||||||
|
|
||||||
|
// Collection actions
|
||||||
|
selectCollection,
|
||||||
|
openEditCollection,
|
||||||
|
openCreateCollection,
|
||||||
|
saveCollection,
|
||||||
|
deleteCollection,
|
||||||
|
|
||||||
|
// Entity actions
|
||||||
|
createEntity,
|
||||||
|
selectEntity,
|
||||||
|
startEditingEntity,
|
||||||
|
cancelEditingEntity,
|
||||||
|
closeEntityEditor,
|
||||||
|
saveEntity,
|
||||||
|
deleteEntity,
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
initialize,
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user