forked from enviPath/enviPy
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>
186 lines
5.7 KiB
HTML
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>
|