From 0811d9ccd8a7efc975d1f8def3df43075d7bfb27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Ml=C3=A1dek?= Date: Sun, 28 Jan 2024 19:27:01 +0100 Subject: [PATCH] feat(webui): required & optional attributes TODO: honor distinction in EntryLists as well --- webui/src/lib/components/Inspect.svelte | 31 +++- .../lib/components/InspectTypeEditor.svelte | 137 +++++++++++++----- 2 files changed, 126 insertions(+), 42 deletions(-) diff --git a/webui/src/lib/components/Inspect.svelte b/webui/src/lib/components/Inspect.svelte index b66bb46..7884d84 100644 --- a/webui/src/lib/components/Inspect.svelte +++ b/webui/src/lib/components/Inspect.svelte @@ -182,19 +182,38 @@ } async function fetchCorrectlyTagged() { - const attributes = ( + if (!$entity?.attr[`~${ATTR_OF}`]?.length) { + return; + } + + const allAttributes = ( await Promise.all( - ($entity?.attr[`~${ATTR_OF}`] ?? []).map((e) => api.addressToComponents(e.entity)) + ($entity?.attr[`~${ATTR_OF}`] ?? []).map(async (e) => { + return { address: e.entity, components: await api.addressToComponents(e.entity) }; + }) ) ) - .filter((ac) => ac.t == 'Attribute') - .map((ac) => ac.c) + .filter((ac) => ac.components.t == 'Attribute') .filter(isDefined); + const attributeRequiredQuery = await api.query( + Query.matches( + allAttributes.map((ac) => `@${ac.address}`), + 'TYPE_REQUIRED', + Any + ) + ); + + const requiredAttributes = allAttributes + .filter((ac) => { + return attributeRequiredQuery.getObject(ac.address).attr['TYPE_REQUIRED']?.length; + }) + .map((ac) => ac.components.c as string); + const attributeQuery = await api.query( Query.matches( tagged.map((t) => `@${t.entity}`), - attributes, + requiredAttributes, Any ) ); @@ -204,7 +223,7 @@ for (const element of tagged) { const entity = attributeQuery.getObject(element.entity); - if (attributes.every((attr) => entity.attr[attr])) { + if (requiredAttributes.every((attr) => entity.attr[attr])) { correctlyTagged = [...correctlyTagged, element.entity]; } else { incorrectlyTagged = [...incorrectlyTagged, element.entity]; diff --git a/webui/src/lib/components/InspectTypeEditor.svelte b/webui/src/lib/components/InspectTypeEditor.svelte index 4479f25..e99b55b 100644 --- a/webui/src/lib/components/InspectTypeEditor.svelte +++ b/webui/src/lib/components/InspectTypeEditor.svelte @@ -4,7 +4,8 @@ import IconButton from './utils/IconButton.svelte'; import api from '$lib/api'; import { i18n } from '../i18n'; - import type { UpObject, UpEntry } from '@upnd/upend'; + import { type UpObject, type UpEntry, Query } from '@upnd/upend'; + import { Any } from '@upnd/upend/query'; import type { Readable } from 'svelte/store'; import { ATTR_OF } from '@upnd/upend/constants'; import { createEventDispatcher } from 'svelte'; @@ -18,7 +19,33 @@ $: if (adding && typeSelector) typeSelector.focus(); - $: typeEntries = $entity?.attr[`~${ATTR_OF}`] || []; + let types: Array<{ address: string; entry: UpEntry; required: UpEntry | undefined }> = []; + $: updateTypes($entity?.attr[`~${ATTR_OF}`] || []); + async function updateTypes(entries: UpEntry[]) { + types = []; + const query = await api.query( + Query.matches( + entries.flatMap((e) => [`@${e.address}`, `@${e.entity}`]), + Any, + Any + ) + ); + + types = entries + .map((entry) => ({ + address: entry.entity, + entry, + required: query.getObject(entry.address)?.attr['TYPE_REQUIRED']?.[0] + })) + .sort((a, b) => { + const aLabel = query.getObject(a.address)?.identify().join('/'); + const bLabel = query.getObject(b.address)?.identify().join('/'); + if (aLabel && bLabel) { + return aLabel.localeCompare(bLabel); + } + return a.address.localeCompare(b.address); + }); + } async function add(ev: CustomEvent) { if (!$entity || ev.detail.t !== 'Attribute') { @@ -51,10 +78,26 @@ dispatch('change'); } } + + async function setRequired(entry: UpEntry, required: boolean) { + if (required) { + await api.putEntry({ + entity: entry.address, + attribute: 'TYPE_REQUIRED', + value: { t: 'Null', c: null } + }); + } else { + const requiredAddress = types.find((t) => t.entry === entry)?.required?.address; + if (requiredAddress) { + await api.deleteEntry(requiredAddress); + } + } + dispatch('change'); + } -{#if typeEntries.length || $entity?.attr['~IN']?.length} - +{#if types.length || $entity?.attr['~IN']?.length} + {$i18n.t('Type Attributes')} {#if adding}
@@ -70,41 +113,34 @@
{/if}
-
    - {#each typeEntries as typeEntry} -
  • -
    - -
    -
    - remove(typeEntry)} /> -
    -
  • +
    + {#each types as type} +
    + +
    + +
    + remove(type.entry)} /> +
    {:else} -
  • +
    {$i18n.t('No attributes assigned to this type.')} -
  • +
    {/each} -
-
- (adding = true)} /> +
+ (adding = true)} /> +
{/if}