use PvAutocomplete instead Chips, start FilterTags

This commit is contained in:
ma0068
2025-11-05 11:09:16 +01:00
parent 707d00b280
commit df63b8c1b7
6 changed files with 336 additions and 74 deletions
+55 -10
View File
@@ -11,16 +11,18 @@ class Config extends Auth_Controller
{
parent::__construct(
array(
'index' => 'dashboard/benutzer:r',
'dummy' => 'dashboard/benutzer:r',
'genWidgetId' => 'dashboard/benutzer:rw',
'addWidgetsToPreset' => 'dashboard/admin:rw',
'removeWidgetFromPreset' => 'dashboard/admin:rw',
'addWidgetsToUserOverride' => 'dashboard/benutzer:rw',
'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw',
'funktionen' => 'dashboard/admin:r',
'preset' => 'dashboard/admin:r',
'presetBatch' => 'dashboard/admin:r'
'index' => 'dashboard/benutzer:r',
'dummy' => 'dashboard/benutzer:r',
'genWidgetId' => 'dashboard/benutzer:rw',
'addWidgetsToPreset' => 'dashboard/admin:rw',
'removeWidgetFromPreset' => 'dashboard/admin:rw',
'addWidgetsToUserOverride' => 'dashboard/benutzer:rw',
'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw',
'funktionen' => 'dashboard/admin:r',
'preset' => 'dashboard/admin:r',
'presetBatch' => 'dashboard/admin:r',
'insertAndUpdateTagsForBookmarksUser' => 'dashboard/benutzer:rw',
'getTagFilter' => 'dashboard/benutzer:rw',
)
);
@@ -213,4 +215,47 @@ class Config extends Auth_Controller
return $this->outputJsonSuccess($result);
}
//TODO(Manu) move to Bookmark controller
public function insertAndUpdateTagsForBookmarksUser()
{
$widgetId = $this->input->get('widget_id');
$funktion_kurzbz = $this->input->get('funktion_kurzbz');
$uid = $this->AuthLib->getAuthObj()->username;
$tags = $this->input->get('tags');
$this->load->model('dashboard/Dashboard_Override_model', 'Dashboard_OverrideModel');
$result = $this->Dashboard_OverrideModel->addTagFilter($widgetId, $uid, $funktion_kurzbz, $tags);
$this->outputJsonSuccess($result ?: []);
}
//TODO(Manu) move to Bookmark controller
public function getTagFilter()
{
$widgetId = $this->input->get('widget_id');
$funktion_kurzbz = $this->input->get('funktion_kurzbz');
$uid = $this->AuthLib->getAuthObj()->username;
//$dashboard_kurzbz = 1;
$this->load->model('dashboard/Dashboard_Override_model', 'Dashboard_OverrideModel');
/* $override = $this->DashboardLib->getOverride($dashboard_kurzbz, $uid);
if (empty($override)) {
http_response_code(404);
$this->terminateWithJsonError('userconfig for dashboard ' . $dashboard_kurzbz . ' not found.');
}
$override_decoded = json_decode($override->override, true);
return $this->outputJsonSuccess($override_decoded ?: []);*/
$result = $this->Dashboard_OverrideModel->getTagFilter($widgetId, $uid, $funktion_kurzbz);
$this->outputJsonSuccess($result ?: []);
}
}
@@ -23,4 +23,58 @@ class Dashboard_Override_model extends DB_Model
{
return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'uid'=> $uid));
}
/**
* Get Overrides of given uid and funktion_kurzbz and widget_id
* @param integer dashboard_id
* @param string $uid
* @return array
*/
//TODO(MANU) move to bookmark model
public function getTagFilter($widgetId, $uid, $funktion_kurzbz)
{
// $qry = "SELECT override -> ? -> 'widgets' -> ? -> 'tags' AS tags FROM dashboard.tbl_dashboard_benutzer_override WHERE uid = ?";
// $qry = " SELECT override -> '" . $funktion_kurzbz . "'-> 'widgets'-> '" . $widgetId . "'-> 'tags' AS tags FROM dashboard.tbl_dashboard_benutzer_override WHERE uid = '" . $uid . "';";
//unter config
$qry = " SELECT override -> '" . $funktion_kurzbz . "'-> 'widgets'-> '" . $widgetId . "'-> 'config' -> 'tags' AS tags FROM dashboard.tbl_dashboard_benutzer_override WHERE uid = '" . $uid . "';";
/* $qry = <<<SQL
SELECT
override -> ?
-> 'widgets'
-> ?
-> 'tags' AS tags
FROM dashboard.tbl_dashboard_benutzer_override
WHERE uid = ?
SQL;*/
//return $qry;
return $this->execQuery($qry);
// return $this->execQuery($qry, array($funktion_kurzbz, $widgetId, $uid));
}
//TODO(MANU) move to bookmark model
public function addTagFilter($widgetId, $uid, $funktion_kurzbz, $tags)
{
if (is_array($tags)) {
$tags = json_encode($tags); // convert PHP array to JSON string
}
$params = [$funktion_kurzbz, $widgetId, $tags, $uid];
$qry = "
UPDATE dashboard.tbl_dashboard_benutzer_override
SET override = jsonb_set(
COALESCE(override, '{}'::jsonb),
format('{%s,widgets,%s,config,tags}',? , ?)::text[],
?::jsonb,
true
)
WHERE uid = ?
";
// return $qry;
return $this->execQuery($qry, $params);
}
}
+8
View File
@@ -54,4 +54,12 @@ export default {
url: '/api/frontend/v1/Bookmark/getAllBookmarkTags'
};
},
//TODO(Manu) getTagFilter and addTagFilter
//in bookmark_model
/* updateBenutzeroverride() {
return{
method: 'post',
url: `/api/frontend/v1/Bookmark/update/${bookmark_id}`,
params: {url, title, tag}
}*/
};
+2 -1
View File
@@ -189,8 +189,9 @@ export default {
</div>
</Transition>
</div>
<!-- TODO Manu rename/remove-->
<div v-if="ready" class="card-body overflow-hidden p-0">
<component :is="component" v-model:shared-data="sharedData" :config="arguments" :width="width" :height="height" @setConfig="setConfig" @change="changeConfigManually"></component>
<component :is="component" v-model:shared-data="sharedData" :config="arguments" :width="width" :height="height" :widget-id="widgetID" :manu2="widgetID" :widgetConfig="config" @setConfig="setConfig" @change="changeConfigManually"></component>
</div>
<div v-else class="card-body overflow-hidden text-center d-flex flex-column justify-content-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
<bs-modal v-if="hasConfig" ref="config" @hideBsModal="handleHideBsModal" @showBsModal="handleShowBsModal">
@@ -5,7 +5,10 @@ export default {
"height",
"configMode",
"sharedData",
"widgetInfo"
"widgetInfo",
"widgetId", //check
"manu2", //TODO(Manu) check/remove
"widgetConfig"
],
emits: [
"setConfig",
+213 -62
View File
@@ -4,6 +4,7 @@ import BsModal from "../Bootstrap/Modal.js";
import AbstractWidget from './Abstract.js';
import ApiBookmark from '../../api/factory/widget/bookmark.js';
import ApiDashboard from '../../api/factory/cis/dashboard.js'; //nutzt aber nicht viel
export default {
name: "WidgetsUrl",
@@ -12,6 +13,10 @@ export default {
editModeIsActive: {
type: Boolean,
default: false
},
sectionName: {
type: String,
default: false
}
},
components:{
@@ -19,7 +24,8 @@ export default {
FormInput,
BsModal,
PvChips: primevue.chips,
PvMultiSelect: primevue.multiselect
PvMultiSelect: primevue.multiselect,
PvAutoComplete: primevue.autocomplete,
},
data: () => ({
bookmark_id: null,
@@ -30,10 +36,17 @@ export default {
invalidURL: false,
invalidTitel: false,
},
tagsArray: [
],
tagsArray: [],
selectedTags: [],
newTag: null,
selectedFilters: [], //die aber im benutzer_override speichern
items:[],
value: '',
//viewData: []
ArrayAC: [],
selectedTagsAC: [],
tagsInput2: [],
filteredArrayAC: [],
}),
computed: {
tagName() {
@@ -69,7 +82,9 @@ export default {
tagsInput(){
return this.selectedTags.map(item => item.tag);
},
filterInput(){
return this.selectedFilters.map(item => item.tag);
},
},
methods: {
stopDrag(event){
@@ -87,7 +102,9 @@ export default {
this.title_input = bookmark.title;
this.url_input = bookmark.url;
this.bookmark_id = bookmark.bookmark_id;
this.selectedTags = this.prepareTag(bookmark.tag);
//but the JSON string to array
this.selectedTagsAC = JSON.parse(bookmark.tag);
// this.selectedTags = this.prepareTag(bookmark.tag);
this.$refs.editModal.show();
},
@@ -100,7 +117,8 @@ export default {
bookmark_id: this.bookmark_id,
title: this.title_input,
url: this.url_input,
tag: this.tagsInput,
tag: this.selectedTagsAC,
//tag: this.tagsInput, //old version
}))
.then((res) => res.data)
.then((result) => {
@@ -133,7 +151,8 @@ export default {
this.$api
.call(ApiBookmark.insert({
//tag: this.config.tag,
tag: this.tagsInput,
//tag: this.tagsInput, //old version
tag: this.selectedTagsAC,
title: this.title_input,
url: this.url_input,
sort: this.sort
@@ -186,6 +205,7 @@ export default {
this.$fhcAlert.alertInfo(this.$p.t("bookmark", "bookmarkDeleted"));
// refetch the bookmarks to see the updates
this.fetchBookmarks();
this.getAllBookmarkTags();
})
.catch(this.$fhcAlert.handleSystemError);
},
@@ -265,27 +285,190 @@ export default {
.call(ApiBookmark.getAllBookmarkTags())
.then((res) => res.data)
.then((result) => {
//Variante Chips
this.tagsArray = this.prepareTag(result.data);
//Variante Autocomplete
/*
this.ArrayAC = result.data;
console.log("type of " + typeof this.ArrayAC);
console.log(Array.isArray(this.ArrayAC));*/
// this.ArrayAC = Object.values(result.data);
// this.ArrayAC = Object.keys(result.data);
this.ArrayAC = result.data; //in object umwandeln
/* this.ArrayAC = {...this.ArrayAC};
console.log("type of " + typeof this.ArrayAC);
console.log(Array.isArray(this.ArrayAC)); // true*/
/* Array.isArray(result.data)
? result.data
: JSON.parse(result.data); */
})
.catch(this.$fhcAlert.handleSystemError);
}
},
addBookmarkFilter(){
console.log("in addFilterLogic: save in benutzeroverride " + this.selectedFilters.map(tag => tag.tag).join(", "));
console.log("widget_id " + this.manu2);
console.log("tags " + this.filterInput);
console.log(`${this.apiurl}/dashboard/Config/insertAndUpdateTagsForBookmarksUser`, {
db: "CIS",
funktion_kurzbz: this.sectionName,
widget_id: this.manu2,
tags: this.filterInput
});
axios.post(this.apiurl + '/dashboard/Config/insertAndUpdateTagsForBookmarksUser', {
db: "CIS",
funktion_kurzbz: this.sectionName,
widget_id: this.manu2,
tags: this.filterInput
})
.then(res => {
console.log("API Response:", res.data);
})
.catch(err => {
console.error("ERROR:", err);
});
},
getTagFilter(widget_id){
/* console.log(`${this.apiurl}/dashboard/Config/getTagFilter`, {
db: "CIS",
funktion_kurzbz: this.sectionName,
widget_id: widget_id
});*/
axios.get(this.apiurl + '/dashboard/Config/getTagFilter', {
params: {
db: "CIS",
funktion_kurzbz: this.sectionName,
widget_id: widget_id
}
})
.then(res => {
// console.log("API Response:", res.data);
//old version
/* const raw = res.data?.retval?.retval?.[0]?.tags;
const filterArray = raw ? JSON.parse(raw) : [];
this.selectedFilters = this.prepareTag(filterArray);*/
//unter config
const rawTags = res.data.retval.retval[0].tags; // string
const filterArray = JSON.parse(rawTags);
//this.selectedFilters = res.data.retval;
this.selectedFilters = this.prepareTag(filterArray);
})
.catch(err =>
console.error('ERROR: ', err));
},
//Variante mit AutocompleteFeld
/* search(event) {
this.ArrayAC = this.ArrayAC.map((item) => item) || event;
},*/
search(event) {
const query = event.query ?? ""; // PrimeVue liefert query
// Filter ArrayAC nach eingegebenem Text
this.filteredArrayAC = this.ArrayAC.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
// Wenn kein Treffer → manuellen Text erlauben
if (this.filteredArrayAC.length === 0 && query) {
this.filteredArrayAC = [query];
}
console.log("Filtered suggestions:", this.filteredArrayAC);
},
/* addCustomTag(event) {
const value = event.value; // das ist der vom User eingegebene Text
console.log("Custom Tag:", value);
// Füge den neuen Tag zu selectedTags hinzu
// this.selectedTags = [...this.selectedTags, value];
const newTag = { tag: value };
// Optional: Füge den Tag auch in ArrayAC hinzu, damit er zukünftig auswählbar ist
if (!this.tagsArray.includes(value)) {
this.tagsArray.push(value);
}
}*/
},
async mounted() {
await this.fetchBookmarks();
this.getAllBookmarkTags();
if(this.manu2) {
this.getTagFilter(this.manu2);
}
/* console.log("config " + this.config);
console.log("widgetID " + this.widgetId);*/
// console.log("apiurl " + this.apiurl );
/* console.log("widgetinfo " + this.widgetInfo);
console.log("config " + this.config);
console.log(JSON.stringify(this.config));
console.log("width " + this.width);
console.log("height " + this.height);
console.log("configMode " + this.configMode);
console.log("sharedData " + this.sharedData);
console.log(JSON.stringify(this.sharedData));*/
//console.log( this.config.map(tag => tag.tag).join(", "));
/* console.log("manu " + this.manu);
console.log("manu2 " + this.manu2);
console.log("widgetID " + this.widgetID);*/
},
created() {
//
// this.$emit('setConfig', true); -> use this to enable widget config mode if needed
// this.$emit('setConfig', true); // -> use this to enable widget config mode if needed
/*
widgetId: {{widgetId}}
{{manu2}} {{sectionName}}
*/
},
template: /*html*/ `
<div class="widgets-url w-100 h-100 overflow-auto" style="padding: 1rem 1rem;">
<div class="d-flex flex-column justify-content-between">
<button class="btn btn-outline-secondary btn-sm w-100 mt-2 card" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
<div class="d-flex flex-column justify-content-between">
<!--TODO Manu eigener button mit modal für Filter, Autocomplete Variante !-->
<!-- div class="mt-2 row">
<div class="col-10">
<PvMultiSelect
v-model="selectedFilters"
id="tagFilterUrl"
:options="tagsArray"
optionLabel="tag"
display="chip"
placeholder="Show only tags"
:maxSelectedLabels="3"
class="p-inputtext-sm w-100 me-2"
/>
</div>
<div class="col-1">
<button
class="btn btn-secondary"
@click="addBookmarkFilter"
title="nach Tags filtern"
>
<i class="fa-solid fa-filter"></i>
</button>
</div>
</div -->
</div>
<button class="btn btn-outline-secondary btn-sm w-100 mt-2 card" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
<template v-if="shared">
<template v-if="!emptyBookmarks">
@@ -341,15 +524,15 @@ export default {
<span>{{$p.t('bookmark','emptyBookmarks')}}</span>
</div>
</template>
<template v-else>
<p v-for="i in 4" class="placeholder-glow">
<span class="placeholder" :class="{'col-9' : true}"></span>
</p>
</template>
</div>
</div>
</template>
</div>
<!--EDIT MODAL-->
<teleport to="body">
<bs-modal @[\`hide.bs.modal\`]="bookmark_id=null; clearInputs();" ref="editModal">
@@ -362,31 +545,15 @@ export default {
<form-input label="Url" title="Url" id="editUrl" v-model="url_input" name="url"></form-input>
<label class="mt-2">Tags</label>
<div class="mt-2 row">
<div class="col-6">
<form-input
v-model="newTag"
placeholder="Add new tag"
@keyup.enter="addNewTag"
class="w-full"
/>
</div>
<div class="col-2">
<button class="btn btn-secondary" @click="addNewTag">+</button>
</div>
</div>
<div class="mt-2">
<PvMultiSelect
v-model="selectedTags"
id="tagUrl"
:options="tagsArray"
optionLabel="tag"
display="chip"
placeholder="Selected Tags for URL"
:maxSelectedLabels="3"
class="w-full md:w-20rem mt-1"
<PvAutoComplete
v-model="selectedTagsAC"
multiple
dropdown
:suggestions="filteredArrayAC"
@complete="search"
/>
</div>
</div>
</template>
<template #footer>
<button @click="editBookmark" class="btn btn-primary">{{$p.t('bookmark','saveLink')}}</button>
@@ -405,31 +572,15 @@ export default {
<form-input label="Url" title="Url" id="insertUrl" v-model="url_input" name="url"></form-input>
<label class="mt-2">Tags</label>
<div class="mt-2 row">
<div class="col-6">
<form-input
v-model="newTag"
placeholder="Add new tag"
@keyup.enter="addNewTag"
class="w-full"
/>
</div>
<div class="col-2">
<button class="btn btn-secondary" @click="addNewTag">+</button>
</div>
</div>
<div class="mt-2">
<PvMultiSelect
v-model="selectedTags"
id="tagUrl"
:options="tagsArray"
optionLabel="tag"
display="chip"
placeholder="Selected Tags for URL"
:maxSelectedLabels="3"
class="w-full md:w-20rem mt-1"
/>
</div>
<PvAutoComplete
v-model="selectedTagsAC"
multiple
dropdown
:suggestions="filteredArrayAC"
@complete="search"
/>
</div>
</template>
<template #footer>