diff --git a/jupyterhub_custom_config.yaml b/jupyterhub_custom_config.yaml index 1ffbd8ae08f5d733d1c68cbc0e417125355e0fb0..9075ce77f6e3257d022bd88949d22c2e299df0f8 100644 --- a/jupyterhub_custom_config.yaml +++ b/jupyterhub_custom_config.yaml @@ -223,11 +223,13 @@ services: show: true type: dropdown options: - values_func: get_versions + values_func: get_version invalidFeedback: "Please choose a Version." label: type: text value: "Versionss" + onChangeUpdateKeys: + - system - type: hr allowedGroups: - default @@ -259,23 +261,47 @@ services: options: - key: kernel type: multiple_checkboxes - values_func: nav_default_env_kernel show: true + options: + values_func: nav_default_env_kernel + args: + - "4.2" + label: + type: header + value: Kernels - type: hr - key: extensions type: multiple_checkboxes - values_func: nav_default_env_extensions show: true + options: + values_func: nav_default_env_extensions + args: + - "4.2" + label: + type: header + value: Extensions - type: hr - key: proxies type: multiple_checkboxes - values_func: nav_default_env_proxies show: true + options: + values_func: nav_default_env_proxies + args: + - "4.2" + label: + type: header + value: Proxies - type: hr - key: helper type: multiple_checkboxes - values_func: nav_default_env_selectall show: true + options: + values_func: nav_default_env_selectall + label: + type: header + value: "" + script: + values_func: nav_default_env_selectall_script resources: show: type: function @@ -400,47 +426,48 @@ services: options: text: "Available Flavors" values_func: get_flavorsummary - "3.6": - name: "JupyterLab - 3.6" - kernelSet: "3.6" - extensionSet: "3.6" - proxySet: "3.6" - allowedLists: - groups: - - default - systems: - - JURECA - - JUWELS - - JUSUF - - DEEP - - JSC-Cloud - custom: - name: "Custom Docker image (starting a Jupyter server)" - userInput: - required: true - defaultMountEnabled: true - defaultMountPath: "/mnt/userdata" - allowedLists: - groups: - - default - systems: - - JSC-Cloud - - LRZ-Staging - - LRZ - - MPCDF - - LocalDummy - - Dummy - "repo2docker": - name: "Repo2docker (Binder) - beta" - userInput: - isRepo2Docker: true - allowedLists: - groups: - - default - systems: - - JSC-Cloud - - LocalDummy - - Dummy +stuff: + "3.6": + name: "JupyterLab - 3.6" + kernelSet: "3.6" + extensionSet: "3.6" + proxySet: "3.6" + allowedLists: + groups: + - default + systems: + - JURECA + - JUWELS + - JUSUF + - DEEP + - JSC-Cloud + custom: + name: "Custom Docker image (starting a Jupyter server)" + userInput: + required: true + defaultMountEnabled: true + defaultMountPath: "/mnt/userdata" + allowedLists: + groups: + - default + systems: + - JSC-Cloud + - LRZ-Staging + - LRZ + - MPCDF + - LocalDummy + - Dummy + "repo2docker": + name: "Repo2docker (Binder) - beta" + userInput: + isRepo2Docker: true + allowedLists: + groups: + - default + systems: + - JSC-Cloud + - LocalDummy + - Dummy systems: JUWELS: interactivePartitions: diff --git a/static/js/home/dropdown-options.js b/static/js/home/dropdown-options.js index 2f8f77ea5a35a454c529e8947414c91c6225c4e3..eb50a409c2ad364e1782c2b9744e6031fb4d5811 100644 --- a/static/js/home/dropdown-options.js +++ b/static/js/home/dropdown-options.js @@ -487,6 +487,9 @@ define(["jquery", "home/utils"], function ( } var updateLabConfigSelect = function (select, value, lastSelected) { + } + + var updateLabConfigSelect2 = function (select, value, lastSelected) { // For some systems, e.g. cloud, some options are not available if (select.html() == "") { select.append("<option disabled>Not available</option>"); diff --git a/static/js/home/handle-events.js b/static/js/home/handle-events.js index 929ba97c8fa732291f51b24362f153247dcdc6fc..5376b14530c5c9fd4388b5e20bf590cd4bdafd90 100644 --- a/static/js/home/handle-events.js +++ b/static/js/home/handle-events.js @@ -174,7 +174,7 @@ require(["jquery", "home/utils", "home/dropdown-options"], function ( } } - /* $("select[id*=version]").change(function () { + $("select[id*=verfffsion]").change(function () { const id = utils.getId(this); const values = utils.getLabConfigSelectValues(id); if (!$(this).hasClass("no-update")) { @@ -191,7 +191,7 @@ require(["jquery", "home/utils", "home/dropdown-options"], function ( const userInputInfo = (serviceInfo.JupyterLab.options[values.service] || {}).userInput || {}; _toggle_show_customImage(id, userInputInfo); _toggle_show_repo2Docker(id); - }); */ + }); $("select[id*=type]").change(function () { const id = utils.getId(this); diff --git a/templates/home.html b/templates/home.html index 080c6872a0bcb240a7c5a3acc425956e4f420ea7..cf60188ecf2d6ed5f8885ffb99adb24afdd013f5 100644 --- a/templates/home.html +++ b/templates/home.html @@ -60,7 +60,7 @@ {#- TABLE BODY #} <tbody> {#- New JupyterLab row #} - <tr data-server-id="{{ software }}-{{ new }}" class="new-spawner-tr summary-tr"> + <tr data-server-id="{{ software_prefix }}-{{ new }}" class="new-spawner-tr summary-tr"> <th scope="col" class="details-td"> <div class="d-flex mx-4"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-plus-lg m-auto" viewBox="0 0 16 16"> @@ -73,12 +73,12 @@ {{ home.create_collapsible_tr(software_key, software_prefix, None, {}, custom_config, lab_id=new) }} {#- Existing JupyterLab rows -#} {#- By looping through lab_spawners #} - {%- for spawner in lab_spawners -%} + {# {%- for spawner in lab_spawners -%} {%- set spawner_name = user.spawners[spawner.name].name -%} {%- set user_options = spawner.user_options -%} {{ home.create_summary_tr(software_key, software_prefix, spawner_name, user_options) }} {{ home.create_collapsible_tr(software_key, software_prefix, spawner_name, user_options, custom_config) }} - {%- endfor %} + {%- endfor %} #} </tbody> </table> </div> {#- table responsive #} @@ -335,10 +335,19 @@ require(["jquery", "jhapi", "home/utils", "home/dropdown-options", "home/lab-con {# For new labs, only the first option has to be updated manually. All other updates will be triggered via onChange and set default values. -#} - - // The jinja2 variable "new" points to the id of a new JupyterLab, e.g. "new-jupyterlab" - dropdowns.updateServices("{{ software_key }}", "{{ software }}-{{ new }}"); - $("#{{ software }}-{{ new }}-log-select").prepend(`<option value="latest">latest</option>`); + + {# let select = $("#jupyterlab-new-4.2-labconfig-versioncfg-select"); #} + let select = $("select[id*=versioncfg]"); + {# TODO -> use defaultOption of this software + -> trigger next changes via config #} + console.log(select); + select.val("4.2"); + select.trigger("change"); + console.log(select); + console.log(select.val()); + {# dropdowns.updateServices("{{ software_key }}", "{{ software }}-{{ new }}"); #} + + {# $("#{{ software }}-{{ new }}-log-select").prepend(`<option value="latest">latest</option>`); #} } // Remove all tab warnings since initial changes shouldn't cause warnings diff --git a/templates/macros/custom.jinja b/templates/macros/custom.jinja index 917f6d5cf7c3387dfa0fb99c36c737959edb22c8..37024bcdc39a6f06a10032f8e4418f19bbcbeba7 100644 --- a/templates/macros/custom.jinja +++ b/templates/macros/custom.jinja @@ -1,347 +1,121 @@ {%- import "macros/svgs.jinja" as svg -%} -{%- macro nav_default_env_kernel(custom_config) -%} -<div class="alert bg-info alert-dismissible fade show" style="color: #023d6b;" role="alert"> - <h4 class="alert-heading"> - {{ svg.announcement_svg | safe }} - <span class="align-middle"> {{ custom_config.get("announcement", {}).get("title", "") | safe }} </span> - </h4> - {{ custom_config.get("announcement", {}).get("body", "") | safe }} - <button type="button" class="btn-close" data-bs-dismiss="alert"></button> -</div> +{%- macro nav_default_env_kernel(id, custom_config, kernel_set) -%} +{%- for kernel, options in custom_config.get("userModules", {}).get("kernels", {}).items() %} + {%- if kernel_set in options.get("sets", []) %} + <div id="{{id}}-{{kernel}}-cb-div" class="form-check col-sm-6 col-md-4 col-lg-3"> + <input type="checkbox" class="form-check-input" id="{{id}}-{{kernel}}-check" value="{{kernel}}" {% if options.get("default", false) %} checked {% endif %}> + <label class="form-check-label" for="{{id}}-{{kernel}}-check"> + <span class="align-middle">{{options.displayName}}</span> + <a href="{{options.href}}" target="_blank" class="module-info text-muted ms-3"> + <span>{{ svg.info_svg | safe }}</span> + <div class="module-info-link-div d-inline-block"> + <span class="module-info-link" id="{{kernel}}-info-link"> + {{ svg.link_svg | safe }} + </span> + </div> + </a> + </label> + </div> + {%- endif %} +{%- endfor %} {%- endmacro -%} -{%- macro create_summary_tr(spawner_name, user_options, lab_id="", share_structure=false) -%} +{%- macro nav_default_env_extensions(id, custom_config, extension_set) -%} +<p>Extensions: {{id}}</p> +{%- endmacro -%} -{%- set name = user_options.get("name", "") -%} -{%- if lab_id != "" %} {%- set id = software ~ "-" ~ lab_id -%} -{%- elif spawner_name %} {%- set id = software ~ "-" ~ spawner_name -%} -{%- else %} {%- set id = software ~ "-new-jupyterlab" -%} -{%- endif -%} +{%- macro nav_default_env_proxies(id, custom_config, proxies_set) -%} +<p>Proxies: {{id}}</p> +{%- endmacro -%} -{%- set system = user_options.get("system", "") -%} -{%- set flavor = user_options.get("flavor", "") -%} -{%- set partition = user_options.get("partition", "") -%} -{%- set project = user_options.get("project", "") -%} -{%- set runtime = user_options.get("runtime", "") -%} -{%- set nodes = user_options.get("nodes", "") -%} -{%- set gpus = user_options.get("gpus", "") -%} -<tr data-server-id="{{id}}" class="summary-tr"> - <td class="details-td" data-bs-target="#{{id}}-collapse"> - <div class="d-flex mx-auto accordion-icon {% if not share_structure -%}collapsed {%- endif %}mx-4"></div> - </td> - <th scope="row" class="name-td">{{ name }}</th> - <td class="config-td"> - <div style="max-height: 152px; overflow: auto;"> - {%- macro _config_td_entry(label, value, alignment="start", key="") -%} - {%- if not key %} {% set key = label.lower() %} {% endif -%} - {%- set breakpoints = "col-12 col-lg-4" %} - <div id="{{id}}-config-td-div-{{key}}" - class="col text-lg-{{alignment}} {{breakpoints}}" - {% if not value %}style="display: none;"{% endif %}> - <span class="text-muted" style="font-size: smaller;">{{ label }}</span><br> - <span id="{{id}}-config-td-{{key}}">{{ value }}</span> - </div> - {%- endmacro -%} - {%- set row_margins = "mx-3 mb-1" -%} - {%- set row_breakpoints = "col-12 col-md-6 col-lg-12" -%} - <div class="row {{row_margins}} justify-content-between"> - <div class="row {{row_breakpoints}}"> - {{ _config_td_entry("System", system, "start") }} - {{ _config_td_entry("Flavor", flavor, "center") }} - {{ _config_td_entry("Partition", partition, "center") }} - {{ _config_td_entry("Project", project, "end") }} - </div> - <div class="row {{row_breakpoints}}"> - {{ _config_td_entry("Runtime (min)", runtime, key="runtime", alignment="start") }} - {{ _config_td_entry("Nodes", nodes, "center") }} - {{ _config_td_entry("GPUs", gpus, "end") }} - </div> - </div> - </div> - </td> - {%- if s %} - {{ create_lab_progress_td(id) }} - {{ create_action_td(id, s) }} - {%- endif -%} -</tr> +{%- macro nav_default_env_selectall(id, custom_config) -%} +<p>Select All: {{id}}</p> {%- endmacro -%} -{%- macro create_lab_progress_td(id) -%} -<td class="status-td"> - <div class="d-flex"> - <div class="d-flex flex-column"> - <div class="progress" style="height: 20px; min-width: 100px;"> - <div id="{{id}}-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"></div> - </div> - <span id="{{id}}-progress-info-text" class="progress-info-text text-center text-muted" style="font-size: smaller;"></span> - </div> - <div class="d-flex flex-column ms-3"> - <a role="button" class="log-info-btn lh-1 m-auto" style="padding-top: 1px;">{{ svg.logs_svg | safe }}</a> - <a role="button" class="log-info-text text-muted" style="font-size: smaller; text-decoration: none;">Logs</a> - </div> - </div> -</td> +{%- macro nav_default_env_selectall_script(id, custom_config) -%} +<p>Select All Script: {{id}}</p> {%- endmacro -%} -{%- macro create_action_td(id, s, share_structure=false, user_name="") -%} -<td class="actions-td" style="white-space: nowrap;"> - {%- set button_margin = "my-1 me-1" -%} - {#- Save N/A status to page #} - <span id="{{id}}-na-status" class="na-status d-none">0</span> - {#- Create and show appropriate buttons #} - <button type="button" id="{{id}}-na-btn" class="btn btn-secondary btn-na-lab disabled {{button_margin}}" style="display: none;"> - {{ svg.na_svg | safe }} N/A - </button> - <div id="{{id}}-na-info" class="text-muted my-auto" style="display: none; font-size: smaller; white-space: nowrap;"></div> - {%- if share_structure %} - <button type="button" id="{{id}}-start-btn" class="btn btn-primary btn-start-new-lab ms-auto"> {{ svg.start_svg | safe }} Start </button> - {%- else %} - <button type="button" id="{{id}}-start-btn" class="btn btn-primary btn-start-lab {{button_margin}}" - {% if s and (s.active and not s._stop_pending) -%} style="display: none;" {%- endif %}> - {{ svg.start_svg | safe }} Start - </button> - {%- endif %} - <a type="button" id="{{id}}-open-btn" class="btn btn-success btn-open-lab {{button_margin}}" href='/user/{% if s -%}{{s.user.name}}{%- else -%}{{ user_name }}{%- endif %}/{{id}}' target="_blank" - {% if (not s) or (not s.active or s._stop_pending) -%} style="display: none;" {%- endif %} > - {{ svg.open_svg | safe }} Open - </a> - <button type="button" id="{{id}}-cancel-btn" class="btn btn-danger btn-cancel-lab" - {% if (not s) or (not s.active or s._stop_pending) -%} style="display: none;" {%- endif %} - {% if (not s) or (s.active and s.pending == None) -%} style="display: none;" {%- endif -%}> - {{ svg.stop_svg | safe }} Cancel - </button> - <button type="button" id="{{id}}-stop-btn" class="btn btn-danger btn-stop-lab" - {% if (not s) or (not s.active or s._stop_pending) -%} style="display: none;" {%- endif %} - {% if (not s) or (s.active and s.pending != None) -%} style="display: none;" {%- endif -%}> - {{ svg.stop_svg | safe }} Stop - </button> -</td> +{%- macro get_default_env_runtime(id, custom_config) -%} +<p>Runtime: {{id}}</p> {%- endmacro -%} -{%- macro create_collapsible_tr(software, spawner_name, user_options, custom_config, lab_id="", share_structure=false) -%} +{%- macro get_default_env_runtime_label(id, custom_config) -%} +<p>Runtime Label: {{id}}</p> +{%- endmacro -%} -{%- set name = user_options.get("name", "") -%} -{%- set new_lab_id = "new-jupyterlab" %} -{%- if lab_id != "" %} {%- set id = software ~ "-" ~ lab_id -%} -{%- elif spawner %} {%- set id = software ~ "-" ~ spawner_name -%} -{%- else %} {%- set id = software ~ "-new-jupyterlab" -%} -{%- endif -%} +{%- macro get_default_env_gpus(id, custom_config) -%} +<p>GPUs: {{id}}</p> +{%- endmacro -%} -<tr data-server-id="{{id}}" class="collapsible-tr" style="--bs-table-accent-bg: transparent;"> - <td colspan="100%" class="p-0"> {#- Remove padding to hide td when collapsed #} - <div class="collapse{% if share_structure %} show {% endif %}" id="{{id}}-collapse"> - <div class="d-flex align-items-start m-3"> - {#- TAB NAV PILLS -#} - {%- set nav_tab_margins = "mb-3" %} - {#- TODO -> Tabs an linker Seite konfigurieren -#} - - <div class="nav flex-column nav-pills p-3 ps-0" id="{{ id }}-tab" role="tablist"> - <button class="nav-link active {{ nav_tab_margins }}" id="{{ id }}-config-tab" data-bs-toggle="pill" data-bs-target="#{{ id }}-config" type="button" role="tab">Lab Config</button> - <button class="nav-link {{ nav_tab_margins }}" id="{{ id }}-resources-tab" data-bs-toggle="pill" data-bs-target="#{{ id }}-resources" type="button" role="tab"> - <span>Resources</span> - <span id="{{id}}-resources-tab-warning" class="d-flex invisible"> - {{ svg.warning_svg | safe }} - <span class="visually-hidden">settings changed</span> - </span> - </button> - <button class="nav-link {{ nav_tab_margins }}" id="{{ id }}-modules-tab" data-bs-toggle="pill" data-bs-target="#{{ id }}-modules" type="button" role="tab"> - <span>Kernels and Extensions</span> - <span id="{{id}}-modules-tab-warning" class="d-flex invisible"> - {{ svg.warning_svg | safe }} - <span class="visually-hidden">settings changed</span> - </span> - </button> - {%- if id != new_lab_id %} - <button class="nav-link {{ nav_tab_margins }}" id="{{ id }}-logs-tab" data-bs-toggle="pill" data-bs-target="#{{ id }}-logs" type="button" role="tab">Logs</button> - {%- endif %} - </div> - {#- TAB NAV CONTENT -#} - {#- We only create empty elements here as they will be filled via JS #} - <div class="tab-content w-100" id="{{ id }}-tabContent"> - {#- Lab Config #} - <div class="tab-pane fade show active" id="{{ id }}-config" role="tabpanel"> - <form id="{{id}}-lab-config-form"> - {{ inputs.create_text_input(id, "name", placeholder="Give your lab a name") }} - {{ inputs.create_select(id, "version", custom_config.get("services").get("JupyterLab").get("optionsName", "Version")) }} - {#- Docker images #} - {%- if false %} - {%- call inputs.create_text_input(id, "image", - placeholder="e.g. jupyter/datascience-notebook", - pattern="(([A-Za-z0-9][A-Za-z0-9_.\-\/]*)?:?[A-Za-z0-9_.\-]+)?", - warning="Please enter a valid docker image, e.g. jupyter/datascience-notebook", - persistent_hover=True) %} - A public registry docker image starting a single-user notebook server. - <a href='https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html' target='_blank' style='color: white !important;'>Examples.</a> - {%- endcall %} - {#- Fields for private docker repository #} - {{ inputs.create_checkbox_input(id, "image-private-cb", "Private repository")}} - {%- call inputs.create_text_input(id, "image-private-url", "Image Repository", - placeholder="URL for your image registry", - pattern="^(([a-zA-Z0-9.\-]+)(:[0-9]+)?\/)?([a-zA-Z0-9._\-]+\/)*[a-zA-Z0-9._\-]+(:[a-zA-Z0-9._\-]+|@[A-Fa-f0-9]+(:[A-Fa-f0-9]+)*)?$", - warning="Please enter a valid docker repository URL") %} - {%- endcall %} - {%- call inputs.create_text_input(id, "image-private-user", "Username", - placeholder="Please enter your username") %} - Username for the private docker repository - {%- endcall %} - {{ inputs.create_password_input(id, "image-private-pass", "Password", placeholder="Please enter your password")}} - {#-----#} - {{ inputs.create_checkbox_input(id, "image-mount-cb", "Mount user data")}} - {%- call inputs.create_text_input(id, "image-mount", "User data path", - pattern="^\/[A-Za-z0-9\-\/]+", - warning="Please input a valid Unix-style path, e.g. /mnt/userdata") %} - Path to which your persistent user data will be mounted - {%- endcall %} - <hr> - {#- Repo2Docker fields #} - {{ inputs.create_select(id, key="type", label="Repository") }} - {{ inputs.create_text_input(id, key="repo", label="Repository URL", placeholder="GitHub repository name or URL") }} - {{ inputs.create_text_input(id, key="gitref", label="Git ref (branch,tag, or commit)", placeholder="HEAD") }} - {{ inputs.create_text_input(id, key="notebook", label="URL path to notebook (optional)", placeholder="URL path to notebook (optional)", clazz="optional") }} - {{ inputs.create_select(id, key="notebook_type", label="Notebook Type") }} +{%- macro get_default_env_gpus_label(id, custom_config) -%} +<p>GPUs Label: {{id}}</p> +{%- endmacro -%} - {#- Standard HPC system fields #} - {{ inputs.create_select(id, "system") }} - {{ inputs.create_select(id, "flavor") }} - <div id="{{id}}-flavor-legend-div" class="row align-items-center g-0 mt-4"> - <span class="col-4 fw-bold">Available Flavors</span> - <div class="col d-flex align-items-center ms-2"> - {%- set box_style = "height: 15px; width: 15px; border-radius: 0.25rem;"%} - <div style="{{box_style}} background-color: #198754;"></div> - <span class="ms-1">= Free</span> - <span class="mx-2"></span> - <div style="{{box_style}} background-color: #023d6b;"></div> - <span class="ms-1">= Used</span> - <span class="mx-2"></span> - <div style="{{box_style}} background-color: #dc3545;"></div> - <span class="ms-1">= Limit exceeded</span> - </div> - </div> - <div id="{{id}}-flavor-info-div" class="mb-3"></div> - {{ inputs.create_select(id, "account") }} - {{ inputs.create_select(id, "project") }} - {{ inputs.create_select(id, "partition") }} - <hr id="{{id}}-reservation-hr"> - {{ inputs.create_select(id, "reservation") }} - <div id="{{id}}-reservation-info-div" class="row mb-3"> - {%- set reservation_info_classes = "col-4 fw-bold"%} - <div id="{{id}}-reservation-info" class="col-8 offset-4"> - <div class="row"> - <span class="{{ reservation_info_classes }}">Start Time:</span> - <span id="{{id}}-reservation-start" class="col-auto"></span> - </div> - <div class="row"> - <span class="{{ reservation_info_classes }}">End Time:</span> - <span id="{{id}}-reservation-end" class="col-auto"></span> - </div> - <div class="row"> - <span class="{{ reservation_info_classes }}">State:</span> - <span id="{{id}}-reservation-state" class="col-auto"></span> - </div> - <div class="mt-1"> - <details> - <summary class="fw-bold">Detailed reservation information:</summary> - <pre id="{{id}}-reservation-details"></pre> - {#- TODO: Fix horizontal width upon expanding the detail #} - </details> - </div> - </div> - </div> - {%- endif %} - </form> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> - {#- Lab Resources #} - <div class="tab-pane fade" id="{{ id }}-resources" role="tabpanel"> - <form id="{{id}}-resources-form"> - {{ inputs.create_number_input(id, "nodes") }} - {{ inputs.create_number_input(id, "gpus", "GPUs") }} - {{ inputs.create_number_input(id, "runtime", "Runtime (minutes)") }} - {{ inputs.create_checkbox_input(id, "xserver-cb", "Activate XServer")}} - {{ inputs.create_number_input(id, "xserver", "Use XServer GPU Index") }} - </form> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> - {#- User-selected Modules #} - <div class="tab-pane fade" id="{{ id }}-modules" role="tabpanel"> - <form id="{{id}}-modules-form"> - {%- for module_set in custom_config.get("userModules", {}).keys() %} - <div id="{{id}}-{{module_set}}-div"> - <h4>{{ module_set | title}}</h4> - <div id="{{id}}-{{module_set}}-checkboxes-div" class="row g-0"></div> - </div> - {%- endfor %} - </form> - <hr> - <div id="{{id}}-modules-selector-div" class="row g-0"> - {%- set module_cols = "col-sm-6 col-md-4 col-lg-3" %} - <div class="form-check {{module_cols}}"> - <input class="form-check-input module-selector" type="checkbox" id="{{id}}-modules-select-all"> - <label class="form-check-label" for="{{id}}-modules-select-all">Select all</label> - </div> - <div class="form-check {{module_cols}}"> - <input class="form-check-input module-selector" type="checkbox" id="{{id}}-modules-select-none"> - <label class="form-check-label" for="{{id}}-modules-select-none">Deselect all</label> - </div> - </div> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> - {#- Lab Logs #} - <div class="tab-pane fade" id="{{ id }}-logs" role="tabpanel"> - {{ inputs.create_select(id, "log") }} - <div id="{{id}}-log" class="card card-body"></div> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> - </div> {#- End of tab content #} - </div> {#- End of d-flex #} - </div> {#- End of collapse #} - </td> -</tr> +{%- macro get_default_env_xserver(id, custom_config) -%} +<p>XServer: {{id}}</p> {%- endmacro -%} -{%- macro create_lab_config_buttons(id, start_button_only=false) -%} -<hr> -<div class="d-flex"> - {%- if start_button_only %} - <button type="button" id="{{id}}-share-btn" class="btn btn-share-lab" style="display: none;" data-toggle="modal" data-target="#{{ id }}-share-link">{{ svg.share_svg | safe }} Share </button> - <button type="button" id="{{id}}-start-btn" class="btn btn-success btn-start-new-lab ms-auto"> {{ svg.start_svg | safe }} Start </button> - <button type="button" id="{{id}}-na-btn" class="btn btn-secondary btn-na-lab disabled ms-auto me-2" style="display: none;"> - {{ svg.na_svg | safe }} N/A - </button> - <div id="{{id}}-na-info" class="text-muted my-auto" style="display: none; font-size: smaller;"></div> - {%- else %} - <button id="{{ id }}-share-btn" type="button" class="btn btn-share-lab me-2" style="display: none;" data-toggle="modal" data-target="#{{ id }}-share-link">{{ svg.share_svg | safe }} Share </button> - <button id="{{ id }}-save-btn" type="button" class="btn btn-success btn-save-lab me-2">{{ svg.save_svg | safe }} Save </button> - <button id="{{ id }}-reset-btn" type="button" class="btn btn-danger btn-reset-lab me-2">{{ svg.reset_svg | safe }} Reset</button> +{%- macro get_default_env_xserver_label(id, custom_config) -%} +<p>XServer Label: {{id}}</p> +{%- endmacro -%} - <div class="alert fade p-0 m-0" role="alert"> - <span class="align-middle"></span> - <button type="button" class="btn-close align-middle" onClick="$(this).parent().removeClass('show p-1').addClass('p-0');"></button> - </div> - <button type="button" id="{{id}}-delete-btn" class="btn btn-danger btn-delete-lab ms-auto"> {{ svg.delete_svg | safe }} Delete </button> - {%- endif %} - <!-- Modal --> - <div class="modal fade" id="{{ id }}-share-link" role="dialog" tabindex="-1"> - <div class="modal-dialog modal-dialog-centered"> +{%- macro get_version(id, custom_config) -%} +<p>Version: {{id}}</p> +{%- endmacro -%} - <!-- Modal content--> - <div class="modal-content"> - <div class="modal-header"> - <h4 class="modal-title">Share Lab</h4> - <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"> - </div> - <div class="modal-body"> - <p>Share your lab via URL</p> - <a href=""></a> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button> - <button type="button" id="{{id}}-copy-btn" class="btn btn-outline-primary" data-bs-toggle="tooltip" data-bs-placement="top" title="Copy to clipboard">Copy</button> - </div> - </div> +{%- macro get_hpc_system(id, custom_config) -%} +<p>HPC System: {{id}}</p> +{%- endmacro -%} - </div> - </div> -</div> +{%- macro get_hpc_account(id, custom_config) -%} +<p>HPC Account: {{id}}</p> +{%- endmacro -%} + +{%- macro get_hpc_project(id, custom_config) -%} +<p>HPC Project: {{id}}</p> +{%- endmacro -%} + +{%- macro get_hpc_partition(id, custom_config) -%} +<p>HPC Partition: {{id}}</p> {%- endmacro -%} + +{%- macro get_hpc_reservation(id, custom_config) -%} +<p>HPC Reservation: {{id}}</p> +{%- endmacro -%} + +{%- macro get_hpc_reservationsummary(id, custom_config) -%} +<p>HPC Reservation Summary: {{id}}</p> +{%- endmacro -%} + +{%- macro get_hpc_flavor(id, custom_config) -%} +<p>HPC Flavor: {{id}}</p> +{%- endmacro -%} + +{%- macro get_hpc_flavorsummary(id, custom_config) -%} +<p>HPC Flavor Summary: {{id}}</p> +{%- endmacro -%} + +{% set values_funcs = { + "nav_default_env_kernel": nav_default_env_kernel, + "nav_default_env_extensions": nav_default_env_extensions, + "nav_default_env_proxies": nav_default_env_proxies, + "nav_default_env_selectall": nav_default_env_selectall, + "nav_default_env_selectall_script": nav_default_env_selectall_script, + "get_default_env_runtime": get_default_env_runtime, + "get_default_env_runtime_label": get_default_env_runtime_label, + "get_default_env_gpus": get_default_env_gpus, + "get_default_env_gpus_label": get_default_env_gpus_label, + "get_default_env_xserver": get_default_env_xserver, + "get_default_env_xserver_label": get_default_env_xserver_label, + "get_version": get_version, + "get_hpc_system": get_hpc_system, + "get_hpc_account": get_hpc_account, + "get_hpc_project": get_hpc_project, + "get_hpc_partition": get_hpc_partition, + "get_hpc_reservation": get_hpc_reservation, + "get_hpc_reservationsummary": get_hpc_reservationsummary, + "get_hpc_flavor": get_hpc_flavor, + "get_hpc_flavorsummary": get_hpc_flavorsummary +} %} diff --git a/templates/macros/home.jinja b/templates/macros/home.jinja index 79213a849df046ffed5ded4cd02f546bfa372e79..b1c6ba7913268b259861c57c79e7384d7b6b9bca 100644 --- a/templates/macros/home.jinja +++ b/templates/macros/home.jinja @@ -145,8 +145,9 @@ This will be updated, everytime an event is triggered. #} {%- for navsidebar in custom_config.services.get(software_key, {}).get("frontend", {}).get("navsidebar", []) -%} + {%- set counter = loop.index0 -%} {%- for key, options in navsidebar.items() -%} - <button class="d-none nav-link {{ nav_tab_margins }}" id="{{ id }}-{{ key }}-tab" data-bs-toggle="pill" data-bs-target="#{{ id }}-{{ key }}" type="button" role="tab"> + <button class="nav-link {{ nav_tab_margins }} {% if counter > 0 %}d-none{% endif %}" id="{{ id }}-{{ key }}-tab" data-bs-toggle="pill" data-bs-target="#{{ id }}-{{ key }}" type="button" role="tab"> <span>{{ options.label }}</span> <span id="{{id}}-resources-tab-warning" class="d-flex invisible"> {{ svg.warning_svg | safe }} @@ -165,23 +166,22 @@ {#- TAB NAV CONTENT -#} {#- We create all elements for all tabs, but everything will be hidden. It will be filled with values later via JS, same goes for display/hidden #} - <div class="tab-content w-100" id="{{ id }}-tabContent"> {# Create a block for each version of the software, always start with the general configuration of the software We only add `tabs` which are also available in `navsidecar` #} {%- for version_name, version_options in custom_config.services.get(software_key, {}).get("options", {}).items() %} {%- for nav_item in custom_config.services.get(software_key, {}).get("frontend", {}).get("navsidebar", []) %} + {%- set counter = loop.index0 -%} {%- for key in nav_item.keys() %} {%- set _id = id ~ "-" ~ version_name ~ "-" ~ key %} - {# ----------- ADD d-none class in here later TODO ---------------- #} - <div class="tab-pane fade show active" id="{{ _id }}" role="tabpanel"> + <div class="{% if counter > 0 %}d-none{% endif %} tab-pane fade show active" id="{{ _id }}" role="tabpanel"> <form id="{{ _id }}-form"> {# This blocks comes from the software.frontend configuration and is added to all versions #} {%- for tabOption in custom_config.services.get(software_key, {}).get("frontend", {}).get("tabs", {}).get(key, []) %} {%- if tabOption.type == "inputtext" %} - {{ inputs.create_text_input_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label ) }} + {{ inputs.create_text_input_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label, true ) }} {%- elif tabOption.type == "dropdown" %} - {{ inputs.create_select_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label ) }} + {{ inputs.create_select_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label, true ) }} {%- elif tabOption.type == "hr" %} <hr> {%- endif %} @@ -191,131 +191,25 @@ In the end we can simply show the right div and hide the others #} {%- for tabOption in version_options.get("frontend", {}).get("tabs", {}).get(key, {}).get("options", []) %} {%- if tabOption.type == "dropdown" %} - {{ inputs.create_select_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label ) }} + {{ inputs.create_select_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label, false ) }} {%- elif tabOption.type == "flavorsummary" %} - {{ create_flavor_summary(_id ~ "-" ~ tabOption.key, tabOption.options ) }} + {{ create_flavor_summary(_id ~ "-" ~ tabOption.key, tabOption.options, false ) }} {%- elif tabOption.type == "number" %} - {{ inputs.create_number_input_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label ) }} + {{ inputs.create_number_input_2(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label, false ) }} {%- elif tabOption.type == "reservationsummary" %} - {{ create_reservation_summary(_id ~ "-" ~ tabOption.key) }} + {{ create_reservation_summary(_id ~ "-" ~ tabOption.key, false) }} + {%- elif tabOption.type == "multiple_checkboxes" %} + {{ inputs.create_multiple_checkboxes(_id ~ "-" ~ tabOption.key, tabOption.options, tabOption.label, custom_config, false ) }} {%- endif %} {%- endfor %} </form> {{ create_lab_config_buttons_2(_id, add_save_reset_buttons=true) }} {#- {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} #} </div> + {%- set first_navitem = false %} {%- endfor %} {%- endfor %} {%- endfor %} - - - {#- Lab Config #} - {#- Docker images #} - {%- if false %} - {{ inputs.create_text_input(id, "name", placeholder="Give your lab a name") }} - {{ inputs.create_select(id, "version", custom_config.get("services").get("JupyterLab").get("optionsName", "Version")) }} - {%- call inputs.create_text_input(id, "image", - placeholder="e.g. jupyter/datascience-notebook", - pattern="(([A-Za-z0-9][A-Za-z0-9_.\-\/]*)?:?[A-Za-z0-9_.\-]+)?", - warning="Please enter a valid docker image, e.g. jupyter/datascience-notebook", - persistent_hover=True) %} - A public registry docker image starting a single-user notebook server. - <a href='https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html' target='_blank' style='color: white !important;'>Examples.</a> - {%- endcall %} - {#- Fields for private docker repository #} - {{ inputs.create_checkbox_input(id, "image-private-cb", "Private repository")}} - {%- call inputs.create_text_input(id, "image-private-url", "Image Repository", - placeholder="URL for your image registry", - pattern="^(([a-zA-Z0-9.\-]+)(:[0-9]+)?\/)?([a-zA-Z0-9._\-]+\/)*[a-zA-Z0-9._\-]+(:[a-zA-Z0-9._\-]+|@[A-Fa-f0-9]+(:[A-Fa-f0-9]+)*)?$", - warning="Please enter a valid docker repository URL") %} - {%- endcall %} - {%- call inputs.create_text_input(id, "image-private-user", "Username", - placeholder="Please enter your username") %} - Username for the private docker repository - {%- endcall %} - {{ inputs.create_password_input(id, "image-private-pass", "Password", placeholder="Please enter your password")}} - {#-----#} - {{ inputs.create_checkbox_input(id, "image-mount-cb", "Mount user data")}} - {%- call inputs.create_text_input(id, "image-mount", "User data path", - pattern="^\/[A-Za-z0-9\-\/]+", - warning="Please input a valid Unix-style path, e.g. /mnt/userdata") %} - Path to which your persistent user data will be mounted - {%- endcall %} - <hr> - {#- Repo2Docker fields #} - {{ inputs.create_select(id, key="type", label="Repository") }} - {{ inputs.create_text_input(id, key="repo", label="Repository URL", placeholder="GitHub repository name or URL") }} - {{ inputs.create_text_input(id, key="gitref", label="Git ref (branch,tag, or commit)", placeholder="HEAD") }} - {{ inputs.create_text_input(id, key="notebook", label="URL path to notebook (optional)", placeholder="URL path to notebook (optional)", clazz="optional") }} - {{ inputs.create_select(id, key="notebook_type", label="Notebook Type") }} - - {#- Standard HPC system fields #} - {{ inputs.create_select(id, "system") }} - {{ inputs.create_select(id, "flavor") }} - <div id="{{id}}-flavor-legend-div" class="row align-items-center g-0 mt-4"> - <span class="col-4 fw-bold">Available Flavors</span> - <div class="col d-flex align-items-center ms-2"> - {%- set box_style = "height: 15px; width: 15px; border-radius: 0.25rem;"%} - <div style="{{box_style}} background-color: #198754;"></div> - <span class="ms-1">= Free</span> - <span class="mx-2"></span> - <div style="{{box_style}} background-color: #023d6b;"></div> - <span class="ms-1">= Used</span> - <span class="mx-2"></span> - <div style="{{box_style}} background-color: #dc3545;"></div> - <span class="ms-1">= Limit exceeded</span> - </div> - </div> - <div id="{{id}}-flavor-info-div" class="mb-3"></div> - {{ inputs.create_select(id, "account") }} - {{ inputs.create_select(id, "project") }} - {{ inputs.create_select(id, "partition") }} - <hr id="{{id}}-reservation-hr"> - {{ inputs.create_select(id, "reservation") }} - - {%- endif %} - {#- Lab Resources #} - <div class="tab-pane fade" id="{{ id }}-resources" role="tabpanel"> - <form id="{{id}}-resources-form"> - {{ inputs.create_number_input(id, "nodes") }} - {{ inputs.create_number_input(id, "gpus", "GPUs") }} - {{ inputs.create_number_input(id, "runtime", "Runtime (minutes)") }} - {{ inputs.create_checkbox_input(id, "xserver-cb", "Activate XServer")}} - {{ inputs.create_number_input(id, "xserver", "Use XServer GPU Index") }} - </form> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> - - {#- User-selected Modules #} - <div class="tab-pane fade" id="{{ id }}-modules" role="tabpanel"> - <form id="{{id}}-modules-form"> - {%- for module_set in custom_config.get("userModules", {}).keys() %} - <div id="{{id}}-{{module_set}}-div"> - <h4>{{ module_set | title}}</h4> - <div id="{{id}}-{{module_set}}-checkboxes-div" class="row g-0"></div> - </div> - {%- endfor %} - </form> - <hr> - <div id="{{id}}-modules-selector-div" class="row g-0"> - {%- set module_cols = "col-sm-6 col-md-4 col-lg-3" %} - <div class="form-check {{module_cols}}"> - <input class="form-check-input module-selector" type="checkbox" id="{{id}}-modules-select-all"> - <label class="form-check-label" for="{{id}}-modules-select-all">Select all</label> - </div> - <div class="form-check {{module_cols}}"> - <input class="form-check-input module-selector" type="checkbox" id="{{id}}-modules-select-none"> - <label class="form-check-label" for="{{id}}-modules-select-none">Deselect all</label> - </div> - </div> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> - {#- Lab Logs #} - <div class="tab-pane fade" id="{{ id }}-logs" role="tabpanel"> - {{ inputs.create_select(id, "log") }} - <div id="{{id}}-log" class="card card-body"></div> - {{ create_lab_config_buttons(id, id == new_lab_id or share_structure) }} - </div> </div> {#- End of tab content #} </div> {#- End of d-flex #} </div> {#- End of collapse #} @@ -348,8 +242,8 @@ </div> {%- endmacro %} -{%- macro create_reservation_summary(id) %} -<div id="{{id}}-reservation-info-div" class="row mb-3"> +{%- macro create_reservation_summary(id, show) %} +<div id="{{id}}-reservation-info-div" class="row mb-3{% if not show %} d-none{% endif %}"> {%- set reservation_info_classes = "col-4 fw-bold"%} <div id="{{id}}-reservation-info" class="col-8 offset-4"> <div class="row"> @@ -375,8 +269,8 @@ </div> {%- endmacro %} -{%- macro create_flavor_summary(id, options) -%} -<div id="{{id}}-flavor-legend-div" class="row align-items-center g-0 mt-4"> +{%- macro create_flavor_summary(id, options, show) -%} +<div id="{{id}}-flavor-legend-div" class="row align-items-center g-0 mt-4{% if not show %} d-none{% endif %}"> <span class="col-4 fw-bold">{{ options.text }}</span> <div class="col d-flex align-items-center ms-2"> {%- set box_style = "height: 15px; width: 15px; border-radius: 0.25rem;"%} diff --git a/templates/macros/inputs.jinja b/templates/macros/inputs.jinja index ad0d36c4857c7dc4e0a9bf5fcf40a991dd9900c0..3e9e431ee2d109ab124be57f7d39f867541bb5a6 100644 --- a/templates/macros/inputs.jinja +++ b/templates/macros/inputs.jinja @@ -1,4 +1,6 @@ {%- import "macros/svgs.jinja" as svg -%} +{%- import "macros/custom.jinja" as custom -%} + {%- macro create_label(id, label) -%} <label for="{{id}}-input" class="col-4 col-form-label"> {%- if label.type == "text" %} @@ -31,6 +33,8 @@ {%- if label.value is string %} {{ label.value }} {%- endif %} + {%- elif label.type == "header" %} + <h4>{{ label.value }}</h4> {%- endif %} </label> {%- endmacro -%} @@ -54,44 +58,11 @@ {%- endif %} {%- endmacro -%} -{%- macro create_text_input(id, key, label="", placeholder="", pattern="", warning="", persistent_hover=False, clazz="") -%} +{%- macro create_text_input_2(id, options, label, show) -%} {#- Macro to create a text input #} -{%- set key = key.lower() %} {#- We use a different margin than the other input elements since the warning text will already create a margin.#} -<div id="{{id}}-{{key}}-input-div" class="row mb-1"> - <label for="{{id}}-{{key}}-input" class="col-4 col-form-label"> - {{ label if label else key.capitalize() }} - {% if caller -%} - {% if persistent_hover -%} - <button type="button" class="btn" - data-bs-toggle="tooltip" data-bs-placement="right" data-bs-html="true" - title="{{ caller() }}"> - {{ svg.info_svg | safe }} - <span class="text-muted" style="font-size: smaller">(click me)</span> - </button> - {%- else %} - <a class="lh-1 ms-3" style="padding-top: 1px;" - data-bs-toggle="tooltip" data-bs-placement="right" data-bs-html="true" - title="{{ caller() }}"> - {{ svg.info_svg | safe }} - </a> - {%- endif %} - {%- endif %} - </label> - <div class="col-8"> - <input type="text" class="form-control {{clazz}}" id="{{id}}-{{key}}-input" - placeholder="{{placeholder}}" {% if pattern -%} pattern={{pattern}} {%- endif %}> - <div class="invalid-feedback">{{ warning }}</div> - </div> -</div> -{%- endmacro -%} - -{%- macro create_text_input_2(id, options, label) -%} -{#- Macro to create a text input #} -{#- We use a different margin than the other input elements -since the warning text will already create a margin.#} -<div id="{{id}}-input-div" class="row mb-1"> +<div id="{{id}}-input-div" class="row mb-1 {% if not show %}d-none{% endif %}"> {{ create_label(id, label) }} <div class="col-8"> <input type="text" class="form-control" id="{{id}}-input" @@ -99,6 +70,7 @@ since the warning text will already create a margin.#} <div class="invalid-feedback">{{ warning }}</div> </div> </div> +{{ create_label_scripts(id, label) }} {%- endmacro -%} @@ -125,19 +97,78 @@ since the warning text will already create a margin.#} </div> {%- endmacro -%} -{%- macro create_select_2(id, options, label) -%} -<div id="{{id}}-select-div" class="row mb-3"> +{%- macro create_password_input_2(id, options, label, show) -%} +<div id="{{id}}-input-div" class="row mb-1{% if not show %} d-none{% endif %}"> + {{ create_label(id, label) }} + <div class="col-8"> + <div class="input-group"> + <input type="password" class="form-control" id="{{id}}-input" + placeholder="{{ tabOption.get("placeholder", "") }}" {% if tabOption.pattern -%} pattern={{ tabOption.pattern }} {%- endif %}> + <span class="input-group-append"> + <button class="btn btn-light" type="button" id="{{id}}-view-password"> + <i id="{{id}}-password-eye" class="fa fa-eye" aria-hidden="true"></i> + </button> + </span> + <div class="invalid-feedback">{{ warning }}</div> + </div> + </div> +</div> +{{ create_label_scripts(id,label) }} +{%- endmacro -%} + +{%- macro create_select_2(id, options, label, show) -%} +<div id="{{id}}-select-div" class="row mb-3{% if not show %} d-none{% endif %}"> {{ create_label(id, label) }} <div class="col-8"> <select id="{{id}}-select" class="form-select" required> + <option value="{{id}}">{{id}}</option> + <option value="{{id}}">{{id[-9:]}}</option> + {% if id[-10:] == "versioncfg" %} + <option value="4.2">JupyterLab - 4.2</option> + {% endif %} </select> <div class="invalid-feedback">{{ options.get("invalidFeedback", "Input required") }}</div> </div> </div> +{{ create_label_scripts(id, label) }} +{{ create_select_onChange_scripts(id, options) }} +{%- endmacro -%} + +{%- macro create_select_onChange_scripts(id, options) -%} +<script> + require(["jquery", "/static/js/home/utils.js", "/static/js/home/dropdown-options.js"], function ( + $, + utils, + dropdowns + ) { + "use strict"; + const escapedId = "{{ id }}".replace(/\./g, '\\.'); + + $("select[id^=" + escapedId + "]").change(function () { + {# const values = utils.getLabConfigSelectValues(id); #} + console.log("change"); + {# if (!$(this).hasClass("no-update")) { + try { + dropdowns.updateSystems(id, values.service); + } + catch (e) { + utils.setLabAsNA(id, "due to a JS error"); + console.log(e); + } + } + + const serviceInfo = getServiceInfo(); + const userInputInfo = (serviceInfo.JupyterLab.options[values.service] || {}).userInput || {}; + _toggle_show_customImage(id, userInputInfo); + _toggle_show_repo2Docker(id); + #} + }); + }); +</script> {%- endmacro -%} -{%- macro create_number_input_2(id, options, label) -%} -<div id="{{id}}-input-div" class="row mb-3"> {# style="display: none; #} +{%- macro create_number_input_2(id, options, label, show) -%} +<div id="{{id}}-input-div" class="row mb-3{% if not show %} d-none{% endif %}"> {# style="display: none; #} {{ create_label(id, label) }} <div class="col-8"> <input type="number" id="{{id}}-input" class="form-control" value="{{ options.get("default", -1)}}"> @@ -190,5 +221,44 @@ since the warning text will already create a margin.#} <button id="{{ id }}-{{ text }}-btn" type="button" class="btn btn-success me-2"> {{text}} </button> {%- endmacro -%} -{%- macro create_multiple_checkboxes(id, value_sets_list) -%} -{%- endmacro -%} \ No newline at end of file + + +{%- macro create_multiple_checkboxes(id, options, label, custom_config, show) -%} +{#- User-selected Modules #} +<div id="{{ id }}-div" class="{% if not show %}d-none{% endif %}"> + {{ create_label(id, label) }} + <div id="{{ id }}-checkboxes-div" class="row g-0"> + {#{%- set func = custom.values_funcs.get(options.values_func) %} + {%- if func %} + {%- set args = [id, custom_config] + options.get("args", []) %} + {{ func(*args) }} + {%- endif %}#} + </div> +</div> +{%- endmacro %} +{# + {%- set moduleOptions = values_func[options.values_func](id) %} + <p>ModuleOpt: {{ moduleOptions }}</p> + {# {{ create_multiple_checkboxes_single(id, moduleOptions) }} + </div> +</div> + {%- for module_set in custom_config.get("userModules", {}).keys() %} + <div id="{{id}}-{{module_set}}-div"> + <h4>{{ module_set | title}}</h4> + <div id="{{id}}-{{module_set}}-checkboxes-div" class="row g-0"></div> + </div> + {%- endfor %} + <hr> + <div id="{{id}}-modules-selector-div" class="row g-0"> + {%- set module_cols = "col-sm-6 col-md-4 col-lg-3" %} + <div class="form-check {{module_cols}}"> + <input class="form-check-input module-selector" type="checkbox" id="{{id}}-modules-select-all"> + <label class="form-check-label" for="{{id}}-modules-select-all">Select all</label> + </div> + <div class="form-check {{module_cols}}"> + <input class="form-check-input module-selector" type="checkbox" id="{{id}}-modules-select-none"> + <label class="form-check-label" for="{{id}}-modules-select-none">Deselect all</label> + </div> + </div> + +{%- endmacro -%} #} \ No newline at end of file