diff --git a/src/ui/client/builder/instances2.py b/src/ui/client/builder/instances2.py index a734ff330..9b61d7b6e 100644 --- a/src/ui/client/builder/instances2.py +++ b/src/ui/client/builder/instances2.py @@ -1,219 +1,36 @@ import json import base64 -# TODO : REMOVE operation by custom endpoint +from builder.instances import instances_builder -from builder.utils.widgets import button, button_group, title, text, tabulator, input -columns = [ - {"title": "Name", "field": "name", "formatter": "text"}, - {"title": "Hostname", "field": "hostname", "formatter": "text"}, - {"title": "Type", "field": "type", "formatter": "text"}, - {"title": "Method", "field": "method", "formatter": "text"}, - {"title": "Creation date", "field": "creation_date", "formatter": "text"}, - {"title": "Last seen", "field": "last_seen", "formatter": "text"}, - {"title": "health", "field": "health", "formatter": "text"}, # up, down, loading - { - "title": "Actions", - "field": "actions", - "formatter": "buttonGroup", - }, +# Create instance class using keys from the instances list +class Instance: + def __init__(self, _type, health, _id, hostname, name): + self._type = _type + self.health = health + self._id = _id + self.hostname = hostname + self.name = name + + +instances = [ + Instance("static", True, 1, "hostname1", "instance1"), + Instance("container", True, 2, "hostname2", "instance2"), ] -# Because we are going to use built-in filters, we can't use the Filter component -# So we need this format in order to create under the hood fields that will be linked to the tabulator filter -# We need to pass on the setting key the same props as the Fields component. For example a "=" tabulator filter will be used with a select field, this one need "values" array to work. -# type : Choose between available tabulator built-in filters ("keywords", "like", "!=", ">", "<", ">=", "<=", "in", "regex", "!=") -filters = [ - { - "type": "like", - "fields": ["name", "hostname"], - "setting": { - "id": "input-search-host-name", - "name": "input-search-host-name", - "label": "instances_search_host_name", # keep it (a18n) - "value": "", - "inpType": "input", - "columns": {"pc": 3, "tablet": 4, " mobile": 12}, - }, - }, - { - "type": "=", - "fields": ["type"], - "setting": { - "id": "select-type", - "name": "select-type", - "label": "instances_select_type", # keep it (a18n) - "value": "all", # keep "all" - "values": ["all", "type1", "type2"], # keep "all" but add your types - "inpType": "select", - "onlyDown": True, - "columns": {"pc": 3, "tablet": 4, " mobile": 12}, - }, - }, - { - "type": "=", - "fields": ["method"], - "setting": { - "id": "select-method", - "name": "select-method", - "label": "instances_select_method", # keep it (a18n) - "value": "all", # keep "all" - "values": ["all", "method1", "method2"], # keep "all" but add your methods - "inpType": "select", - "onlyDown": True, - "columns": {"pc": 3, "tablet": 4, " mobile": 12}, - }, - }, -] - -actions = ( - { - "buttons": [ - button( - id="ping-instance-INSTANCE_NAME", # replace INSTANCE_NAME by the instance name - text="action_ping", # keep it (a18n) - color="success", - size="normal", - hideText=True, - iconName="globe", - iconColor="white", - modal={ - "widgets": [ - title(title="instances_ping_title"), # keep it (a18n) - text(text="instances_ping_subtitle"), # keep it (a18n) - text(bold=True, text="INSTANCE_NAME"), # replace INSTANCE_NAME by the instance name - button_group( - buttons=[ - button( - id="close-ping-btn-INSTANCE_NAME", # replace INSTANCE_NAME by the instance name - text="action_close", # keep it (a18n) - color="close", - size="normal", - attrs={"data-close-modal": ""}, # a11y - )["data"], - button( - id="ping-btn-INSTANCE_NAME", # replace INSTANCE_NAME by the instance name - text="action_ping", # keep it (a18n) - color="info", - size="normal", - attrs={ - "data-submit-form": '{"instance_name" : ", "instance_hostname" : "", "operation" : "ping" }' - }, # replace values by needed ones for ping operation, data-submit-form attributs will parse and submit values to the form - )["data"], - ] - ), - ], - }, - ), - button( - id="delete-instance-INSTANCE_NAME", # replace INSTANCE_NAME by the instance name - text="action_delete", # keep it (a18n) - color="success", - size="normal", - hideText=True, - iconName="globe", - iconColor="white", - modal={ - "widgets": [ - title(title="instances_delete_title"), # keep it (a18n) - text(text="instances_delete_subtitle"), # keep it (a18n) - text(bold=True, text="INSTANCE_NAME"), # replace INSTANCE_NAME by the instance name - button_group( - buttons=[ - button( - id="close-delete-btn-INSTANCE_NAME", # replace INSTANCE_NAME by the instance name - text="action_close", # keep it (a18n) - color="close", - size="normal", - attrs={"data-close-modal": ""}, # a11y - )["data"], - button( - id="delete-btn-INSTANCE_NAME", # replace INSTANCE_NAME by the instance name - text="action_delete", # keep it (a18n) - color="delete", - size="normal", - attrs={ - "data-submit-form": '{"instance_name" : ", "instance_hostname" : "", "operation" : "delete" }' - }, # replace values by needed ones for ping operation, data-submit-form attributs will parse and submit values - )["data"], - ] - ), - ], - }, - ), - ] - }, -) +types = ["static", "container"] +methods = ["ui", "manual"] -items = [ - { - "name": text(text="Name")["data"], - "hostname": text(text="Hostname")["data"], - "type": text(text="Type")["data"], - "method": text(text="Method")["data"], - "creation_date": text(text="Creation date")["data"], - "last_seen": text(text="Last seen")["data"], - "actions": actions, - }, -] - - -# TODO : Add warning that port and server_name will be override by scheduler -instance_create_form_widgets = [ - input( - id="instance-name", - name="instance-name", - label="instances_name", # keep it (a18n) - value="", - pattern="", # add your pattern if needed - columns={"pc": 3, "tablet": 4, " mobile": 12}, - ), - input( - id="instance-hostname", - name="instance-hostname", - label="instances_hostname", # keep it (a18n) - value="", - pattern="", # add your pattern if needed - columns={"pc": 3, "tablet": 4, " mobile": 12}, - ), - button( - id="create-instance", - text="action_create", # keep it (a18n) - color="success", - size="normal", - ), -] - -builder = [ - { - "type": "card", - "display": ["main", 1], - "widgets": [ - tabulator( - id="table-instances", - columns=columns, - items=items, - filters=filters, - ) - ], - }, - { - "type": "card", - "display": ["main", 2], - "widgets": instance_create_form_widgets, - }, -] - +builder = instances_builder(instances) +# store on a file with open("instances2.json", "w") as f: - f.write(json.dumps(builder)) + json.dump(builder, f) output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8")) - output_base64_string = output_base64_bytes.decode("ascii") - with open("instances2.txt", "w") as f: f.write(output_base64_string) diff --git a/src/ui/client/builder/pages/bans2.py b/src/ui/client/builder/pages/bans2.py index 4fa69548b..67cbfe38f 100644 --- a/src/ui/client/builder/pages/bans2.py +++ b/src/ui/client/builder/pages/bans2.py @@ -102,10 +102,10 @@ def ban_item(id: str, ip: str, reason: str, ban_start_date: int, ban_end_date: i def bans_items(items: list) -> list: items = [] - for item in items: + for index, item in enumerate(items): items.append( ban_item( - id=item.get("id"), + id=index, ip=item.get("ip"), reason=item.get("reason"), ban_start_date=item.get("ban_start_date"), @@ -246,33 +246,31 @@ add_ban_action = ( ) +def bans_tabs(): + return [ + button_widget( + text="bans_tab_list", + display=["main", 1], + size="tab", + iconColor="white", + iconName="list", + ), + button_widget( + text="bans_tab_add", + display=["main", 2], + size="tab", + iconColor="white", + iconName="plus", + ), + ] + + def bans_builder(bans: list, reasons: list, remains: list) -> list: return [ # Tabs is button group with display value and a size tab inside a tabs container { type: "tabs", - "widgets": [ - button_group_widget( - buttons=[ - button_widget( - text="bans_tab_list", - display=["main", 1], - isTab=True, - size="tab", - iconColor="white", - iconName="list", - ), - button_widget( - text="bans_tab_add", - display=["main", 2], - isTab=True, - size="tab", - iconColor="white", - iconName="plus", - ), - ] - ) - ], + "widgets": [button_group_widget(buttons=bans_tabs())], }, { "type": "card", diff --git a/src/ui/client/builder/pages/configs2.py b/src/ui/client/builder/pages/configs2.py index 0b360052a..f9c3f7696 100644 --- a/src/ui/client/builder/pages/configs2.py +++ b/src/ui/client/builder/pages/configs2.py @@ -1,5 +1,11 @@ from enum import StrEnum + +class Global(StrEnum): + YES = "yes" + NO = "no" + + from builder.utils.widgets import ( input_widget, select_widget, @@ -11,14 +17,23 @@ from builder.utils.widgets import ( title_widget, text_widget, tabulator_widget, + icons_widget, ) from builder.utils.table import add_column -class Global(StrEnum): - YES = "yes" - NO = "no" +configs_columns = [ + add_column(title="Name", field="name", formatter="text"), + add_column(title="Type", field="type", formatter="text"), + add_column(title="global", field="global", formatter="icons"), + add_column( + title="services", field="services", formatter="buttonGroup" + ), # We will display a button with a modal that show all services apply. Case global, show all services. + add_column( + title="actions", field="actions", formatter="buttonGroup" + ), # edit button that will switch to the form using display store + delete with modal to confirm +] def config_form( @@ -120,19 +135,6 @@ def config_form( ) -configs_columns = [ - add_column(title="Name", field="name", formatter="text"), - add_column(title="Type", field="type", formatter="text"), - add_column(title="global", field="global", formatter="text"), - add_column( - title="services", field="services", formatter="buttonGroup" - ), # We will display a button with a modal that show all services apply. Case global, show all services. - add_column( - title="actions", field="actions", formatter="buttonGroup" - ), # edit button that will switch to the form using display store + delete with modal to confirm -] - - def configs_filter(types: list): return [ { @@ -194,7 +196,7 @@ def config_item(filename: str, conf_type: str, is_global: Global, services: list { "name": text_widget(text=filename)["data"], "type": text_widget(text=conf_type)["data"], - "global": text_widget(text=is_global)["data"], + "global": icons_widget(iconName="check" if is_global == "yes" else "cross", value=is_global)["data"], "services": button_group_widget( buttons=[ button_widget( diff --git a/src/ui/client/builder/pages/instances2.py b/src/ui/client/builder/pages/instances2.py new file mode 100644 index 000000000..5c906d2cb --- /dev/null +++ b/src/ui/client/builder/pages/instances2.py @@ -0,0 +1,281 @@ +from builder.utils.widgets import button_widget, button_group_widget, title_widget, text_widget, tabulator_widget, input_widget, icons_widget +from builder.utils.table import add_column +from enum import StrEnum + + +class Healths(StrEnum): + UP = "up" + DOWN = "down" + LOADING = "loading" + + +columns = [ + add_column(title="Name", field="name", formatter="text"), + add_column(title="Hostname", field="hostname", formatter="text"), + add_column(title="Type", field="type", formatter="text"), + add_column(title="Method", field="method", formatter="text"), + add_column(title="Creation date", field="creation_date", formatter="text"), + add_column(title="Last seen", field="last_seen", formatter="text"), + add_column(title="health", field="health", formatter="icons"), + add_column(title="Actions", field="actions", formatter="buttonGroup"), +] + + +def instances_filter(healths: Healths, types: list = [], methods: list = []) -> list: + filters = [ + { + "type": "like", + "fields": ["name", "hostname"], + "setting": { + "id": "input-search-host-name", + "name": "input-search-host-name", + "label": "instances_search_host_name", # keep it (a18n) + "value": "", + "inpType": "input", + "columns": {"pc": 3, "tablet": 4, " mobile": 12}, + }, + } + ] + + if len(types) >= 2: + filters.append( + { + "type": "=", + "fields": ["type"], + "setting": { + "id": "select-type", + "name": "select-type", + "label": "instances_select_type", # keep it (a18n) + "value": "all", # keep "all" + "values": ["all"] + types, + "inpType": "select", + "onlyDown": True, + "columns": {"pc": 3, "tablet": 4, " mobile": 12}, + }, + } + ) + + if len(methods) >= 2: + filters.append( + { + "type": "=", + "fields": ["method"], + "setting": { + "id": "select-method", + "name": "select-method", + "label": "instances_select_method", # keep it (a18n) + "value": "all", # keep "all" + "values": ["all"] + methods, + "inpType": "select", + "onlyDown": True, + "columns": {"pc": 3, "tablet": 4, " mobile": 12}, + }, + }, + ) + + if len(healths) >= 2: + filters.append( + { + "type": "=", + "fields": ["health"], + "setting": { + "id": "select-health", + "name": "select-health", + "label": "instances_select_health", # keep it (a18n) + "value": "all", # keep "all" + "values": ["all"] + healths, + "inpType": "select", + "onlyDown": True, + "columns": {"pc": 3, "tablet": 4, " mobile": 12}, + }, + }, + ) + + return filters + + +def instance_item( + instance_name: str, + hostname: str, + instance_type: str, + method: str, + health: str, + creation_date: str, + last_seen: str, +): + { + "name": text_widget(text=instance_name)["data"], + "hostname": text_widget(text=hostname)["data"], + "type": text_widget(text=instance_type)["data"], + "method": text_widget(text=method)["data"], + "creation_date": text_widget(text=creation_date)["data"], + "last_seen": text_widget(text=last_seen)["data"], + "health": icons_widget( + iconName="check" if health == "up" else "cross" if health == "down" else "search", + value=health, + ), + "actions": { + "buttons": [ + button_widget( + id=f"ping-instance-{instance_name}", + text="action_ping", # keep it (a18n) + color="success", + size="normal", + hideText=True, + iconName="globe", + iconColor="white", + modal={ + "widgets": [ + title_widget(title="instances_ping_title"), # keep it (a18n) + text_widget(text="instances_ping_subtitle"), # keep it (a18n) + text_widget(bold=True, text=instance_name), + button_group_widget( + buttons=[ + button_widget( + id=f"close-ping-btn-{instance_name}", + text="action_close", # keep it (a18n) + color="close", + size="normal", + attrs={"data-close-modal": ""}, # a11y + )["data"], + button_widget( + id=f"ping-btn-{instance_name}", + text="action_ping", # keep it (a18n) + color="info", + size="normal", + attrs={ + "data-submit-form": f"""{{"instance_name" : "{instance_name}", "instance_hostname" : "{hostname}" }}""", + "data-submit-endpoint": "/ping", + }, + )["data"], + ] + ), + ], + }, + ), + button_widget( + id=f"delete-instance-{instance_name}", + text="action_delete", # keep it (a18n) + color="success", + size="normal", + hideText=True, + iconName="globe", + iconColor="white", + modal={ + "widgets": [ + title_widget(title="instances_delete_title"), # keep it (a18n) + text_widget(text="instances_delete_subtitle"), # keep it (a18n) + text_widget(bold=True, text=instance_name), + button_group_widget( + buttons=[ + button_widget( + id=f"close-delete-btn-{instance_name}", + text="action_close", # keep it (a18n) + color="close", + size="normal", + attrs={"data-close-modal": ""}, # a11y + )["data"], + button_widget( + id=f"delete-btn-{instance_name}", + text="action_delete", # keep it (a18n) + color="delete", + size="normal", + attrs={ + "data-submit-form": f"""{{ "instance_name" : "{instance_name}", "instance_hostname" : "{hostname}" }}""", + "data-submit-endpoint": "/delete", + }, + )["data"], + ] + ), + ], + }, + ), + ] + }, + }, + + +def instance_new_form() -> list: + return [ + input_widget( + id="instance-name", + name="instance-name", + label="instances_name", # keep it (a18n) + value="", + pattern="", # add your pattern if needed + columns={"pc": 3, "tablet": 4, " mobile": 12}, + ), + input_widget( + id="instance-hostname", + name="instance-hostname", + label="instances_hostname", # keep it (a18n) + value="", + pattern="", # add your pattern if needed + columns={"pc": 3, "tablet": 4, " mobile": 12}, + ), + button_widget( + id="create-instance", + text="action_create", # keep it (a18n) + color="success", + size="normal", + ), + ] + + +def instances_tabs(): + return [ + button_widget( + text="instances_tab_list", + display=["main", 1], + size="tab", + iconColor="white", + iconName="list", + ), + button_widget( + text="instances_tab_add", + display=["main", 2], + size="tab", + iconColor="white", + iconName="plus", + ), + ] + + +def instances_builder(instances: list, types: list = [], methods: list = []) -> list: + items = [] + for instance in instances: + items.append( + instance_item( + instance_name=instance.get("name"), + hostname=instance.get("hostname"), + instance_type=instance.get("type"), + method=instance.get("method"), + creation_date=instance.get("creation_date"), + last_seen=instance.get("last_seen"), + ) + ) + + return [ + # Tabs is button group with display value and a size tab inside a tabs container + { + type: "tabs", + "widgets": [button_group_widget(buttons=instances_tabs())], + }, + { + "type": "card", + "display": ["main", 1], + "widgets": [ + tabulator_widget( + id="table-instances", + columns=columns, + items=items, + filters=instances_filter(types=types, methods=methods), + ) + ], + }, + { + "type": "card", + "display": ["main", 2], + "widgets": instance_new_form(), + }, + ] diff --git a/src/ui/client/dashboard/components/Icons/Box.vue b/src/ui/client/dashboard/components/Icons/Box.vue index be4a65444..9cb69a9f3 100644 --- a/src/ui/client/dashboard/components/Icons/Box.vue +++ b/src/ui/client/dashboard/components/Icons/Box.vue @@ -8,6 +8,7 @@ import { defineProps, reactive } from "vue"; * color: 'info', * } * @param {string} [iconClass="icon-default"] - The class of the icon. + * @param {any} [value=""] - Attach a value to icon. Useful on some cases like table filtering using icons. * @param {string} [color="dark"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker'). * @param {boolean} [disabled=false] - If true, the icon will be disabled. */ @@ -18,6 +19,11 @@ const props = defineProps({ required: false, default: "icon-default", }, + value: { + type: [String, Number], + required: false, + default: "", + }, color: { type: String, required: false, @@ -33,6 +39,7 @@ const icon = reactive({