mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
start jobs builder + add table filter + overflow
* update python jobs builder (start merging in the builder format) * update some header and keys to filter * add filter table items logic on Filter component * fix table overflow style update with filtering * add i18n
This commit is contained in:
parent
79e5f8c8f2
commit
4e65e24292
8 changed files with 3013 additions and 2536 deletions
91
vuejs/client/src/components/Builder/Jobs.vue
Normal file
91
vuejs/client/src/components/Builder/Jobs.vue
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<script setup>
|
||||
// Containers
|
||||
import Grid from "@components/Widget/Grid.vue";
|
||||
import GridLayout from "@components/Widget/GridLayout.vue";
|
||||
import Table from "@components/Widget/Table.vue";
|
||||
import Title from "@components/Widget/Title.vue";
|
||||
|
||||
/**
|
||||
@name Builder/Jobs.vue
|
||||
@description This component is lightweight builder containing only the necessary components to create the jobs page.
|
||||
@example
|
||||
[
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "table",
|
||||
"data": {
|
||||
"title": "jobs_table_title",
|
||||
"minWidth": "lg",
|
||||
"header": [
|
||||
"jobs_table_name",
|
||||
"jobs_table_plugin_id",
|
||||
"jobs_table_interval",
|
||||
"jobs_table_last_run",
|
||||
"jobs_table_success",
|
||||
"jobs_table_last_run_date",
|
||||
"jobs_table_cache"
|
||||
],
|
||||
"positions": [
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
3,
|
||||
2
|
||||
],
|
||||
"items": [
|
||||
[
|
||||
{
|
||||
"name": "anonymous-report",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "anonymous-report"
|
||||
}
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@param {array} builder - Array of containers and widgets
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
builder: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- top level grid (layout) -->
|
||||
<GridLayout
|
||||
v-for="(container, index) in props.builder"
|
||||
:key="index"
|
||||
:gridLayoutClass="container.containerClass"
|
||||
:type="container.type"
|
||||
:title="container.title"
|
||||
:link="container.link"
|
||||
:columns="container.containerColumns"
|
||||
>
|
||||
<!-- widget grid -->
|
||||
<Grid>
|
||||
<!-- widget element -->
|
||||
<template v-for="(widget, index) in container.widgets" :key="index">
|
||||
<Table v-if="widget.type === 'Table'" v-bind="widget.data" />
|
||||
<Title v-if="widget.type === 'Title'" v-bind="widget.data" />
|
||||
</template>
|
||||
</Grid>
|
||||
</GridLayout>
|
||||
</template>
|
||||
|
|
@ -13,13 +13,14 @@ import { useFilter } from "@utils/form.js";
|
|||
We have default values that avoid filter ("all" for select and "" for keyword).
|
||||
Filters are fields so we need to provide a "field" key with same structure as a Field.
|
||||
We have to define "keys" that will be the keys the filter value will check.
|
||||
We can set filter :"default" in order to filter the base keys of an object.
|
||||
We can set filter :"settings" in order to filter settings, data must be an advanced template.
|
||||
We can set filter "default" in order to filter the base keys of an object.
|
||||
We can set filter "settings" in order to filter settings, data must be an advanced template.
|
||||
We can set filter "table" in order to filter table items.
|
||||
Check example for more details.
|
||||
@example
|
||||
[
|
||||
{
|
||||
filter: "default", // or "settings"
|
||||
filter: "default", // or "settings" or "table"
|
||||
type: "select",
|
||||
value: "all",
|
||||
keys: ["type"],
|
||||
|
|
@ -85,8 +86,6 @@ function filterData(filter, value) {
|
|||
let template = JSON.parse(JSON.stringify(props.data));
|
||||
const getFilters = JSON.parse(JSON.stringify(filters.base));
|
||||
|
||||
// Filter order for template : plugins, settings
|
||||
|
||||
// Base keys filtering (like plugin)
|
||||
const defaultFilters = getFilters.filter((f) => f.filter === "default");
|
||||
template = useFilter(template, defaultFilters);
|
||||
|
|
@ -117,6 +116,28 @@ function filterData(filter, value) {
|
|||
});
|
||||
}
|
||||
|
||||
// Base keys filtering (like plugin)
|
||||
const tableFilters = getFilters.filter((f) => f.filter === "table");
|
||||
if (tableFilters.length) {
|
||||
// Loop on each array of array
|
||||
for (let i = 0; i < template.length; i++) {
|
||||
const row = template[i];
|
||||
// We need to check one complete row on filter
|
||||
// So we need to merge all keys of each column
|
||||
const mergeRow = {};
|
||||
row.forEach((item) => {
|
||||
Object.keys(item).forEach((key) => {
|
||||
mergeRow[key] = item[key];
|
||||
});
|
||||
});
|
||||
const newRow = useFilter([mergeRow], tableFilters);
|
||||
// Case newRow is empty array, didn't pass filter
|
||||
if (Array.isArray(newRow) && newRow.length <= 0) template[i] = [];
|
||||
}
|
||||
// Remove empty row
|
||||
template = template.filter((row) => row.length > 0);
|
||||
}
|
||||
|
||||
emits("filter", template);
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { reactive, computed, ref, onMounted, onUpdated } from "vue";
|
||||
import { reactive, computed, ref, onMounted, watch } from "vue";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import Container from "@components/Widget/Container.vue";
|
||||
import Text from "@components/Widget/Text.vue";
|
||||
|
|
@ -33,7 +33,7 @@ import Filter from "@components/Widget/Filter.vue";
|
|||
],
|
||||
...
|
||||
],
|
||||
|
||||
|
||||
const filters = [
|
||||
{
|
||||
filter: "default",
|
||||
|
|
@ -123,11 +123,12 @@ const tableHeader = ref(null);
|
|||
|
||||
const table = reactive({
|
||||
id: uuidv4(),
|
||||
overflow: "0px",
|
||||
length: computed(() => {
|
||||
return props.header.length;
|
||||
}),
|
||||
rowLength: computed(() => {
|
||||
return props.items.length;
|
||||
return table.itemsFormat.length;
|
||||
}),
|
||||
title: computed(() => {
|
||||
return props.title ? props.title : "dashboard_table";
|
||||
|
|
@ -139,22 +140,27 @@ const table = reactive({
|
|||
});
|
||||
|
||||
function getOverflow() {
|
||||
const overflow =
|
||||
tableBody.value.getBoundingClientRect().width - tableBody.value.clientWidth;
|
||||
setTimeout(() => {
|
||||
const overflow =
|
||||
+tableBody.value.getBoundingClientRect().width -
|
||||
+tableBody.value.clientWidth;
|
||||
|
||||
if (overflow > 0) {
|
||||
return (tableHeader.value.style.paddingRight = `${overflow}px`);
|
||||
}
|
||||
|
||||
// remove style
|
||||
return tableHeader.value.removeAttribute("style");
|
||||
table.overflow = tableHeader.value.style.paddingRight = `${overflow}px`;
|
||||
if (overflow <= 0) {
|
||||
return tableHeader.value.removeAttribute("style");
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOverflow();
|
||||
});
|
||||
// whatch itemsFormat changes
|
||||
watch(
|
||||
() => table.itemsFormat,
|
||||
() => {
|
||||
getOverflow();
|
||||
}
|
||||
);
|
||||
|
||||
onUpdated(() => {
|
||||
onMounted(() => {
|
||||
getOverflow();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -163,7 +169,7 @@ onUpdated(() => {
|
|||
<Container :containerClass="`${props.containerClass} table-container`">
|
||||
<Filter
|
||||
v-if="filters.length"
|
||||
@filter="(v) => (table.itemsFormat = filterData)"
|
||||
@filter="(v) => (table.itemsFormat = v)"
|
||||
:data="table.itemsBase"
|
||||
:filters="filters"
|
||||
/>
|
||||
|
|
@ -190,20 +196,24 @@ onUpdated(() => {
|
|||
:class="['table-header-item', `col-span-${props.positions[id]}`]"
|
||||
>
|
||||
<th role="columnheader">
|
||||
{{ head }}
|
||||
{{ $t(head, head) }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-table-body ref="tableBody" class="table-content">
|
||||
<tr
|
||||
v-for="rowId in table.rowLength - 1"
|
||||
v-for="rowId in table.rowLength"
|
||||
:key="rowId - 1"
|
||||
role="row"
|
||||
:aria-rowindex="rowId"
|
||||
class="table-content-item"
|
||||
>
|
||||
<template v-for="(col, id) in table.itemsFormat[rowId]">
|
||||
<template
|
||||
v-for="(col, id) in table.itemsFormat[rowId - 1]"
|
||||
:key="col"
|
||||
>
|
||||
<td
|
||||
role="Scell"
|
||||
role="cell"
|
||||
:class="[
|
||||
'table-content-item-wrap',
|
||||
`col-span-${props.positions[id]}`,
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@
|
|||
"instances_status": "status",
|
||||
"instances_active": "active",
|
||||
"instances_inactive": "inactive",
|
||||
"jobs_title": "Jobs list",
|
||||
"jobs_download_cache_file": "download cache files",
|
||||
"jobs_search": "search jobs",
|
||||
"jobs_search_desc": "Search within job name, plugin id or last run.",
|
||||
|
|
@ -187,6 +188,14 @@
|
|||
"jobs_interval_desc": "Choose interval between available options.",
|
||||
"jobs_success": "success",
|
||||
"jobs_success_desc": "Choose the job success state.",
|
||||
"jobs_last_run": "last run",
|
||||
"jobs_last_run_desc": "Choose the job last run state."
|
||||
"jobs_reload": "reload",
|
||||
"jobs_reload_desc": "Choose the job reload state.",
|
||||
"jobs_table_title" :"Jobs list with plugin id, name, last run, interval and success state.",
|
||||
"jobs_table_name" : "Name",
|
||||
"jobs_table_plugin_id" : "Plugin id",
|
||||
"jobs_table_interval" : "Interval",
|
||||
"jobs_table_reload" : "Reload",
|
||||
"jobs_table_success" : "Success",
|
||||
"jobs_table_last_run_date" : "Last run date",
|
||||
"jobs_table_cache" : "Cache"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script setup>
|
||||
import { reactive, onBeforeMount, onMounted } from "vue";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderHome from "@components/Builder/Home.vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
import { useForm } from "@utils/form.js";
|
||||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import BuilderHome from "@components/Builder/Home.vue";
|
||||
|
||||
/**
|
||||
@name Page/Home.vue
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
2544
vuejs/jobs.json
2544
vuejs/jobs.json
File diff suppressed because it is too large
Load diff
155
vuejs/test3.py
155
vuejs/test3.py
|
|
@ -205,7 +205,7 @@ jobs = {
|
|||
}
|
||||
|
||||
|
||||
def jobs_to_list(jobs):
|
||||
def get_jobs_list(jobs):
|
||||
data = []
|
||||
# loop on each dict
|
||||
for key, value in jobs.items():
|
||||
|
|
@ -282,9 +282,156 @@ def jobs_to_list(jobs):
|
|||
|
||||
data.append(item)
|
||||
|
||||
# store on a file
|
||||
return data
|
||||
|
||||
|
||||
def job_builder(jobs):
|
||||
|
||||
jobs_list = get_jobs_list(jobs)
|
||||
|
||||
intervals = ["all"]
|
||||
|
||||
# loop on each job
|
||||
for job in jobs_list:
|
||||
# loop on each item
|
||||
for item in job:
|
||||
# get the interval if not already in intervals
|
||||
if item.get("every") and item.get("every") not in intervals:
|
||||
intervals.append(item.get("every"))
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {"title": "jobs_title", "tag": "h1", "type": "card"},
|
||||
},
|
||||
{
|
||||
"type": "Table",
|
||||
"data": {
|
||||
"title": "jobs_table_title",
|
||||
"minWidth": "lg",
|
||||
"header": [
|
||||
"jobs_table_name",
|
||||
"jobs_table_plugin_id",
|
||||
"jobs_table_interval",
|
||||
"jobs_table_reload",
|
||||
"jobs_table_success",
|
||||
"jobs_table_last_run_date",
|
||||
"jobs_table_cache",
|
||||
],
|
||||
"positions": [2, 2, 1, 1, 1, 2, 3],
|
||||
"items": jobs_list,
|
||||
"filters": [
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["name", "plugin_id", "last_run"],
|
||||
"field": {
|
||||
"id": "jobs-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "jobs-keyword",
|
||||
"containerClass": "setting",
|
||||
"label": "jobs_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": False,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_search_desc",
|
||||
"iconName": "info",
|
||||
"iconColor": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "every",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["every"],
|
||||
"field": {
|
||||
"id": "jobs-every",
|
||||
"value": "all",
|
||||
"values": intervals,
|
||||
"name": "jobs-every",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_interval",
|
||||
"containerClass": "setting",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_interval_desc",
|
||||
"iconName": "info",
|
||||
"iconColor": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "success",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["success"],
|
||||
"field": {
|
||||
"id": "jobs-success",
|
||||
"value": "all",
|
||||
"values": ["all", "success", "failed"],
|
||||
"name": "jobs-success",
|
||||
"onlyDown": True,
|
||||
"containerClass": "setting",
|
||||
"label": "jobs_success",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_success_desc",
|
||||
"iconName": "info",
|
||||
"iconColor": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "reload",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["reload"],
|
||||
"field": {
|
||||
"id": "jobs-last-run",
|
||||
"value": "all",
|
||||
"values": ["all", "success", "failed"],
|
||||
"name": "jobs-last-run",
|
||||
"onlyDown": True,
|
||||
"containerClass": "setting",
|
||||
"label": "jobs_reload",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_reload_desc",
|
||||
"iconName": "info",
|
||||
"iconColor": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
# store on a file
|
||||
with open("jobs.json", "w") as f:
|
||||
json.dump(data, f, indent=4)
|
||||
json.dump(builder, f, indent=4)
|
||||
|
||||
|
||||
jobs_to_list(jobs)
|
||||
job_builder(jobs)
|
||||
|
|
|
|||
Loading…
Reference in a new issue