Files
enviPy-bayer/templates/modals/objects/update_scenario_additional_information_modal.html
Tobias O dc18b73e08 [Feature] Adds timeseries display (#313)
Adds a way to input/display timeseries data to the additional information

Reviewed-on: enviPath/enviPy#313
Reviewed-by: jebus <lorsbach@envipath.com>
Co-authored-by: Tobias O <tobias.olenyi@envipath.com>
Co-committed-by: Tobias O <tobias.olenyi@envipath.com>
2026-02-04 01:01:06 +13:00

186 lines
5.7 KiB
HTML

{% load static %}
<dialog
id="update_scenario_additional_information_modal"
class="modal"
x-data="{
isSubmitting: false,
items: [],
schemas: {},
loading: false,
error: null,
originalItems: [], // Store original data to detect changes
modifiedUuids: new Set(), // Track which items were modified
async init() {
try {
this.loading = true;
const scenarioUuid = '{{ scenario.uuid }}';
const { items, schemas } =
await window.AdditionalInformationApi.loadSchemasAndItems(scenarioUuid);
this.schemas = schemas;
// Store deep copy of original items for comparison
this.originalItems = JSON.parse(JSON.stringify(items));
this.items = items;
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
reset() {
this.isSubmitting = false;
this.error = null;
this.modifiedUuids.clear();
Alpine.store('validationErrors').clearErrors(); // Clear all contexts
},
updateItemData(uuid, data) {
// Update the item's data in the items array
const item = this.items.find(i => i.uuid === uuid);
if (item) {
item.data = data;
// Mark this item as modified
this.modifiedUuids.add(uuid);
}
},
async submit() {
if (this.items.length === 0) {
this.error = 'No data to update';
return;
}
// Filter to only items that were actually modified
const modifiedItems = this.items.filter(item => this.modifiedUuids.has(item.uuid));
if (modifiedItems.length === 0) {
this.error = 'No changes to save';
return;
}
this.isSubmitting = true;
this.error = null;
try {
const scenarioUuid = '{{ scenario.uuid }}';
// Use the unified API client for sequential, safe updates - only modified items
await window.AdditionalInformationApi.updateItems(scenarioUuid, modifiedItems);
// Close modal and reload page
document.getElementById('update_scenario_additional_information_modal').close();
window.location.reload();
} catch (err) {
// Handle validation errors with field-level details
if (err.isValidationError && err.fieldErrors) {
this.error = err.message || 'Please correct the errors in the form';
// Backend returns errors keyed by UUID, each with field-level error arrays
// Set errors for each item with its UUID as context
Object.entries(err.fieldErrors).forEach(([uuid, fieldErrors]) => {
Alpine.store('validationErrors').setErrors(fieldErrors, uuid);
});
} else {
this.error = err.message || 'An error occurred. Please try again.';
}
} finally {
this.isSubmitting = false;
}
}
}"
@close="reset()"
@update-item-data.window="updateItemData($event.detail.uuid, $event.detail.data)"
>
<div class="modal-box max-h-[90vh] max-w-4xl overflow-y-auto">
<!-- Header -->
<h3 class="text-lg font-bold">Update Additional Information</h3>
<!-- Close button (X) -->
<form method="dialog">
<button
class="btn btn-sm btn-circle btn-ghost absolute top-2 right-2"
:disabled="isSubmitting"
>
</button>
</form>
<!-- Body -->
<div class="py-4">
<!-- Loading state -->
{% include "components/modals/loading_state.html" with loading_var="loading" %}
<!-- Error state -->
{% include "components/modals/error_state.html" with error_var="error" %}
<!-- Items list -->
<template x-if="!loading">
<div class="space-y-4">
<template x-if="items.length === 0">
<p class="text-base-content/60">
No additional information to update.
</p>
</template>
<template x-for="(item, index) in items" :key="item.uuid">
<div class="card bg-base-200 shadow-sm">
<div class="card-body p-4">
<div
x-data="schemaRenderer({
rjsf: schemas[item.type.toLowerCase()],
data: item.data,
mode: 'edit',
context: item.uuid // Pass item UUID as context for error scoping
})"
x-init="await init(); $watch('data', (value) => { $dispatch('update-item-data', { uuid: item.uuid, data: value }) }, { deep: true })"
>
{% include "components/schema_form.html" %}
</div>
</div>
</div>
</template>
</div>
</template>
</div>
<!-- Footer -->
<div class="modal-action">
<button
type="button"
class="btn"
onclick="this.closest('dialog').close()"
:disabled="isSubmitting"
>
Close
</button>
<button
type="button"
class="btn btn-primary"
@click="submit()"
:disabled="isSubmitting || loading || items.length === 0 || modifiedUuids.size === 0"
>
<span x-show="!isSubmitting">
<template x-if="modifiedUuids.size > 0">
<span x-text="`Update (${modifiedUuids.size})`"></span>
</template>
<template x-if="modifiedUuids.size === 0">
<span>No Changes</span>
</template>
</span>
<span
x-show="isSubmitting"
class="loading loading-spinner loading-sm"
></span>
<span x-show="isSubmitting">Updating...</span>
</button>
</div>
</div>
<!-- Backdrop -->
<form method="dialog" class="modal-backdrop">
<button :disabled="isSubmitting">close</button>
</form>
</dialog>