mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-05-21 10:51:38 +00:00
Merge branch 'master' into feature/kicad-enhancements
This commit is contained in:
commit
6ab75488b4
56 changed files with 7142 additions and 2997 deletions
|
|
@ -60,6 +60,15 @@
|
|||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted("@tools.label_scanner") %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ path('scan_dialog') }}">
|
||||
<i class="fa-fw fa-solid fa-camera-retro"></i>
|
||||
{% trans %}parts.create_from_scan.title{% endtrans %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('@parts.import') %}
|
||||
|
|
@ -159,7 +168,7 @@
|
|||
<li role="separator" class="dropdown-divider"></li>
|
||||
<h6 class="dropdown-header">{% trans %}user.language_select{% endtrans %}</h6>
|
||||
<div id="locale-select-menu">
|
||||
{# This menu is filled by 'turbo/locale_menu' controller from the _turbo_control.html.twig template, to always have the correct path #}
|
||||
{# This menu is filled by a turbo-stream in _turbo_contro.html.twig #}
|
||||
</div>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,39 @@
|
|||
{# Insert flashes #}
|
||||
<div class="toasts-global d-none">
|
||||
{% for label, messages in app.flashes() %}
|
||||
{% for message in messages %}
|
||||
{{ include('_toast.html.twig', {
|
||||
'label': label,
|
||||
'message': message
|
||||
}) }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% block flashes %}
|
||||
{# Insert flashes #}
|
||||
<turbo-stream action="replace" action="morph" target="toast-container">
|
||||
<template>
|
||||
<div class="toast-container" id="toast-container">
|
||||
{% for label, messages in app.flashes() %}
|
||||
{% for message in messages %}
|
||||
{{ include('_toast.html.twig', {
|
||||
'label': label,
|
||||
'message': message
|
||||
}) }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</template>
|
||||
</turbo-stream>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{# Allow pages to request a fully reload of everything #}
|
||||
{% if global_reload_needed is defined and global_reload_needed %}
|
||||
<div {{ stimulus_controller('turbo/global_reload') }}></div>
|
||||
{% endif %}
|
||||
|
||||
{# Insert info about when the sidebar trees were updated last time, so the sidebar_tree_controller can decide if it needs to reload the tree #}
|
||||
<span id="sidebar-last-time-updated" style="display: none;" data-last-update="{{ sidebar_tree_updater.lastTreeUpdate.format("Y-m-d\\TH:i:sP") }}"></span>
|
||||
|
||||
{# The title block is already escaped, therefore we dont require any additional escaping here #}
|
||||
<div class="d-none" data-title="{{ current_page_title|trim|raw }}" {{ stimulus_controller('turbo/title') }}></div>
|
||||
<turbo-stream action="update" target="locale-select-menu">
|
||||
<template>
|
||||
{% set locales = settings_instance('localization').languageMenuEntries %}
|
||||
{% if locales is empty %}
|
||||
{% set locales = locale_menu %}
|
||||
{% endif %}
|
||||
|
||||
{% for locale in locales %}
|
||||
<a class="dropdown-item" data-turbo="false" data-turbo-frame="_top" href="{{ path(app.request.attributes.get('_route'),
|
||||
app.request.query.all|merge(app.request.attributes.get('_route_params'))|merge({'_locale': locale})) }}">
|
||||
{{ locale|language_name }} ({{ locale|upper }})</a>
|
||||
{% endfor %}
|
||||
</template>
|
||||
</turbo-stream>
|
||||
|
||||
<div class="d-none" {{ stimulus_controller('turbo/locale_menu') }}>
|
||||
{% set locales = settings_instance('localization').languageMenuEntries %}
|
||||
{% if locales is empty %}
|
||||
{% set locales = locale_menu %}
|
||||
{% endif %}
|
||||
|
||||
{% for locale in locales %}
|
||||
<a class="dropdown-item" data-turbo="false" data-turbo-frame="_top" href="{{ path(app.request.attributes.get('_route'),
|
||||
app.request.query.all|merge(app.request.attributes.get('_route_params'))|merge({'_locale': locale})) }}">
|
||||
{{ locale|language_name }} ({{ locale|upper }})</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
|||
78
templates/attachments/html_sandbox.html.twig
Normal file
78
templates/attachments/html_sandbox.html.twig
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ app.request.locale | replace({"_": "-"}) }}"
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>️⚠️️</text></svg>">
|
||||
|
||||
{# The content block is already escaped. so we must not escape it again. #}
|
||||
<title>{% trans %}attachment.sandbox.title{% endtrans %}: {{ attachment.filename }}</title>
|
||||
|
||||
<style>
|
||||
/* Reset margins and stop the page from scrolling */
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* The Flex Container */
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* The Warning Header */
|
||||
.warning-bar {
|
||||
background-color: #ff4d4d;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
z-index: 10; /* Keep it above the iframe */
|
||||
}
|
||||
|
||||
/* The Iframe: The 'flex: 1' makes it fill all remaining space */
|
||||
.content-frame {
|
||||
flex: 1;
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}
|
||||
{# We have a fullscreen iframe, with an warning on top #}
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<header>
|
||||
<header class="warning-bar">
|
||||
<b>⚠️ {% trans%}attachment.sandbox.warning{% endtrans %}</b>
|
||||
|
||||
<br>
|
||||
<small>
|
||||
{% trans%}[Attachment]{% endtrans%}: <b>{{ attachment.name }}</b> / <b>{{ attachment.filename ?? "" }}</b> ({% trans%}id.label{% endtrans %}: {{ attachment.id }})
|
||||
<a href="{{ path("attachment_view", {id: attachment.id}) }}" style="color: white; margin-left: 15px;">{% trans%}attachment.sandbox.as_plain_text{% endtrans %}</a>
|
||||
<a href="{{ path("homepage") }}" style="color: white; margin-left: 15px;">{% trans%}attachment.sandbox.back_to_partdb{% endtrans %}</a>
|
||||
</small>
|
||||
</header>
|
||||
</header>
|
||||
|
||||
<iframe referrerpolicy="no-referrer" class="content-frame"
|
||||
{# When changing this sandbox, also change the sandbox CSP in the controller #}
|
||||
sandbox="allow-scripts allow-downloads allow-modals"
|
||||
srcdoc="{{ content|e('html_attr') }}"
|
||||
></iframe>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<!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 #}
|
||||
{# For the UX translator, just use the language part (before the _. should be 2 chars), otherwise it finds no translations #}
|
||||
data-symfony-ux-translator-locale="{{ app.request.locale|u.truncate(2) }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
|
@ -26,6 +26,11 @@
|
|||
<meta name="turbo-refresh-method" content="morph">
|
||||
<meta name="turbo-refresh-scroll" content="preserve">
|
||||
|
||||
{# Allow pages to request a fully reload of everything #}
|
||||
{% if global_reload_needed is defined and global_reload_needed %}
|
||||
<meta name="turbo-visit-control" content="reload">
|
||||
{% endif %}
|
||||
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{{ asset('favicon.ico') }}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset('icon/apple-touch-icon.png') }}">
|
||||
<link rel="icon" type="image/png" href="{{ asset('icon/favicon-32x32.png') }}" sizes="32x32">
|
||||
|
|
@ -68,7 +73,17 @@
|
|||
{{ encore_entry_script_tags('webauthn_tfa') }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body data-base-url="{{ path('homepage', {'_locale': app.request.locale}) }}" data-locale="{{ app.request.locale|default("en")|slice(0,2) }}">
|
||||
<body data-base-url="{{ path('homepage', {'_locale': app.request.locale}) }}"
|
||||
data-locale="{{ app.request.locale|default("en")|slice(0,2) }}"
|
||||
data-keybindings-special-characters="{{ settings_instance('keybindings').enableSpecialCharacters ? 'true' : 'false' }}">
|
||||
|
||||
{# Listen for the special #}
|
||||
{% if is_granted("@tools.label_scanner") %}
|
||||
<form class="d-none" {{ stimulus_controller('helpers/scan_special_char') }} action="{{ path("scan_dialog") }}" data-turbo-frame="content">
|
||||
<input name="input" type="hidden">
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% block body %}
|
||||
<header>
|
||||
<turbo-frame id="navbar-frame" target="content" data-turbo-action="advance">
|
||||
|
|
@ -114,13 +129,13 @@
|
|||
|
||||
<!-- Back to top button -->
|
||||
<button id="back-to-top" class="btn btn-primary back-to-top btn-sm" role="button" title="{% trans %}back_to_top{% endtrans %}"
|
||||
{{ stimulus_controller('common/back_to_top') }} {{ stimulus_action('common/back_to_top', 'backToTop') }}>
|
||||
{{ stimulus_controller('common/back_to_top') }} {{ stimulus_action('common/back_to_top', 'backToTop') }}>
|
||||
<i class="fas fa-angle-up fa-fw"></i>
|
||||
</button>
|
||||
|
||||
{# Must be outside of the sidebar or it will be hidden too #}
|
||||
<button class="btn btn-outline-secondary btn-sm p-0 d-md-block d-none" type="button" id="sidebar-toggle-button" title="{% trans %}sidebar.big.toggle{% endtrans %}"
|
||||
{{ stimulus_controller('common/hide_sidebar') }} {{ stimulus_action('common/hide_sidebar', 'toggleSidebar') }} style="--fa-width: 10px;">
|
||||
{{ stimulus_controller('common/hide_sidebar') }} {{ stimulus_action('common/hide_sidebar', 'toggleSidebar') }} style="--fa-width: 10px;">
|
||||
<i class="fas fa-angle-left"></i>
|
||||
</button>
|
||||
|
||||
|
|
|
|||
154
templates/label_system/scanner/_info_mode.html.twig
Normal file
154
templates/label_system/scanner/_info_mode.html.twig
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
{% import "helper.twig" as helper %}
|
||||
|
||||
{% if decoded is not empty %}
|
||||
<hr>
|
||||
|
||||
{% if part %} {# Show detailed info when it is a part #}
|
||||
<div class="card border-success">
|
||||
<h5 class="card-header text-bg-success">
|
||||
<small>{% trans %}label_scanner.db_part_found{% endtrans %}</small>
|
||||
{% if openUrl %}
|
||||
<div class="btn-group float-end">
|
||||
<a href="{{ openUrl }}" target="_blank" class="btn btn-sm btn-outline-light"
|
||||
title="{% trans %}label_scanner.open{% endtrans %}">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</h5>
|
||||
<div class="card-body row">
|
||||
<div class="col-sm-2">
|
||||
<img class="d-block w-100 img-fluid img-thumbnail bg-light part-info-image"
|
||||
src="{{ entity_thumbnail(part) ?? asset('img/part_placeholder.svg') }}" alt="">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-10">
|
||||
<h4 class="card-title mb-0">{{ part.name }}</h4>
|
||||
<div class="card-text text-muted">{{ part.description | format_markdown(true) }}</div>
|
||||
<div>
|
||||
<dt class="d-inline-block">
|
||||
<span class="visually-hidden">{% trans %}category.label{% endtrans %}</span>
|
||||
<i class="fas fa-tag fa-fw" title="{% trans %}category.label{% endtrans %}"></i>
|
||||
</dt>
|
||||
<dd class="d-inline">
|
||||
<span class="text-muted">{{ helper.structural_entity_link(part.category) }}</span>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<dt class="d-inline-block">
|
||||
<span class="visually-hidden">{% trans %}footprint.label{% endtrans %}</span>
|
||||
<i class="fas fa-microchip fa-fw" title="{% trans %}footprint.label{% endtrans %}"></i>
|
||||
</dt>
|
||||
<dd class="d-inline">
|
||||
<span class="text-muted">{{ helper.structural_entity_link(part.footprint) }}</span>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
{# Show part lots / locations #}
|
||||
{% if part.partLots is not empty %}
|
||||
<table class="table table-sm table-striped mb-2 w-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% trans %}part_lots.storage_location{% endtrans %}</th>
|
||||
<th scope="col" class="text-end" style="width: 6rem;">
|
||||
{% trans %}part_lots.amount{% endtrans %}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for lot in part.partLots %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if lot.storageLocation %}
|
||||
{{ helper.structural_entity_link(lot.storageLocation) }}
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end" style="width: 6rem;">
|
||||
{% if lot.instockUnknown %}
|
||||
<span class="text-muted">?</span>
|
||||
{% else %}
|
||||
{{ lot.amount | format_amount(part.partUnit, {'decimals': 5}) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="text-muted">{% trans %}label_scanner.no_locations{% endtrans %}</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% elseif entity %} {# If we have an entity but that is not an part #}
|
||||
|
||||
<div class="card border-success">
|
||||
<h5 class="card-header text-bg-success">
|
||||
<small>{% trans %}label_scanner.target_found{% endtrans %}: {{ type_label(entity) }}</small>
|
||||
{% if openUrl %}
|
||||
<div class="btn-group float-end">
|
||||
<a href="{{ openUrl }}" target="_blank" class="btn btn-sm btn-outline-light"
|
||||
title="{% trans %}label_scanner.open{% endtrans %}">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</h5>
|
||||
<div class="card-body row">
|
||||
<div class="col-sm-2">
|
||||
<img class="d-block w-100 img-fluid img-thumbnail bg-light part-info-image"
|
||||
src="{{ entity_thumbnail(entity) ?? asset('img/part_placeholder.svg') }}" alt="">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-10">
|
||||
<h4 class="card-title mb-0">{{ entity.name }}</h4>
|
||||
<p>{% trans %}id.label{% endtrans %}: {{ entity.id }} ({{ type_label(entity) }})</p>
|
||||
|
||||
{% if entity.fullPath is defined %}
|
||||
{{ helper.breadcrumb_entity_link(entity)}}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if createUrl %}
|
||||
<div class="alert alert-info mb-2">
|
||||
<h4 class="alert-heading mb-0">{% trans %}label_scanner.part_can_be_created{% endtrans %}</h4>
|
||||
<p class="text-muted mb-0"><small >{% trans %}label_scanner.part_can_be_created.help{% endtrans %}</small></p>
|
||||
<hr>
|
||||
<a class="btn btn-outline-success" href="{{ createUrl }}" target="_blank"><i class="fas fa-plus-square"></i> {% trans %}label_scanner.part_create_btn{% endtrans %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h4 class="mt-2">
|
||||
{% trans %}label_scanner.scan_result.title{% endtrans %}
|
||||
</h4>
|
||||
|
||||
{# Decoded barcode fields #}
|
||||
<table class="table table-striped table-hover table-bordered table-sm">
|
||||
<tbody>
|
||||
{% for key, value in decoded %}
|
||||
<tr>
|
||||
<th class="text-nowrap">{{ key }}</th>
|
||||
<td><code>{{ value }}</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{# Whitespace under table and Input form fields #}
|
||||
<hr>
|
||||
|
||||
{% endif %}
|
||||
|
|
@ -10,35 +10,28 @@
|
|||
<div class="">
|
||||
<div class="form-group row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
|
||||
<div class="img-thumbnail" style="max-width: 600px;">
|
||||
<div id="reader-box" {{ stimulus_controller('pages/barcode_scan') }}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="scan-augmented-result" class="mt-3">
|
||||
{% include "label_system/scanner/_info_mode.html.twig" %}
|
||||
</div>
|
||||
|
||||
{{ form_start(form, {'attr': {'id': 'scan_dialog_form'}}) }}
|
||||
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
|
||||
{{ form_start(form, {'attr': {'id': 'scan_dialog_form'}}) }}
|
||||
|
||||
{{ form_end(form) }}
|
||||
|
||||
|
||||
{% if infoModeData %}
|
||||
<hr>
|
||||
<h4>{% trans %}label_scanner.decoded_info.title{% endtrans %}</h4>
|
||||
|
||||
<table class="table table-striped table-hover table-bordered table-sm">
|
||||
<tbody>
|
||||
{% for key, value in infoModeData %}
|
||||
<tr>
|
||||
<td>{{ key }}</td>
|
||||
<td><code>{{ value }}</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scan_results %}
|
||||
<turbo-stream action="replace" action="morph" target="scan-augmented-result">
|
||||
<template>
|
||||
<div id="scan-augmented-result" class="mt-3">
|
||||
{% include "label_system/scanner/_info_mode.html.twig" %}
|
||||
</div>
|
||||
</template>
|
||||
</turbo-stream>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue