Skip to content

Commit f15d148

Browse files
committed
Fix edit metadata flow
1 parent 3216112 commit f15d148

File tree

4 files changed

+38
-29
lines changed

4 files changed

+38
-29
lines changed

frontend/components/entities/EntityList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<!-- Entity cards -->
1212
<UCard
1313
v-for="(entity, index) in entities"
14-
:key="getEntityId(entity)"
14+
:key="`${getEntityId(entity)}-${entity.updated_at || ''}`"
1515
:data-entity-card="index"
1616
:ui="{ body: 'sm:p-1.5' }"
1717
class="overflow-hidden select-text cursor-pointer root-p-0"

frontend/components/entities/EntityMetadata.vue

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div
3-
v-memo="[props.entityId, Object.keys(props.metadata).length, editState?.isEditing, lockStatesForTemplate]"
3+
v-memo="[props.entityId, JSON.stringify(props.metadata), editState?.isEditing, lockStatesForTemplate]"
44
class="rounded border-t bg-white"
55
style="border-color: var(--theme-light-border-primary)"
66
>
@@ -35,8 +35,8 @@
3535
props.editState?.isSaving
3636
? "Saving..."
3737
: isAuthenticated
38-
? "Save changes"
39-
: "Login required"
38+
? "Save changes"
39+
: "Login required"
4040
}}
4141
</UButton>
4242
<UButton
@@ -59,6 +59,8 @@
5959
resize
6060
class="font-mono text-sm w-full"
6161
autofocus
62+
@keydown.stop
63+
@keyup.stop
6264
/>
6365
<div class="flex items-center justify-between mt-3 text-xs">
6466
<span>{{ getJsonStats(localEditJson) }}</span>
@@ -280,8 +282,8 @@
280282
pendingLockChanges.has(field.key)
281283
? 'Updating lock state...'
282284
: isFieldLocked(field.key)
283-
? `Unlock ${field.displayName}`
284-
: `Lock ${field.displayName}`
285+
? `Unlock ${field.displayName}`
286+
: `Lock ${field.displayName}`
285287
"
286288
:popper="{ placement: 'top' }"
287289
>
@@ -290,8 +292,8 @@
290292
pendingLockChanges.has(field.key)
291293
? 'i-heroicons-arrow-path'
292294
: isFieldLocked(field.key)
293-
? 'i-heroicons-lock-closed'
294-
: 'i-heroicons-lock-open'
295+
? 'i-heroicons-lock-closed'
296+
: 'i-heroicons-lock-open'
295297
"
296298
color="primary"
297299
variant="ghost"
@@ -414,7 +416,7 @@ const pendingLockChanges = ref<Set<string>>(new Set());
414416
// Watch for changes to editState prop and update local state
415417
watch(
416418
() => props.editState?.json,
417-
(newJson) => {
419+
(newJson: string | undefined) => {
418420
if (newJson !== undefined) {
419421
localEditJson.value = newJson;
420422
}
@@ -714,10 +716,10 @@ const getGridSpanClass = (key: string, value: unknown, type: string): string =>
714716
return contentLength <= 30
715717
? "col-span-3"
716718
: contentLength <= 50
717-
? "col-span-4"
718-
: contentLength <= 70
719-
? "col-span-6"
720-
: "col-span-12";
719+
? "col-span-4"
720+
: contentLength <= 70
721+
? "col-span-6"
722+
: "col-span-12";
721723
} else {
722724
// Strings - be more aggressive with space
723725
if (contentLength <= 15) return "col-span-2";
@@ -997,7 +999,7 @@ const getUnifiedGridSpanClass = (field: UnifiedField): string => {
997999
if (field.category === "special") {
9981000
return getSpecialFieldSpanClass(
9991001
field.value,
1000-
getAllFieldsComputed.value.filter((f) => f.category === "status").length,
1002+
getAllFieldsComputed.value.filter((f: UnifiedField) => f.category === "status").length,
10011003
);
10021004
} else if (field.category === "status") {
10031005
return getStatusFieldSpanClass(field.value);

frontend/composables/entities/useEntitySearch.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export function useEntitySearch() {
120120
// Create a map to deduplicate fields based on their labels
121121
const fieldMap = new Map<string, { label: string; value: string }>();
122122

123-
sortState.availableFields.forEach((field) => {
123+
sortState.availableFields.forEach((field: string) => {
124124
// Use the original field name as the label (without normalization)
125125
let label: string;
126126
if (field.startsWith("metadata.")) {
@@ -458,8 +458,10 @@ export function useEntitySearch() {
458458

459459
const updateEntity = (index: number, entity: Entity): void => {
460460
if (index >= 0 && index < entities.value.length) {
461-
// Use splice to ensure Vue reactivity is triggered
462-
entities.value.splice(index, 1, { ...entity });
461+
// Create a new array to trigger shallowRef reactivity
462+
const newEntities = [...entities.value];
463+
newEntities[index] = { ...entity };
464+
entities.value = newEntities;
463465
}
464466
};
465467

@@ -486,7 +488,7 @@ export function useEntitySearch() {
486488
// Watch preferences and sync to local state
487489
watch(
488490
() => searchPreferences.sortBy.value,
489-
(newSortBy) => {
491+
(newSortBy: string) => {
490492
if (sortState.sortBy !== newSortBy) {
491493
sortState.sortBy = newSortBy;
492494
sortState.field = newSortBy;
@@ -497,7 +499,7 @@ export function useEntitySearch() {
497499

498500
watch(
499501
() => searchPreferences.sortOrder.value,
500-
(newSortOrder) => {
502+
(newSortOrder: "asc" | "desc") => {
501503
if (sortState.sortOrder !== newSortOrder) {
502504
sortState.sortOrder = newSortOrder;
503505
sortState.order = newSortOrder;
@@ -508,7 +510,7 @@ export function useEntitySearch() {
508510

509511
watch(
510512
() => searchPreferences.pageSize.value,
511-
(newPageSize) => {
513+
(newPageSize: number) => {
512514
if (scrollState.pageSize !== newPageSize) {
513515
scrollState.pageSize = newPageSize;
514516
}
@@ -519,7 +521,7 @@ export function useEntitySearch() {
519521
// Watch route query parameter to sync search query
520522
watch(
521523
() => route.query.q,
522-
(newQuery) => {
524+
(newQuery: string | (string | null)[] | null | undefined) => {
523525
const queryString = (newQuery as string) || "";
524526
if (userSearchQuery.value !== queryString) {
525527
userSearchQuery.value = queryString;

frontend/composables/entities/useEntitySelection.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export function useEntitySelection() {
171171
* Download selected entities as JSON file
172172
*/
173173
async function downloadSelectedEntities(): Promise<void> {
174-
const selectedEntityIds = Array.from(selectionState.selectedEntities);
174+
const selectedEntityIds = Array.from(selectionState.selectedEntities) as number[];
175175
if (selectedEntityIds.length === 0) {
176176
return;
177177
}
@@ -394,25 +394,30 @@ export function useEntitySelection() {
394394
// Use the edited JSON if provided, otherwise fall back to the edit state JSON
395395
const jsonToSave = editedJson || editState.editedJson;
396396
const parsedMetadata = JSON.parse(jsonToSave);
397+
// Wrap metadata in the format expected by the backend
398+
const payload = { entity_id: entityId, metadata: parsedMetadata };
397399

398400
// Call the backend API to save metadata with cookie-based authentication
399401
const { updateEntity } = useApiClient();
400-
await updateEntity(entityId, parsedMetadata);
402+
const updatedEntity = await updateEntity(entityId, payload);
401403

402404
toast.add({
403405
title: "Success",
404406
description: "Entity metadata updated successfully.",
405407
color: "success",
406408
});
407409

408-
// Update the entity in the local state
410+
// Update the entity in the local state using server response
409411
const entityIndex = entities.findIndex((entity: Entity) => getPrimaryKeyValue(entity) === entityId);
410412
if (entityIndex !== -1) {
411-
updateEntityInArray(entityIndex, {
413+
// Use server's metadata if provided, otherwise use the metadata we sent
414+
// (server may return flattened data without a 'metadata' key)
415+
const mergedEntity = {
412416
...entities[entityIndex],
413-
entity_id: entityId,
414-
metadata: parsedMetadata,
415-
});
417+
...updatedEntity,
418+
metadata: updatedEntity.metadata || parsedMetadata,
419+
};
420+
updateEntityInArray(entityIndex, mergedEntity);
416421
}
417422

418423
// Exit edit mode
@@ -485,7 +490,7 @@ export function useEntitySelection() {
485490
* Delete selected entities with confirmation
486491
*/
487492
async function deleteSelectedEntities(): Promise<void> {
488-
const selectedEntityIds = Array.from(selectionState.selectedEntities);
493+
const selectedEntityIds = Array.from(selectionState.selectedEntities) as number[];
489494
if (selectedEntityIds.length === 0) {
490495
return;
491496
}

0 commit comments

Comments
 (0)