Merge branch 'feature-78139/CIS4_Dashboard_ausgeblendete_Widgets'

This commit is contained in:
Harald Bamberger
2026-08-18 17:51:40 +02:00
5 changed files with 111 additions and 73 deletions
+49 -20
View File
@@ -62,6 +62,9 @@ export default {
return [type, result];
}));
},
hiddenWidgets() {
return this.widgets.filter(widget => widget.hidden);
}
},
methods: {
@@ -69,6 +72,9 @@ export default {
this.$refs.widgetpicker
.getWidget()
.then(widget_id => {
if (widget_id.hidden)
this.widgetUnhide(widget_id, widget.place);
widget.widget = widget_id;
// NOTE(chris): min size
widget.place = Object.fromEntries(Object.entries(widget.place).map(([key, value]) => {
@@ -131,17 +137,6 @@ export default {
this.widgets.forEach((widget, i) => {
if (failed.includes(widget.id)) {
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
/** NOTE(chris): if you wanna hide or unhide a
* preset and it fails: switch around the hidden
* value to revert it properly (checkboxes can't
* really handle it otherwise)
*/
if (payload[widget.id].hidden !== undefined) {
this.widgets[i].hidden = payload[widget.id].hidden;
this.$nextTick(() => {
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
});
}
} else if (payload[widget.id]) {
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index;
@@ -160,6 +155,31 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
widgetUnhide(orig, place) {
place = Object.fromEntries(Object.entries(place).map(([key, value]) => {
value.w = this.sizeLimits[orig.widget].width.min;
value.h = this.sizeLimits[orig.widget].height.min;
// NOTE(chris): unpin pinned widgets because they got a new placement
value.pinned = false;
return [key, value];
}));
// NOTE(chris): clear placement in other screensizes
Object.keys(orig.place).forEach(key => {
if (!place[key]) {
place[key] = {
x: undefined,
y: undefined,
pinned: false
};
}
});
let payload = {};
payload[orig.id] = {
hidden: false,
place
};
return this.widgetUpdate(payload);
},
async fetchViewData() {
let viewDataResult = await this.$api.call(ApiDashboard.getViewData());
const viewData = viewDataResult.data;
@@ -204,15 +224,23 @@ export default {
},
template: /* html */`
<div class="core-dashboard">
<h3>
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
<button
class="btn ms-2"
aria-label="edit dashboard"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/edit') }"
@click="editMode = !editMode"
><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
</h3>
<header class="d-flex justify-content-between align-items-baseline">
<h3>
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
</h3>
<div class="form-check form-check-reverse form-switch">
<label class="form-check-label" for="switchEditMode">
{{ $p.t('dashboard/edit') }}
</label>
<input
class="form-check-input"
type="checkbox"
role="switch"
id="switchEditMode"
v-model="editMode"
>
</div>
</header>
<dashboard-section
name="general"
:widgets="widgets"
@@ -223,6 +251,7 @@ export default {
<dashboard-widget-picker
ref="widgetpicker"
:widgets="widgetsSetup"
:hidden-widgets="hiddenWidgets"
></dashboard-widget-picker>
</div>`
}
+2 -26
View File
@@ -36,7 +36,6 @@ export default {
"width",
"height",
"custom",
"hidden",
"editMode",
"loading", // widget got added and is waiting for backend to save in db
"item_data",
@@ -115,14 +114,6 @@ export default {
},
ready() {
return this.component && this.arguments !== null;
},
visible: {
get() {
return !this.hidden;
},
set(value) {
this.$emit('remove', this.hidden);
}
}
},
methods: {
@@ -202,10 +193,8 @@ export default {
},
template: /*html*/ `
<article
v-if="!hidden || editMode"
class="dashboard-item card overflow-hidden h-100 position-relative"
:class="{
'hidden-widget': hidden,
[arguments?.className]: arguments && arguments.className
}"
>
@@ -299,9 +288,9 @@ export default {
>
<i class="fa-solid fa-gear" aria-hidden="true"></i>
</a>
<!-- delete button -->
<!-- delete/hide button -->
<a
v-if="custom && editMode"
v-if="editMode"
href="#"
class="col-auto px-1"
:aria-label="$p.t('dashboard/widget_delete')"
@@ -310,19 +299,6 @@ export default {
>
<i class="fa-solid fa-trash" aria-hidden="true"></i>
</a>
<!-- hide button -->
<Transition>
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
<input
type="checkbox"
role="switch"
v-model="visible"
class="form-check-input ms-0"
:value="true"
:aria-label="$p.t('dashboard/widget_toggle_visibility')"
>
</div>
</Transition>
</header>
<!-- TODO Manu rename/remove-->
<div v-if="ready" class="card-body overflow-hidden p-0">
+3 -16
View File
@@ -124,8 +124,6 @@ export default {
return { ...item, ...placement, weight };
});
if (this.editModeIsActive)
return placedItems;
return placedItems.filter(item => !item.hidden);
}
},
@@ -141,22 +139,12 @@ export default {
handleConfigClosed() {
this.configOpened = false
},
removeWidget(item, revert) {
removeWidget(item) {
if (item.custom) {
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', item.id, this.name));
} else {
let update = {};
update[item.id] = { hidden: !revert };
if (!revert) {
// NOTE(chris): move to last line
update[item.id].place = [];
let y = this.gridHeight;
if (this.additionalRow)
y--;
update[item.id].place[this.gridWidth] = { x: 0, y };
}
update[item.id] = { hidden: true };
this.updatePreset(update);
}
},
@@ -251,13 +239,12 @@ export default {
:loading="item.loading"
:config="item.config"
:custom="item.custom"
:hidden="item.hidden"
:editMode="editModeIsActive"
:place="item.place[gridWidth]"
:widget-template="indexedWidgetsTemplates[item.widget]"
:source="adminMode ? null : item.source || 'custom'"
@change="saveConfig($event, item)"
@remove="removeWidget(item, $event)"
@remove="removeWidget(item)"
@config-opened="handleConfigOpened"
@config-closed="handleConfigClosed"
@pin-item="updatePositions"
+44 -11
View File
@@ -7,11 +7,21 @@ export default {
WidgetIcon,
},
props: [
"widgets"
"widgets",
"hiddenWidgets"
],
data: () => ({
callbacks: {}
}),
computed: {
hasAnyWidgets() {
if (!this.widgets)
return false;
if (!this.widgets.length && !this.hiddenWidgets?.length)
return false;
return true;
}
},
methods: {
getWidget() {
return new Promise((resolve,reject) => {
@@ -37,23 +47,46 @@ export default {
<bs-modal
ref="modal"
class="fade"
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0 }"
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': hasAnyWidgets }"
@hiddenBsModal="close"
>
<template v-slot:title>{{ $p.t('dashboard/createWidget') }}</template>
<template v-slot:default>
<div v-if="widgets" class="row g-2">
<div v-if="!widgets.length">
{{ $p.t('dashboard/noWidgetsAvailable') }}
</div>
<template v-if="widgets && hiddenWidgets">
<div
v-for="widget in widgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
v-if="!widgets.length && !hiddenWidgets.length"
class="row g-2"
>
<widget-icon @select="pick" :widget="widget"></widget-icon>
<div>{{ $p.t('dashboard/noWidgetsAvailable') }}</div>
</div>
</div>
<template v-else>
<div
v-if="hiddenWidgets.length"
class="row g-2"
:class="widgets.length ? 'border-bottom pb-3 mb-3' : ''"
>
<div
v-for="widget in hiddenWidgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
>
<widget-icon @select="pick(widget)" :widget="widgets.find(w => w.widget_id == widget.widget)"></widget-icon>
</div>
</div>
<div
v-if="widgets.length"
class="row g-2"
>
<div
v-for="widget in widgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
>
<widget-icon @select="pick" :widget="widget"></widget-icon>
</div>
</div>
</template>
</template>
<div v-else class="text-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
+13
View File
@@ -233,6 +233,14 @@ export default {
if (value == oldValue)
return;
/** NOTE(chris): tempPositionUpdates depends on the cols not to be
* updated. So if they do we reset to get a clean value. In case the
* drag and drop operation is still motion on the next tick this
* will be correct again and if there is no such operation we clean
* up.
*/
this.tempPositionUpdates = null;
this.reinitGrid();
},
rows: {
@@ -246,6 +254,11 @@ export default {
},
indexedItems: {
handler(value) {
/** NOTE(chris): there was an update of the items, so in case
* it came from a drag and drop operation we need to clean up
*/
this.tempPositionUpdates = null;
this.reinitGrid();
},
immediate: true,