mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-01 04:49:36 +00:00
Improved augmentented info styling and allow to use it with normal form submit too
This commit is contained in:
parent
8dd972f1ad
commit
bfa9b9eee0
7 changed files with 211 additions and 215 deletions
|
|
@ -92,7 +92,6 @@ class ScanController extends AbstractController
|
||||||
$input = $form['input']->getData();
|
$input = $form['input']->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
$infoModeData = null;
|
|
||||||
|
|
||||||
if ($input !== null && $input !== '') {
|
if ($input !== null && $input !== '') {
|
||||||
$mode = $form->isSubmitted() ? $form['mode']->getData() : null;
|
$mode = $form->isSubmitted() ? $form['mode']->getData() : null;
|
||||||
|
|
@ -119,7 +118,18 @@ class ScanController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info mode fallback: render page with prefilled result
|
// Info mode fallback: render page with prefilled result
|
||||||
$infoModeData = $scan->getDecodedForInfoMode();
|
$decoded = $scan->getDecodedForInfoMode();
|
||||||
|
|
||||||
|
//Try to resolve to an entity, to enhance info mode with entity-specific data
|
||||||
|
$dbEntity = $this->resultHandler->resolveEntity($scan);
|
||||||
|
$resolvedPart = $this->resultHandler->resolvePart($scan);
|
||||||
|
$openUrl = $this->resultHandler->getInfoURL($scan);
|
||||||
|
|
||||||
|
//If no entity is found, try to create an URL for creating a new part (only for vendor codes)
|
||||||
|
$createUrl = null;
|
||||||
|
if ($dbEntity === null) {
|
||||||
|
$createUrl = $this->buildCreateUrlForScanResult($scan);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// Keep fallback user-friendly; avoid 500
|
// Keep fallback user-friendly; avoid 500
|
||||||
|
|
@ -129,7 +139,13 @@ class ScanController extends AbstractController
|
||||||
|
|
||||||
return $this->render('label_system/scanner/scanner.html.twig', [
|
return $this->render('label_system/scanner/scanner.html.twig', [
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
'infoModeData' => $infoModeData,
|
|
||||||
|
//Info mode
|
||||||
|
'decoded' => $decoded ?? null,
|
||||||
|
'entity' => $dbEntity ?? null,
|
||||||
|
'part' => $resolvedPart ?? null,
|
||||||
|
'openUrl' => $openUrl ?? null,
|
||||||
|
'createUrl' => $createUrl ?? null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,51 +197,6 @@ class ScanController extends AbstractController
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildLocationsForPart(Part $part): array
|
|
||||||
{
|
|
||||||
$byLocationId = [];
|
|
||||||
|
|
||||||
foreach ($part->getPartLots() as $lot) {
|
|
||||||
$loc = $lot->getStorageLocation();
|
|
||||||
if ($loc === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$locId = $loc->getID();
|
|
||||||
$qty = $lot->getAmount();
|
|
||||||
|
|
||||||
if (!isset($byLocationId[$locId])) {
|
|
||||||
$byLocationId[$locId] = [
|
|
||||||
'breadcrumb' => $this->buildStorageBreadcrumb($loc),
|
|
||||||
'qty' => $qty,
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$byLocationId[$locId]['qty'] += $qty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($byLocationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildStorageBreadcrumb(StorageLocation $loc): array
|
|
||||||
{
|
|
||||||
$items = [];
|
|
||||||
$cur = $loc;
|
|
||||||
|
|
||||||
// 20 is the overflow limit in src/Entity/Base/AbstractStructuralDBElement.php line ~273
|
|
||||||
for ($i = 0; $i < 20 && $cur !== null; $i++) {
|
|
||||||
$items[] = [
|
|
||||||
'name' => $cur->getName(),
|
|
||||||
'url' => $this->generateUrl('part_list_store_location', ['id' => $cur->getID()]),
|
|
||||||
];
|
|
||||||
|
|
||||||
$parent = $cur->getParent(); // inherited from AbstractStructuralDBElement
|
|
||||||
$cur = ($parent instanceof StorageLocation) ? $parent : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_reverse($items);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides XHR endpoint for looking up barcode information and return JSON response
|
* Provides XHR endpoint for looking up barcode information and return JSON response
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
|
|
@ -261,53 +232,31 @@ class ScanController extends AbstractController
|
||||||
|
|
||||||
$decoded = $scan->getDecodedForInfoMode();
|
$decoded = $scan->getDecodedForInfoMode();
|
||||||
|
|
||||||
// Determine if this barcode resolves to *anything* (part, lot->part, storelocation)
|
|
||||||
$redirectUrl = null;
|
|
||||||
$targetFound = false;
|
|
||||||
|
|
||||||
try {
|
//Try to resolve to an entity, to enhance info mode with entity-specific data
|
||||||
$redirectUrl = $this->resultHandler->getInfoURL($scan);
|
$dbEntity = $this->resultHandler->resolveEntity($scan);
|
||||||
$targetFound = true;
|
$resolvedPart = $this->resultHandler->resolvePart($scan);
|
||||||
} catch (EntityNotFoundException) {
|
$openUrl = $this->resultHandler->getInfoURL($scan);
|
||||||
}
|
|
||||||
|
|
||||||
// Only resolve Part for part-like targets. Storelocation scans should remain null here.
|
//If no entity is found, try to create an URL for creating a new part (only for vendor codes)
|
||||||
$part = null;
|
|
||||||
$partName = null;
|
|
||||||
$partUrl = null;
|
|
||||||
$locations = [];
|
|
||||||
|
|
||||||
if ($targetFound) {
|
|
||||||
$part = $this->resultHandler->resolvePart($scan);
|
|
||||||
|
|
||||||
if ($part instanceof Part) {
|
|
||||||
$partName = $part->getName();
|
|
||||||
$partUrl = $this->generateUrl('app_part_show', ['id' => $part->getID()]);
|
|
||||||
$locations = $this->buildLocationsForPart($part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create link only when NOT found (vendor codes)
|
|
||||||
$createUrl = null;
|
$createUrl = null;
|
||||||
if (!$targetFound) {
|
if ($dbEntity === null) {
|
||||||
$createUrl = $this->buildCreateUrlForScanResult($scan);
|
$createUrl = $this->buildCreateUrlForScanResult($scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render fragment (use openUrl for universal "Open" link)
|
// Render fragment (use openUrl for universal "Open" link)
|
||||||
$html = $this->renderView('label_system/scanner/augmented_result.html.twig', [
|
$html = $this->renderView('label_system/scanner/_info_mode.html.twig', [
|
||||||
'decoded' => $decoded,
|
'decoded' => $decoded,
|
||||||
'found' => $targetFound,
|
'entity' => $dbEntity,
|
||||||
'openUrl' => $redirectUrl,
|
'part' => $resolvedPart,
|
||||||
'partName' => $partName,
|
'openUrl' => $openUrl,
|
||||||
'partUrl' => $partUrl,
|
|
||||||
'locations' => $locations,
|
|
||||||
'createUrl' => $createUrl,
|
'createUrl' => $createUrl,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
'found' => $targetFound,
|
'found' => $openUrl !== null, // we consider the code "found", if we can at least show an info page (even if the part is not found, but we can show the decoded data and a "create" button)
|
||||||
'redirectUrl' => $redirectUrl, // client redirects only when infoMode=false
|
'redirectUrl' => $openUrl, // client redirects only when infoMode=false
|
||||||
'createUrl' => $createUrl,
|
'createUrl' => $createUrl,
|
||||||
'html' => $html,
|
'html' => $html,
|
||||||
'infoMode' => $infoMode,
|
'infoMode' => $infoMode,
|
||||||
|
|
|
||||||
|
|
@ -71,17 +71,15 @@ final readonly class BarcodeScanResultHandler
|
||||||
* Determines the URL to which the user should be redirected, when scanning a QR code.
|
* Determines the URL to which the user should be redirected, when scanning a QR code.
|
||||||
*
|
*
|
||||||
* @param BarcodeScanResultInterface $barcodeScan The result of the barcode scan
|
* @param BarcodeScanResultInterface $barcodeScan The result of the barcode scan
|
||||||
* @return string the URL to which should be redirected
|
* @return string|null the URL to which should be redirected, or null if no suitable URL could be determined for the given barcode scan result
|
||||||
*
|
|
||||||
* @throws EntityNotFoundException
|
|
||||||
*/
|
*/
|
||||||
public function getInfoURL(BarcodeScanResultInterface $barcodeScan): string
|
public function getInfoURL(BarcodeScanResultInterface $barcodeScan): ?string
|
||||||
{
|
{
|
||||||
//For other barcodes try to resolve the part first and then redirect to the part page
|
//For other barcodes try to resolve the part first and then redirect to the part page
|
||||||
$entity = $this->resolveEntity($barcodeScan);
|
$entity = $this->resolveEntity($barcodeScan);
|
||||||
|
|
||||||
if ($entity === null) {
|
if ($entity === null) {
|
||||||
throw new EntityNotFoundException("No entity could be resolved for the given barcode scan result");
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity instanceof Part) {
|
if ($entity instanceof Part) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,10 @@ declare(strict_types=1);
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use App\Entity\Attachments\Attachment;
|
use App\Entity\Attachments\Attachment;
|
||||||
|
use App\Entity\Attachments\AttachmentContainingDBElement;
|
||||||
|
use App\Entity\Parts\Part;
|
||||||
use App\Services\Attachments\AttachmentURLGenerator;
|
use App\Services\Attachments\AttachmentURLGenerator;
|
||||||
|
use App\Services\Attachments\PartPreviewGenerator;
|
||||||
use App\Services\Misc\FAIconGenerator;
|
use App\Services\Misc\FAIconGenerator;
|
||||||
use Twig\Attribute\AsTwigFunction;
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
|
|
@ -31,7 +34,7 @@ use Twig\TwigFunction;
|
||||||
|
|
||||||
final readonly class AttachmentExtension
|
final readonly class AttachmentExtension
|
||||||
{
|
{
|
||||||
public function __construct(private AttachmentURLGenerator $attachmentURLGenerator, private FAIconGenerator $FAIconGenerator)
|
public function __construct(private AttachmentURLGenerator $attachmentURLGenerator, private FAIconGenerator $FAIconGenerator, private PartPreviewGenerator $partPreviewGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,6 +47,26 @@ final readonly class AttachmentExtension
|
||||||
return $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name);
|
return $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of the thumbnail of the given element. Returns null if no thumbnail is available.
|
||||||
|
* For parts, a special preview image is generated, for other entities, the master picture is used as preview (if available).
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("entity_thumbnail")]
|
||||||
|
public function entityThumbnail(AttachmentContainingDBElement $element, string $filter_name = 'thumbnail_sm'): ?string
|
||||||
|
{
|
||||||
|
if ($element instanceof Part) {
|
||||||
|
$preview_attachment = $this->partPreviewGenerator->getTablePreviewAttachment($element);
|
||||||
|
} else { // For other entities, we just use the master picture as preview, if available
|
||||||
|
$preview_attachment = $element->getMasterPictureAttachment();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($preview_attachment === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->attachmentURLGenerator->getThumbnailURL($preview_attachment, $filter_name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the font-awesome icon type for the given file extension. Returns "file" if no specific icon is available.
|
* Return the font-awesome icon type for the given file extension. Returns "file" if no specific icon is available.
|
||||||
* Null is allowed for files withot extension
|
* Null is allowed for files withot extension
|
||||||
|
|
|
||||||
119
templates/label_system/scanner/_info_mode.html.twig
Normal file
119
templates/label_system/scanner/_info_mode.html.twig
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
{% 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-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-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>
|
||||||
|
{% 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 %}
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
{% if decoded is not empty %}
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<h4 class="mb-0 me-2">
|
|
||||||
{% if found and partName %}
|
|
||||||
{% trans %}label_scanner.part_info.title{% endtrans %}
|
|
||||||
{% else %}
|
|
||||||
{% trans %}label_scanner.scan_result.title{% endtrans %}
|
|
||||||
{% endif %}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
|
|
||||||
{% if createUrl %}
|
|
||||||
<a class="btn btn-primary ms-2 mb-2"
|
|
||||||
href="{{ createUrl }}"
|
|
||||||
target="_blank"
|
|
||||||
title="{% trans %}part.create.btn{% endtrans %}">
|
|
||||||
<i class="fa-solid fa-plus-square"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if found %}
|
|
||||||
<div class="alert alert-success mb-2 d-flex align-items-center justify-content-between">
|
|
||||||
<div>
|
|
||||||
{% if partName %}
|
|
||||||
{{ partName }}
|
|
||||||
{% else %}
|
|
||||||
{% trans %}label_scanner.target_found{% endtrans %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if openUrl %}
|
|
||||||
<a href="{{ openUrl }}" target="_blank" class="btn btn-sm btn-outline-success">
|
|
||||||
{% trans %}open{% endtrans %}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if partName %}
|
|
||||||
{% if locations is not empty %}
|
|
||||||
<table class="table table-sm 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 loc in locations %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<ul class="structural_link d-inline">
|
|
||||||
{% for crumb in loc.breadcrumb %}
|
|
||||||
<li><a href="{{ crumb.url }}" target="_blank">{{ crumb.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
<td class="text-end" style="width: 6rem;">
|
|
||||||
{% if loc.qty is not null %}<strong>{{ loc.qty }}</strong>{% else %}<span class="text-muted">—</span>{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
<div class="text-muted mb-2">{% trans %}label_scanner.no_locations{% endtrans %}</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<div class="alert alert-warning mb-2">
|
|
||||||
{% trans %}label_scanner.qr_part_no_found{% endtrans %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Decoded barcode fields #}
|
|
||||||
<table class="table table-striped table-hover table-bordered table-sm mt-4 mb-0">
|
|
||||||
<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 #}
|
|
||||||
<div class="pt-3">
|
|
||||||
<hr class="my-0">
|
|
||||||
</div>
|
|
||||||
<div class="mb-4"></div>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
@ -20,37 +20,11 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="scan-augmented-result" class="mt-3"></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_start(form, {'attr': {'id': 'scan_dialog_form'}}) }}
|
||||||
|
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
|
|
||||||
|
|
||||||
{% if infoModeData %}
|
|
||||||
<hr>
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<h4 class="mb-0">{% trans %}label_scanner.decoded_info.title{% endtrans %}</h4>
|
|
||||||
|
|
||||||
{% if createUrl is defined and createUrl %}
|
|
||||||
<a class="btn btn-primary ms-2" href="{{ createUrl }}"
|
|
||||||
target="_blank" title="{% trans %}part.create.btn{% endtrans %}">
|
|
||||||
<i class="fa-solid fa-plus-square"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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 %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -9539,7 +9539,7 @@ Please note, that you can not impersonate a disabled user. If you try you will g
|
||||||
<unit id="PTh4EK_" name="label_scanner.no_locations">
|
<unit id="PTh4EK_" name="label_scanner.no_locations">
|
||||||
<segment>
|
<segment>
|
||||||
<source>label_scanner.no_locations</source>
|
<source>label_scanner.no_locations</source>
|
||||||
<target>Part is not stored at any locations</target>
|
<target>Part is not stored at any location.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="lV9HDL0" name="label_scanner.qr_part_no_found">
|
<unit id="lV9HDL0" name="label_scanner.qr_part_no_found">
|
||||||
|
|
@ -12545,5 +12545,35 @@ Buerklin-API Authentication server:
|
||||||
<target>Last stocktake</target>
|
<target>Last stocktake</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="aEgd0if" name="label_scanner.open">
|
||||||
|
<segment>
|
||||||
|
<source>label_scanner.open</source>
|
||||||
|
<target>View details</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="vw_0Qws" name="label_scanner.db_part_found">
|
||||||
|
<segment>
|
||||||
|
<source>label_scanner.db_part_found</source>
|
||||||
|
<target>Database [part] found for barcode</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="zntajcd" name="label_scanner.part_can_be_created">
|
||||||
|
<segment>
|
||||||
|
<source>label_scanner.part_can_be_created</source>
|
||||||
|
<target>[Part] can be created</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="cLTbd9w" name="label_scanner.part_can_be_created.help">
|
||||||
|
<segment>
|
||||||
|
<source>label_scanner.part_can_be_created.help</source>
|
||||||
|
<target>No matching [part] was found in the database, but you can create a new [part] based of this barcode.</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="FfHA3Yf" name="label_scanner.part_create_btn">
|
||||||
|
<segment>
|
||||||
|
<source>label_scanner.part_create_btn</source>
|
||||||
|
<target>Create [part] from barcode</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue