-
+
@@ -20,9 +20,9 @@ Vue.component('tt-autocomplete', {
-
+
-
+
Mehr Suchergebnisse vorhanden. Bitte genauer eingeben
-
+
Keine Suchergebnisse vorhanden.
- Bitte mindestens 3 Zeichen eingeben
+ Bitte mindestens 3 Zeichen eingeben
@@ -54,19 +55,29 @@ Vue.component('tt-autocomplete', {
},
label: {
type: String,
- required: true,
+ required: false,
},
apiUrl: String,
+ items: {
+ type: Array,
+ default: () => [],
+ }
},
async mounted() {
- if (this.value) {
+ if (this.value && this.apiUrl) {
const response = await axios.get(`${this.apiUrl}&autocomplete=1&searchedID=${this.value}`);
this.displayValue = response.data[0].text;
+ } else if (this.value) {
+ const selectedItem = this.items.find(item => item.value === this.value);
+ this.displayValue = selectedItem ? selectedItem.text : '';
}
+
+ console.log(this.$slots.append ? 'append' : 'no append');
+
},
data() {
return {
- items: [],
+ displayingItems: [],
displayValue: '',
isLoading: false,
showSuggestions: false,
@@ -76,7 +87,7 @@ Vue.component('tt-autocomplete', {
},
watch: {
value(newValue) {
- const selectedItem = this.items.find(item => item.value === newValue);
+ const selectedItem = this.displayingItems.find(item => item.value === newValue);
this.displayValue = selectedItem ? selectedItem.text : '';
},
apiUrl() {
@@ -86,6 +97,7 @@ Vue.component('tt-autocomplete', {
methods: {
onInput(event) {
this.displayValue = event.target.value;
+ this.$emit('input', undefined);
this.fetchSuggestions();
},
onFocus() {
@@ -97,7 +109,28 @@ Vue.component('tt-autocomplete', {
}, 200);
},
fetchSuggestions() {
- if (!this.apiUrl || this.displayValue.length < 3) return;
+ if (this.displayValue.length < 3) {
+ this.$set(this, 'displayingItems', []);
+ return this.displayingItems = [];
+ }
+
+ if (!this.apiUrl) {
+
+ const isNegated = this.displayValue.startsWith('!');
+ const substrings = (isNegated ? this.displayValue.slice(1) : this.displayValue).split(' ').map(s => s.toLowerCase());
+ this.displayingItems = this.items.filter(item => {
+ let substringMatch = true;
+ for (var k = 0, klen = substrings.length; k < klen; ++k) {
+ if (!item.text.toLowerCase().includes(substrings[k])) {
+ substringMatch = false;
+ break;
+ }
+ }
+ return (isNegated && !substringMatch) || (!isNegated && substringMatch);
+ })
+
+ return
+ }
this.isLoading = true;
clearTimeout(this.fetchSuggestionsDebounceTimer);
@@ -107,9 +140,9 @@ Vue.component('tt-autocomplete', {
setTimeout(async () => {
const response = await axios.get(`${this.apiUrl}&autocomplete=1&q=${this.displayValue}`);
if (response.data?.status === 'error') {
- this.items = [];
+ this.displayingItems = [];
} else {
- this.items = response.data
+ this.displayingItems = response.data
}
this.isLoading = false;
diff --git a/public/plugins/vue/tt-components/tt-icon-select.js b/public/plugins/vue/tt-components/tt-icon-select.js
index 5266ad581..b70e9dd2f 100644
--- a/public/plugins/vue/tt-components/tt-icon-select.js
+++ b/public/plugins/vue/tt-components/tt-icon-select.js
@@ -8,10 +8,13 @@ Vue.component('tt-icon-select', {
};
},
mounted() {
- // Dynamically add CSS to disable default dropdown caret
- const style = document.createElement('style');
- style.innerHTML = `.tt-select .dropdown-toggle::after { display: none; }`;
- document.body.appendChild(style);
+ if (! document.getElementById('tt-icon-select-style')) {
+ // Dynamically add CSS to disable default dropdown caret
+ const style = document.createElement('style');
+ style.id = 'tt-icon-select-style';
+ style.innerHTML = `.tt-select .dropdown-toggle::after { display: none; }`;
+ document.body.appendChild(style);
+ }
document.addEventListener('click', this.handleClick);
diff --git a/public/plugins/vue/tt-components/tt-number-range.js b/public/plugins/vue/tt-components/tt-number-range.js
index 451799841..9b7065f55 100644
--- a/public/plugins/vue/tt-components/tt-number-range.js
+++ b/public/plugins/vue/tt-components/tt-number-range.js
@@ -44,9 +44,11 @@ Vue.component('tt-number-range', {
}
}
}, template: `
-
+
`
});
+
+
diff --git a/public/plugins/vue/tt-components/tt-table.js b/public/plugins/vue/tt-components/tt-table.js
index 8107c23cc..8edfb7dae 100644
--- a/public/plugins/vue/tt-components/tt-table.js
+++ b/public/plugins/vue/tt-components/tt-table.js
@@ -1,22 +1,44 @@
/**
* @typedef {Object} ttTableColumnConfig
- * @property {string} text - The display text of the column.
- * @property {string} key - The unique key of the column.
- * @property {string} filter - Indicates if filtering is enabled for the column.
- * @property {boolean} sortable - Indicates if sorting is enabled for the column.
- * @property {string} class - The CSS class(es) applied to the column.
+ * @property {string} text - The display text of the column header.
+ * @property {string} key - The unique key of the column (used for data access and filtering).
+ * @property {string} [filter] - (Optional) Determines the type of filter applied to the column. Default is 'search'.
+ * Possible values:
+ * - 'search': Basic text search (case-insensitive).
+ * - 'iconSelect': Filter with icons and associated text (requires 'filterOptions').
+ * - 'numberRange': Filter with numeric ranges (e.g., ">5", "10-20", etc.).
+ * - 'select': Dropdown filter with predefined options (requires 'filterOptions').
+ * - 'date': Date range filter.
+ * - 'false': Disables filtering for the column.
+ * @property {Array} [filterOptions] - (Optional) An array of filter options for 'iconSelect' or 'select' filters.
+ * Each option should be an object with 'text' (display text) and 'value' (filter value) properties.
+ * For 'iconSelect', an additional 'icon' property (CSS class for the icon) is required.
+ * @property {boolean} [sortable=true] - (Optional) Indicates whether the column is sortable. Default is true.
+ * @property {string} [class] - (Optional) Additional CSS classes to apply to the column.
+ * @property {string} [suffix] - (Optional) Additional CSS classes to apply to the column.
+ * @property {string} [prefix] - (Optional) Additional CSS classes to apply to the column.
+ * */
+
+/**
+ * @typedef {Object} ttTableConfig
+ * @property {string} key - A unique key for the table instance (used for saving/loading settings).
+ * @property {string} tableHeader - The main header text displayed above the table.
+ * @property {ttTableColumnConfig[]} headers - An array of column configuration objects (see `ttTableColumnConfig` typedef).
+ * @property {Function} [customRowClass] - (Optional) A function that returns a CSS class string based on row data.
+ * @property {Function} [expandCondition] - (Optional) A function that determines if a row is expandable.
+ * @property {number} [defaultPageSize=10] - (Optional) The default number of rows to display per page.
+ * @property {Function} [customExcelProcessor] - (Optional) A function to preprocess row data before exporting to Excel.
*/
+
Vue.component('tt-table-pagination', {
props: {
pagination: {
type: Object,
required: true,
default: () => ({page: 1, per_page: 10, total_rows: 0, filtered_available: 0, total_pages: 1})
- },
- reverse: {type: Boolean, default: false}
- },
- computed: {
+ }, reverse: {type: Boolean, default: false}
+ }, computed: {
pagesToDisplay() {
const range = 2;
const start = Math.max(this.pagination.page - range, 1);
@@ -26,65 +48,61 @@ Vue.component('tt-table-pagination', {
pages.push(i);
}
return pages.length === 0 ? [1] : pages;
- },
- pageInfoText() {
+ }, pageInfoText() {
const start = Math.min(this.pagination.page * this.pagination.per_page - this.pagination.per_page + 1, this.pagination.total_rows);
const end = Math.min(this.pagination.page * this.pagination.per_page, this.pagination.total_rows);
- const total = this.pagination.total_rows === this.pagination.filtered_available
- ? this.pagination.total_rows
- : `${this.pagination.filtered_available} (${this.pagination.total_rows})`;
+ const total = this.pagination.total_rows === this.pagination.filtered_available ? this.pagination.total_rows : `${this.pagination.filtered_available} (${this.pagination.total_rows})`;
return `${start} bis ${end} von ${total}`;
}
- },
- methods: {
+ }, methods: {
fetchRows(page) {
this.$emit('fetch-rows', page);
}
- },
- template: `
-