mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-01 21:09:35 +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();
|
||||
}
|
||||
|
||||
$infoModeData = null;
|
||||
|
||||
if ($input !== null && $input !== '') {
|
||||
$mode = $form->isSubmitted() ? $form['mode']->getData() : null;
|
||||
|
|
@ -119,7 +118,18 @@ class ScanController extends AbstractController
|
|||
}
|
||||
|
||||
// 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) {
|
||||
// Keep fallback user-friendly; avoid 500
|
||||
|
|
@ -129,7 +139,13 @@ class ScanController extends AbstractController
|
|||
|
||||
return $this->render('label_system/scanner/scanner.html.twig', [
|
||||
'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;
|
||||
}
|
||||
|
||||
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
|
||||
* @param Request $request
|
||||
|
|
@ -261,53 +232,31 @@ class ScanController extends AbstractController
|
|||
|
||||
$decoded = $scan->getDecodedForInfoMode();
|
||||
|
||||
// Determine if this barcode resolves to *anything* (part, lot->part, storelocation)
|
||||
$redirectUrl = null;
|
||||
$targetFound = false;
|
||||
|
||||
try {
|
||||
$redirectUrl = $this->resultHandler->getInfoURL($scan);
|
||||
$targetFound = true;
|
||||
} catch (EntityNotFoundException) {
|
||||
}
|
||||
//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);
|
||||
|
||||
// Only resolve Part for part-like targets. Storelocation scans should remain null here.
|
||||
$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)
|
||||
//If no entity is found, try to create an URL for creating a new part (only for vendor codes)
|
||||
$createUrl = null;
|
||||
if (!$targetFound) {
|
||||
if ($dbEntity === null) {
|
||||
$createUrl = $this->buildCreateUrlForScanResult($scan);
|
||||
}
|
||||
|
||||
// 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,
|
||||
'found' => $targetFound,
|
||||
'openUrl' => $redirectUrl,
|
||||
'partName' => $partName,
|
||||
'partUrl' => $partUrl,
|
||||
'locations' => $locations,
|
||||
'entity' => $dbEntity,
|
||||
'part' => $resolvedPart,
|
||||
'openUrl' => $openUrl,
|
||||
'createUrl' => $createUrl,
|
||||
]);
|
||||
|
||||
return new JsonResponse([
|
||||
'ok' => true,
|
||||
'found' => $targetFound,
|
||||
'redirectUrl' => $redirectUrl, // client redirects only when infoMode=false
|
||||
'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' => $openUrl, // client redirects only when infoMode=false
|
||||
'createUrl' => $createUrl,
|
||||
'html' => $html,
|
||||
'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.
|
||||
*
|
||||
* @param BarcodeScanResultInterface $barcodeScan The result of the barcode scan
|
||||
* @return string the URL to which should be redirected
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
$entity = $this->resolveEntity($barcodeScan);
|
||||
|
||||
if ($entity === null) {
|
||||
throw new EntityNotFoundException("No entity could be resolved for the given barcode scan result");
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($entity instanceof Part) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ declare(strict_types=1);
|
|||
namespace App\Twig;
|
||||
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Entity\Attachments\AttachmentContainingDBElement;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Services\Attachments\AttachmentURLGenerator;
|
||||
use App\Services\Attachments\PartPreviewGenerator;
|
||||
use App\Services\Misc\FAIconGenerator;
|
||||
use Twig\Attribute\AsTwigFunction;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
|
@ -31,7 +34,7 @@ use Twig\TwigFunction;
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Null is allowed for files withot extension
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue