mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-19 08:39:34 +00:00
Merge branch 'master' into ux-selectpanel
This commit is contained in:
commit
a43b64bd5c
339 changed files with 18550 additions and 6970 deletions
|
|
@ -1,4 +1,5 @@
|
|||
{% import "helper.twig" as helper %}
|
||||
{% import "vars.macro.twig" as vars %}
|
||||
{% import "components/search.macro.html.twig" as search %}
|
||||
|
||||
<nav class="navbar navbar-expand-md bg-body-tertiary border-bottom shadow-sm fixed-top py-0" id="navbar">
|
||||
|
|
@ -17,7 +18,7 @@
|
|||
</div>
|
||||
|
||||
<a class="navbar-brand" href="{{ path('homepage') }}"><i class="fa fa-microchip"
|
||||
aria-hidden="true"></i> {{ partdb_title }}</a>
|
||||
aria-hidden="true"></i> {{ vars.partdb_title() }}</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent"
|
||||
aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
|
||||
<div class="nav flex-column">
|
||||
{% for item in sidebar_items %}
|
||||
{{ tree.treeview_sidebar('sidebar-panel-' ~ loop.index, item) }}
|
||||
{% for item in settings_instance('sidebar').items %}
|
||||
{{ tree.treeview_sidebar('sidebar-panel-' ~ loop.index, item.value) }}
|
||||
<div class="mb-2"></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
{% extends "admin/base_admin.html.twig" %}
|
||||
|
||||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fa-solid fa-coins"></i> {% trans %}currency.caption{% endtrans %}
|
||||
{% endblock %}
|
||||
|
|
@ -20,8 +22,8 @@
|
|||
{{ form_row(form.exchange_rate) }}
|
||||
{% if entity.inverseExchangeRate %}
|
||||
<p class="form-text text-muted offset-3 col-9">
|
||||
{{ '1'|format_currency(default_currency) }} = {{ entity.inverseExchangeRate.tofloat | format_currency(entity.isoCode, {fraction_digit: 5}) }}<br>
|
||||
{{ '1'|format_currency(entity.isoCode) }} = {{ entity.exchangeRate.tofloat | format_currency(default_currency, {fraction_digit: 5}) }}
|
||||
{{ '1'|format_currency(vars.base_currency()) }} = {{ entity.inverseExchangeRate.tofloat | format_currency(entity.isoCode, {fraction_digit: 5}) }}<br>
|
||||
{{ '1'|format_currency(entity.isoCode) }} = {{ entity.exchangeRate.tofloat | format_currency(vars.base_currency(), {fraction_digit: 5}) }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app.request.locale | replace({"_": "-"}) }}"
|
||||
{# For the UX translator, just use the language part (before the _. should be 2 chars), otherwise it finds no translations #}
|
||||
|
|
@ -31,21 +33,19 @@
|
|||
<link rel="mask-icon" href="{{ asset('icon/safari-pinned-tab.svg') }}" color="#5bbad5">
|
||||
|
||||
{# The content block is already escaped. so we must not escape it again. #}
|
||||
<title>{% apply trim|raw %}{% block title %}{{ partdb_title }}{% endblock %}{% endapply %}</title>
|
||||
<title>{% apply trim|raw %}{% block title %}{{ vars.partdb_title() }}{% endblock %}{% endapply %}</title>
|
||||
{% set current_page_title = block("title")|raw %}
|
||||
{% block stylesheets %}
|
||||
{# Include the main bootstrap theme based on user/global setting #}
|
||||
|
||||
|
||||
|
||||
{% if not app.user.theme is defined or app.user.theme is null %}
|
||||
{% set theme = global_theme %}
|
||||
{% if app.user.theme is not defined or app.user.theme is null %}
|
||||
{% set theme = settings_instance('customization').theme %}
|
||||
{% else %}
|
||||
{% set theme = app.user.theme %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
{% if theme and theme in available_themes and encore_entry_exists('theme_' ~ theme) %}
|
||||
{{ encore_entry_link_tags('theme_' ~ theme) }}
|
||||
{% else %}
|
||||
|
|
@ -53,6 +53,14 @@
|
|||
{% endif %}
|
||||
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
|
||||
{% set table_settings = settings_instance('table') %}
|
||||
<style>
|
||||
:root {
|
||||
--table-image-preview-min-size: {{ table_settings.previewImageMinWidth }}px;
|
||||
--table-image-preview-max-size: {{ table_settings.previewImageMaxWidth }}px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
{% extends "bundles/TwigBundle/Exception/error.html.twig" %}
|
||||
|
||||
{% block status_comment %}
|
||||
Nice try! But you are not allowed to do this!
|
||||
Nice try! But you are not allowed to do this!<br>
|
||||
<code>{{ exception.message }}</code>
|
||||
<br> <small>If you think you should have access to this ressource, contact the adminstrator.</small>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<input type="hidden" name="ids" {{ stimulus_target('elements/datatables/parts', 'selectIDs') }} value="">
|
||||
|
||||
<div class="mb-2" {{ stimulus_target('elements/datatables/parts', 'selectPanel') }}>
|
||||
<div class="d-none mb-2 bg-body-tertiary shadow-sm border border-secondary rounded mx-2 p-2" {{ stimulus_target('elements/datatables/parts', 'selectPanel') }}>
|
||||
{# <span id="select_count"></span> #}
|
||||
|
||||
<div class="input-group">
|
||||
|
|
@ -95,4 +95,4 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read')],
|
||||
['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read')],
|
||||
['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read')],
|
||||
['devices', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')],
|
||||
['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')],
|
||||
['tools', path('tree_tools'), 'tools.label', true],
|
||||
] %}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
{% extends 'bootstrap_5_horizontal_layout.html.twig' %}
|
||||
|
||||
{%- block toggle_password_widget -%}
|
||||
<div class="{{ toggle_container_classes|join(' ') }}">{{ block('password_widget') }}</div>
|
||||
{%- endblock toggle_password_widget -%}
|
||||
|
||||
{# Make form rows smaller #}
|
||||
{% block form_row -%}
|
||||
{%- set row_attr = row_attr|merge({"class": "mb-2"}) -%}
|
||||
|
|
@ -139,4 +143,4 @@
|
|||
{% else %}
|
||||
{{- parent() -}}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
25
templates/form/settings_form.html.twig
Normal file
25
templates/form/settings_form.html.twig
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "form/extended_bootstrap_layout.html.twig" %}
|
||||
|
||||
{% block form_label %}
|
||||
{# If parameter_envvar exists on form then show it as tooltip #}
|
||||
{% if parameter_envvar is defined and parameter_envvar is not null %}
|
||||
{%- set label_attr = label_attr|merge({title: 'settings.tooltip.overrideable_by_env'|trans(arguments = {'%env%': (parameter_envvar)|trim})}) -%}
|
||||
{% endif %}
|
||||
{{- parent() -}}
|
||||
{% endblock %}
|
||||
|
||||
{% block checkbox_radio_label %}
|
||||
{# If parameter_envvar exists on form then show it as tooltip #}
|
||||
{% if parameter_envvar is defined and parameter_envvar is not null %}
|
||||
{%- set label_attr = label_attr|merge({title: 'settings.tooltip.overrideable_by_env'|trans(arguments = {'%env%': (parameter_envvar)|trim})}) -%}
|
||||
{% endif %}
|
||||
{{- parent() -}}
|
||||
{% endblock %}
|
||||
|
||||
{% block tristate_label %}
|
||||
{# If parameter_envvar exists on form then show it as tooltip #}
|
||||
{% if parameter_envvar is defined and parameter_envvar is not null %}
|
||||
{%- set label_attr = label_attr|merge({title: 'settings.tooltip.overrideable_by_env'|trans(arguments = {'%env%': (parameter_envvar)|trim})}) -%}
|
||||
{% endif %}
|
||||
{{- parent() -}}
|
||||
{% endblock %}
|
||||
|
|
@ -2,27 +2,25 @@
|
|||
|
||||
{% import "components/new_version.macro.html.twig" as nv %}
|
||||
{% import "components/search.macro.html.twig" as search %}
|
||||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if is_granted('@system.show_updates') %}
|
||||
{{ nv.new_version_alert(new_version_available, new_version, new_version_url) }}
|
||||
{% endif %}
|
||||
|
||||
{% block item_search %}
|
||||
{% if is_granted('@parts.read') %}
|
||||
{{ search.search_form("standalone") }}
|
||||
<div class="mb-2"></div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block item_banner %}
|
||||
<div class="rounded p-4 bg-body-secondary">
|
||||
<h1 class="display-3">{{ partdb_title }}</h1>
|
||||
<h4>
|
||||
{% trans %}version.caption{% endtrans %}: {{ shivas_app_version }}
|
||||
{% if git_branch is not empty or git_commit is not empty %}
|
||||
({{ git_branch ?? '' }}/{{ git_commit ?? '' }})
|
||||
{% endif %}
|
||||
</h4>
|
||||
<h1 class="display-3">{{ vars.partdb_title() }}</h1>
|
||||
{% if settings_instance('customization').showVersionOnHomepage %}
|
||||
<h4>
|
||||
{% trans %}version.caption{% endtrans %}: {{ shivas_app_version }}
|
||||
{% if git_branch is not empty or git_commit is not empty %}
|
||||
({{ git_branch ?? '' }}/{{ git_commit ?? '' }})
|
||||
{% endif %}
|
||||
</h4>
|
||||
{% endif %}
|
||||
{% if banner is not empty %}
|
||||
<hr>
|
||||
<div class="latex" data-controller="common--latex">
|
||||
|
|
@ -30,9 +28,11 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block item_first_steps %}
|
||||
{% if show_first_steps %}
|
||||
<div class="card border-info mt-3">
|
||||
<div class="card border-info">
|
||||
<div class="card-header bg-info ">
|
||||
<h4><i class="fa fa-circle-play fa-fw " aria-hidden="true"></i> {% trans %}homepage.first_steps.title{% endtrans %}</h4>
|
||||
</div>
|
||||
|
|
@ -50,8 +50,10 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="card border-primary mt-3">
|
||||
{% block item_license %}
|
||||
<div class="card border-primary">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4><i class="fa fa-book fa-fw" aria-hidden="true"></i> {% trans %}homepage.license{% endtrans %}</h4>
|
||||
</div>
|
||||
|
|
@ -67,9 +69,11 @@
|
|||
<strong><i class="fas fa-comments fa-fw"></i> {% trans %}homepage.forum.caption{% endtrans %}:</strong> {% trans with {'%href%': 'https://github.com/Part-DB/Part-DB-server/discussions'}%}homepage.forum.text{% endtrans %}<br>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block item_last_activity %}
|
||||
{% if datatable is not null %}
|
||||
<div class="card mt-3">
|
||||
<div class="card">
|
||||
<div class="card-header"><i class="fas fa-fw fa-history"></i> {% trans %}homepage.last_activity{% endtrans %}</div>
|
||||
<div class="card-body">
|
||||
{% import "components/history_log_macros.html.twig" as log %}
|
||||
|
|
@ -77,4 +81,23 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if is_granted('@system.show_updates') %}
|
||||
{{ nv.new_version_alert(new_version_available, new_version, new_version_url) }}
|
||||
{% endif %}
|
||||
|
||||
{% for item in settings_instance('customization').homepageitems %}
|
||||
{% if block('item_' ~ item.value) is defined %}
|
||||
{{ block('item_' ~ item.value) }}
|
||||
<div class="mb-2"></div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning mt-3" role="alert">
|
||||
Alert: The homepage item "{{ item.value }}" is not defined!
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
{% else %}
|
||||
{{ provider.providerInfo.name | trans }}
|
||||
{% endif %}
|
||||
|
||||
</h5>
|
||||
<div>
|
||||
{% if provider.providerInfo.description is defined and provider.providerInfo.description is not null %}
|
||||
|
|
@ -23,6 +22,11 @@
|
|||
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{% if provider.providerInfo.settings_class is defined %}
|
||||
<a href="{{ path('info_providers_provider_settings', {'provider': provider.providerKey}) }}" class="btn btn-primary btn-sm {% if not is_granted('@config.change_system_settings') %}disabled{% endif %}"
|
||||
title="{% trans %}info_providers.settings.title{% endtrans %}"
|
||||
><i class="fa-solid fa-cog"></i></a>
|
||||
{% endif %}
|
||||
{% for capability in provider.capabilities %}
|
||||
{# @var capability \App\Services\InfoProviderSystem\Providers\ProviderCapabilities #}
|
||||
<span class="badge text-bg-secondary">
|
||||
|
|
@ -52,4 +56,4 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
{% extends "main_card.html.twig" %}
|
||||
{% macro genId(widget) %}{{ widget.vars.full_name }}{% endmacro %}
|
||||
|
||||
{% form_theme form "form/settings_form.html.twig" %}
|
||||
|
||||
{% block title %}{% trans %}info_providers.settings.title{% endtrans %}: {{ info_provider_info.name }}{% endblock %}
|
||||
|
||||
{% block card_title %}<i class="fa-solid fa-gear fa-fw"></i> {% trans %}info_providers.settings.title{% endtrans %}: <b>{{ info_provider_info.name }}</b>{% endblock %}
|
||||
|
||||
{% block card_content %}
|
||||
<div class="offset-sm-3">
|
||||
<h3>
|
||||
{% if info_provider_info.url %}
|
||||
<a href="{{ info_provider_info.url }}" class="link-external" target="_blank" rel="nofollow">{{ info_provider_info.name }}</a>
|
||||
{% else %}
|
||||
{{ info_provider_info.name }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
{% if info_provider_info.description %}
|
||||
<p class="text-muted">{{ info_provider_info.description }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{{ form_start(form) }}
|
||||
<div class="row">
|
||||
<div class="offset-sm-3 col mb-3 ps-2">
|
||||
<b>{{ form_help(form) }}</b>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
|
|
@ -100,6 +100,10 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if form.update_profile is defined %}
|
||||
{{ form_row(form.update_profile) }}
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<div class="input-group">
|
||||
|
|
@ -133,4 +137,4 @@
|
|||
</object>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ meta_title }}</title>
|
||||
<meta name="author" content="{{ partdb_title }}">
|
||||
<meta name="author" content="{{ vars.partdb_title() }}">
|
||||
<meta name="description" content="Label for {{ meta_title }}">
|
||||
<meta name="keywords" content="Part-DB, Label, Barcode">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
{% apply inky_to_html|inline_css(source('@css/email/foundation-emails.css'), source('@css/email/email.css')) %}
|
||||
|
||||
<container>
|
||||
|
|
@ -5,7 +7,7 @@
|
|||
<row class="header">
|
||||
<columns>
|
||||
<spacer size="16"></spacer>
|
||||
<h4 class="text-center"><a href="{{ url('homepage') }}">{{ partdb_title }}</a></h4>
|
||||
<h4 class="text-center"><a href="{{ url('homepage') }}">{{ vars.partdb_title() }}</a></h4>
|
||||
</columns>
|
||||
</row>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<div class="carousel-item {% if loop.first %}active{% endif %}">
|
||||
<a href="{{ entity_url(pic, 'file_view') }}" data-turbo="false" target="_blank" rel="noopener">
|
||||
<img class="d-block w-100 img-fluid img-thumbnail bg-light part-info-image" src="{{ entity_url(pic, 'file_view') }}" alt="">
|
||||
{% if img_overlay %}
|
||||
{% if settings_instance("part_info").showPartImageOverlay %}
|
||||
<div class="mask"></div>
|
||||
<div class="carousel-caption-hover">
|
||||
<div class="carousel-caption text-white">
|
||||
|
|
|
|||
186
templates/projects/_bom_validation_results.html.twig
Normal file
186
templates/projects/_bom_validation_results.html.twig
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
{# BOM Validation Results Component #}
|
||||
{#
|
||||
Usage:
|
||||
{% include 'projects/_bom_validation_results.html.twig' with {
|
||||
validation_result: validation_result,
|
||||
show_summary: true,
|
||||
show_details: true
|
||||
} %}
|
||||
#}
|
||||
|
||||
{% if validation_result is defined and validation_result is not empty %}
|
||||
{% set stats = validation_result %}
|
||||
|
||||
{# Validation Summary #}
|
||||
{% if show_summary is defined and show_summary %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fa-solid fa-chart-bar fa-fw"></i>
|
||||
{% trans %}project.bom_import.validation.summary{% endtrans %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="h3 text-primary">{{ stats.total_entries }}</div>
|
||||
<small class="text-muted">{% trans %}project.bom_import.validation.total_entries{% endtrans %}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="h3 text-success">{{ stats.valid_entries }}</div>
|
||||
<small class="text-muted">{% trans %}project.bom_import.validation.valid_entries{% endtrans %}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="h3 text-warning">{{ stats.invalid_entries }}</div>
|
||||
<small class="text-muted">{% trans %}project.bom_import.validation.invalid_entries{% endtrans %}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-center">
|
||||
<div class="h3 text-info">
|
||||
{% if stats.total_entries > 0 %}
|
||||
{{ ((stats.valid_entries / stats.total_entries) * 100) | round(1) }}%
|
||||
{% else %}
|
||||
0%
|
||||
{% endif %}
|
||||
</div>
|
||||
<small class="text-muted">{% trans %}project.bom_import.validation.success_rate{% endtrans %}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Validation Messages #}
|
||||
{% if validation_result.errors is defined and validation_result.errors is not empty %}
|
||||
<div class="alert alert-danger">
|
||||
<h4><i class="fa-solid fa-exclamation-triangle fa-fw"></i> {% trans %}project.bom_import.validation.errors.title{% endtrans %}</h4>
|
||||
<p class="mb-2">{% trans %}project.bom_import.validation.errors.description{% endtrans %}</p>
|
||||
<ul class="mb-0">
|
||||
{% for error in validation_result.errors %}
|
||||
<li>{{ error|raw }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if validation_result.warnings is defined and validation_result.warnings is not empty %}
|
||||
<div class="alert alert-warning">
|
||||
<h4><i class="fa-solid fa-exclamation-circle fa-fw"></i> {% trans %}project.bom_import.validation.warnings.title{% endtrans %}</h4>
|
||||
<p class="mb-2">{% trans %}project.bom_import.validation.warnings.description{% endtrans %}</p>
|
||||
<ul class="mb-0">
|
||||
{% for warning in validation_result.warnings %}
|
||||
<li>{{ warning|raw }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if validation_result.info is defined and validation_result.info is not empty %}
|
||||
<div class="alert alert-info">
|
||||
<h4><i class="fa-solid fa-info-circle fa-fw"></i> {% trans %}project.bom_import.validation.info.title{% endtrans %}</h4>
|
||||
<ul class="mb-0">
|
||||
{% for info in validation_result.info %}
|
||||
<li>{{ info|raw }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Detailed Line-by-Line Results #}
|
||||
{% if show_details is defined and show_details and validation_result.line_results is defined %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fa-solid fa-list fa-fw"></i>
|
||||
{% trans %}project.bom_import.validation.details.title{% endtrans %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans %}project.bom_import.validation.details.line{% endtrans %}</th>
|
||||
<th>{% trans %}project.bom_import.validation.details.status{% endtrans %}</th>
|
||||
<th>{% trans %}project.bom_import.validation.details.messages{% endtrans %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for line_result in validation_result.line_results %}
|
||||
<tr class="{% if line_result.is_valid %}table-success{% else %}table-danger{% endif %}">
|
||||
<td>
|
||||
<strong>{{ line_result.line_number }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
{% if line_result.is_valid %}
|
||||
<span class="badge bg-success">
|
||||
<i class="fa-solid fa-check fa-fw"></i>
|
||||
{% trans %}project.bom_import.validation.details.valid{% endtrans %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">
|
||||
<i class="fa-solid fa-times fa-fw"></i>
|
||||
{% trans %}project.bom_import.validation.details.invalid{% endtrans %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if line_result.errors is not empty %}
|
||||
<div class="text-danger">
|
||||
{% for error in line_result.errors %}
|
||||
<div><i class="fa-solid fa-exclamation-triangle fa-fw"></i> {{ error|raw }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if line_result.warnings is not empty %}
|
||||
<div class="text-warning">
|
||||
{% for warning in line_result.warnings %}
|
||||
<div><i class="fa-solid fa-exclamation-circle fa-fw"></i> {{ warning|raw }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if line_result.info is not empty %}
|
||||
<div class="text-info">
|
||||
{% for info in line_result.info %}
|
||||
<div><i class="fa-solid fa-info-circle fa-fw"></i> {{ info|raw }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Action Buttons #}
|
||||
{% if validation_result.is_valid is defined %}
|
||||
<div class="mt-3">
|
||||
{% if validation_result.is_valid %}
|
||||
<div class="alert alert-success">
|
||||
<i class="fa-solid fa-check-circle fa-fw"></i>
|
||||
{% trans %}project.bom_import.validation.all_valid{% endtrans %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-danger">
|
||||
<i class="fa-solid fa-exclamation-triangle fa-fw"></i>
|
||||
{% trans %}project.bom_import.validation.fix_errors{% endtrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
204
templates/projects/import_bom_map_fields.html.twig
Normal file
204
templates/projects/import_bom_map_fields.html.twig
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
{% extends "main_card.html.twig" %}
|
||||
|
||||
{% block title %}{% trans %}project.bom_import.map_fields{% endtrans %}{% endblock %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fa-solid fa-arrows-left-right fa-fw"></i>
|
||||
{% trans %}project.bom_import.map_fields{% endtrans %}{% if project %}: <i>{{ project.name }}</i>{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card_content %}
|
||||
{% if validation_result is defined %}
|
||||
{% include 'projects/_bom_validation_results.html.twig' with {
|
||||
validation_result: validation_result,
|
||||
show_summary: true,
|
||||
show_details: false
|
||||
} %}
|
||||
{% endif %}
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<i class="fa-solid fa-info-circle fa-fw"></i>
|
||||
{% trans %}project.bom_import.map_fields.help{% endtrans %}
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
<i class="fa-solid fa-lightbulb fa-fw"></i>
|
||||
{% trans %}project.bom_import.field_mapping.priority_note{% endtrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form_start(form) }}
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
{{ form_row(form.delimiter) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fa-solid fa-table-columns fa-fw"></i>
|
||||
{% trans %}project.bom_import.field_mapping.title{% endtrans %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans %}project.bom_import.field_mapping.csv_field{% endtrans %}</th>
|
||||
<th>{% trans %}project.bom_import.field_mapping.maps_to{% endtrans %}</th>
|
||||
<th>{% trans %}project.bom_import.field_mapping.suggestion{% endtrans %}</th>
|
||||
<th>{% trans %}project.bom_import.field_mapping.priority{% endtrans %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for field in detected_fields %}
|
||||
<tr>
|
||||
<td>
|
||||
<code>{{ field }}</code>
|
||||
</td>
|
||||
<td>
|
||||
{{ form_widget(form['mapping_' ~ field_name_mapping[field]], {
|
||||
'attr': {
|
||||
'class': 'form-select field-mapping-select',
|
||||
'data-field': field
|
||||
}
|
||||
}) }}
|
||||
</td>
|
||||
<td>
|
||||
{% if suggested_mapping[field] is defined %}
|
||||
<span class="badge bg-success">
|
||||
<i class="fa-solid fa-magic fa-fw"></i>
|
||||
{{ suggested_mapping[field] }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-muted">
|
||||
<i class="fa-solid fa-question fa-fw"></i>
|
||||
{% trans %}project.bom_import.field_mapping.no_suggestion{% endtrans %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<input type="number"
|
||||
class="form-control form-control-sm priority-input"
|
||||
min="1"
|
||||
value="10"
|
||||
style="width: 80px;"
|
||||
data-field="{{ field }}"
|
||||
title="{% trans %}project.bom_import.field_mapping.priority_help{% endtrans %}">
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<h6>{% trans %}project.bom_import.field_mapping.summary{% endtrans %}:</h6>
|
||||
<div id="mapping-summary" class="alert alert-info">
|
||||
<i class="fa-solid fa-info-circle fa-fw"></i>
|
||||
{% trans %}project.bom_import.field_mapping.select_to_see_summary{% endtrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
{{ form_widget(form.submit, {
|
||||
'attr': {
|
||||
'class': 'btn btn-primary'
|
||||
}
|
||||
}) }}
|
||||
<a href="{{ path('project_import_bom', {'id': project.id}) }}" class="btn btn-secondary">
|
||||
<i class="fa-solid fa-arrow-left fa-fw"></i>
|
||||
{% trans %}common.back{% endtrans %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
<script nonce="{{ csp_nonce('script') }}">
|
||||
// Function to initialize the field mapping page
|
||||
function initializeFieldMapping() {
|
||||
const suggestions = {{ suggested_mapping|json_encode|raw }};
|
||||
const fieldNameMapping = {{ field_name_mapping|json_encode|raw }};
|
||||
|
||||
Object.keys(suggestions).forEach(function(field) {
|
||||
// Use the sanitized field name from the server-side mapping
|
||||
const sanitizedField = fieldNameMapping[field];
|
||||
const select = document.querySelector('[name="form[mapping_' + sanitizedField + ']"]');
|
||||
if (select && !select.value) {
|
||||
select.value = suggestions[field];
|
||||
}
|
||||
});
|
||||
|
||||
// Update mapping summary
|
||||
updateMappingSummary();
|
||||
|
||||
// Add event listeners for dynamic updates
|
||||
document.querySelectorAll('.field-mapping-select').forEach(function(select) {
|
||||
select.addEventListener('change', updateMappingSummary);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.priority-input').forEach(function(input) {
|
||||
input.addEventListener('change', updateMappingSummary);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize on both DOMContentLoaded and Turbo events
|
||||
document.addEventListener('DOMContentLoaded', initializeFieldMapping);
|
||||
document.addEventListener('turbo:load', initializeFieldMapping);
|
||||
document.addEventListener('turbo:frame-load', function(event) {
|
||||
// Only initialize if this frame contains our field mapping content
|
||||
if (event.target.id === 'content' || event.target.closest('#content')) {
|
||||
initializeFieldMapping();
|
||||
}
|
||||
});
|
||||
|
||||
function updateMappingSummary() {
|
||||
const summary = document.getElementById('mapping-summary');
|
||||
const mappings = {};
|
||||
const priorities = {};
|
||||
|
||||
// Collect all mappings and priorities
|
||||
document.querySelectorAll('.field-mapping-select').forEach(function(select) {
|
||||
const field = select.getAttribute('data-field');
|
||||
const target = select.value;
|
||||
const priorityInput = document.querySelector('.priority-input[data-field="' + field + '"]');
|
||||
const priority = priorityInput ? parseInt(priorityInput.value) || 10 : 10;
|
||||
|
||||
if (target && target !== '') {
|
||||
if (!mappings[target]) {
|
||||
mappings[target] = [];
|
||||
}
|
||||
mappings[target].push({
|
||||
field: field,
|
||||
priority: priority
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Sort by priority and build summary
|
||||
let summaryHtml = '<div class="row">';
|
||||
Object.keys(mappings).forEach(function(target) {
|
||||
const fieldMappings = mappings[target].sort((a, b) => a.priority - b.priority);
|
||||
const fieldList = fieldMappings.map(m => m.field + ' (' + '{{ "project.bom_import.field_mapping.priority_short"|trans }}' + m.priority + ')').join(', ');
|
||||
|
||||
summaryHtml += '<div class="col-md-6 mb-2">';
|
||||
summaryHtml += '<strong>' + target + ':</strong> ' + fieldList;
|
||||
summaryHtml += '</div>';
|
||||
});
|
||||
summaryHtml += '</div>';
|
||||
|
||||
if (Object.keys(mappings).length === 0) {
|
||||
summary.innerHTML = '<i class="fa-solid fa-info-circle fa-fw"></i> {{ "project.bom_import.field_mapping.select_to_see_summary"|trans }}';
|
||||
} else {
|
||||
summary.innerHTML = summaryHtml;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -22,8 +22,7 @@
|
|||
{% block card_content %}
|
||||
<form action="{{ path('login') }}" method="post" data-turbo="false" class="form-horizontal">
|
||||
|
||||
<input type="hidden" name="_csrf_token"
|
||||
value="{{ csrf_token('authenticate') }}">
|
||||
<input type="hidden" name="_csrf_token" data-controller="csrf-protection" value="{{ csrf_token('authenticate') }}">
|
||||
|
||||
<input type="hidden" name="_target_path" value="{{ app.request.query.get('_target_path') }}" />
|
||||
|
||||
|
|
@ -72,4 +71,4 @@
|
|||
{% if allow_email_pw_reset %}
|
||||
<a class="offset-sm-2" href="{{ path('pw_reset_request') }}">{% trans %}pw_reset.password_forget{% endtrans %}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
66
templates/settings/settings.html.twig
Normal file
66
templates/settings/settings.html.twig
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
{% extends "main_card.html.twig" %}
|
||||
{% macro genId(widget) %}{{ widget.vars.full_name }}{% endmacro %}
|
||||
|
||||
{% form_theme form "form/settings_form.html.twig" %}
|
||||
|
||||
{% block title %}{% trans %}settings.title{% endtrans %}{% endblock %}
|
||||
|
||||
{% block card_title %}<i class="fa-solid fa-gears fa-fw"></i> {% trans %}settings.title{% endtrans %}{% endblock %}
|
||||
|
||||
{% block card_content %}
|
||||
{{ form_start(form) }}
|
||||
|
||||
{# Tabs #}
|
||||
<ul class="nav nav-tabs">
|
||||
{% for tab_widget in form %}
|
||||
{# Create a tab for each compound form #}
|
||||
{% if tab_widget.vars.compound ?? false %}
|
||||
<li class="nav-item">
|
||||
<button type="button" class="nav-link {% if loop.first %}active{% endif %}" aria-current="page" data-bs-toggle="tab"
|
||||
id="settings-{{ _self.genId(tab_widget) }}-tab" data-bs-target="#settings-{{ _self.genId(tab_widget) }}-pane"
|
||||
>{{ (tab_widget.vars.label ?? tab_widget.vars.name|humanize)|trans }}</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{# Panes #}
|
||||
<div class="tab-content">
|
||||
{% for tab_widget in form %}
|
||||
{# Create a tab for each compound form #}
|
||||
{% if tab_widget.vars.compound ?? false %}
|
||||
<div class="tab-pane fade pt-2 {% if loop.first %}show active{% endif %}" id="settings-{{ _self.genId(tab_widget) }}-pane">
|
||||
{{ form_help(tab_widget) }}
|
||||
{{ form_errors(tab_widget) }}
|
||||
|
||||
{% for section_widget in tab_widget %}
|
||||
{% set settings_object = section_widget.vars.value %}
|
||||
|
||||
{% if section_widget.vars.compound ?? false %}
|
||||
<fieldset>
|
||||
<legend class="offset-3">
|
||||
<i class="fa-solid {{ settings_icon(settings_object)|default('fa-sliders') }} fa-fw"></i>
|
||||
{{ (section_widget.vars.label ?? section_widget.vars.name|humanize)|trans }}
|
||||
</legend>
|
||||
<div class="row">
|
||||
<div class="offset-sm-3 col mb-3 ps-2">
|
||||
<b>{{ form_help(section_widget) }}</b>
|
||||
{{ form_errors(section_widget) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form_widget(section_widget) }}
|
||||
</fieldset>
|
||||
{% if not loop.last %}
|
||||
<hr class="mx-0 mb-2 mt-2">
|
||||
{% endif %}
|
||||
{% else %} {# If not a compound render as normal row #}
|
||||
{{ form_row(section_widget) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{% import "helper.twig" as helper %}
|
||||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
<table class="table table-sm table-striped table-hover table-bordered">
|
||||
<tbody>
|
||||
|
|
@ -15,7 +16,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Part-DB Instance name</td>
|
||||
<td>{{ partdb_title }}</td>
|
||||
<td>{{ vars.partdb_title() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Default locale</td>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
{% extends "base.html.twig" %}
|
||||
{% import "vars.macro.twig" as vars %}
|
||||
|
||||
{% block title %}{{ partdb_title }} {% trans %}tfa_backup.codes.title{% endtrans %}{% endblock %}
|
||||
{% block title %}{{ vars.partdb_title() }} {% trans %}tfa_backup.codes.title{% endtrans %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{{ partdb_title }} {% trans %}tfa_backup.codes.title{% endtrans %}
|
||||
{{ vars.partdb_title() }} {% trans %}tfa_backup.codes.title{% endtrans %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{% trans %}tfa_backup.codes.explanation{% endtrans %}</h5>
|
||||
|
|
|
|||
3
templates/vars.macro.twig
Normal file
3
templates/vars.macro.twig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{% macro partdb_title() %}{{ settings_instance("customization").instanceName }}{% endmacro %}
|
||||
|
||||
{% macro base_currency() %}{{ settings_instance("localization").baseCurrency }}{% endmacro %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue