Datasource-spezifische Suche für Projects/Assemblies sowie Parts umsetzen

This commit is contained in:
Marcel Diegelmann 2026-04-01 16:08:54 +02:00
parent dde91ff1c5
commit ca6254cc53
39 changed files with 4695 additions and 494 deletions

View file

@ -120,6 +120,9 @@ export default class extends Controller {
this._autocomplete = autocomplete({
container: this.element,
initialState: {
query: this.element.dataset.initialQuery || that.inputTarget.value || ""
},
//Place the panel in the navbar, if the element is in navbar mode
panelContainer: navbar_mode ? document.getElementById("navbar-search-form") : document.body,
panelPlacement: this.element.dataset.panelPlacement,
@ -162,7 +165,10 @@ export default class extends Controller {
}
input.value = state.query;
input.form.requestSubmit();
if (input.form) {
input.form.requestSubmit();
}
},
getSources({ query }) {

View file

@ -0,0 +1,76 @@
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["datasource", "partOptions", "assemblyOptions", "projectOptions", "divider"];
static values = {
isSearchList: Boolean
};
connect() {
// Delay update slightly to ensure all child controllers are connected and DOM is ready
setTimeout(() => {
this.updateVisibility();
}, 1000);
}
onDatasourceChange() {
this.updateVisibility();
}
updateVisibility() {
if (!this.hasDatasourceTarget) return;
const datasource = this.datasourceTarget.value;
const isSearchList = this.isSearchListValue;
const isPart = (datasource === "parts");
const isAssembly = (datasource === "assemblies");
const isProject = (datasource === "projects");
if (this.hasPartOptionsTarget) {
this.toggleOptions(this.partOptionsTarget, isPart, isSearchList);
}
if (this.hasAssemblyOptionsTarget) {
this.toggleOptions(this.assemblyOptionsTarget, isAssembly, isSearchList);
}
if (this.hasProjectOptionsTarget) {
this.toggleOptions(this.projectOptionsTarget, isProject, isSearchList);
}
if (this.hasDividerTarget) {
this.dividerTarget.classList.toggle("d-none", !isPart && !isAssembly && !isProject);
}
}
toggleOptions(container, show, isSearchList) {
const wasHidden = container.classList.contains("d-none");
container.classList.toggle("d-none", !show);
const checkboxes = container.querySelectorAll('input[type="checkbox"]');
if (!show) {
// Deselect checkboxes if not in correct mode
checkboxes.forEach(checkbox => {
// Store current state to restore it later if the user switches back
if (checkbox.checked) {
checkbox.dataset.previousState = "true";
checkbox.checked = false;
// Trigger a change event to update sessionStorage via the sessionStorage_checkbox controller
// We use a CustomEvent to pass the skipStorage flag
checkbox.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { skipStorage: true } }));
}
});
} else if (wasHidden) {
// Restore state when switching back
checkboxes.forEach(checkbox => {
// Restore state if NOT on search list
// On search list, we don't restore to avoid overwriting Twig's checked state
if (!isSearchList && checkbox.dataset.previousState === "true") {
checkbox.checked = true;
checkbox.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { skipStorage: true } }));
}
delete checkbox.dataset.previousState;
});
}
}
}

View file

@ -0,0 +1,81 @@
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Controller} from "@hotwired/stimulus";
export default class extends Controller
{
static values = {
id: String,
isSearchList: Boolean
}
connect() {
if (this.isSearchListValue) {
// If we are on the search list, we want to update the localStorage with the current (server-side) state
// to ensure consistency.
this.saveState();
} else {
// Otherwise, we load the state from localStorage.
this.loadState();
}
this.element.addEventListener('change', (event) => {
// Don't save state if we are currently being toggled by the search_options controller
// to avoid saving "unchecked" states when options are hidden.
// CustomEvent's detail property contains the data we passed.
if (event instanceof CustomEvent && event.detail && event.detail.skipStorage) {
return;
}
this.saveState()
});
}
loadState() {
let storageKey = this.getStorageKey();
let value = localStorage.getItem(storageKey);
if (value === null) {
return;
}
if (value === 'true') {
this.element.checked = true
}
if (value === 'false') {
this.element.checked = false
}
}
saveState() {
let storageKey = this.getStorageKey();
if (this.element.checked) {
localStorage.setItem(storageKey, 'true');
} else {
localStorage.setItem(storageKey, 'false');
}
}
getStorageKey() {
if (this.hasIdValue) {
return 'persistent_checkbox_' + this.idValue
}
return 'persistent_checkbox_' + this.element.id;
}
}

View file

@ -34,6 +34,10 @@ export default class extends Controller {
/** @type {HTMLFormElement} */
const form = event.target.closest('form');
if (!form) {
return;
}
for(const element of form.elements) {
if(! element.value) {
element.disabled = true;
@ -53,6 +57,11 @@ export default class extends Controller {
clearAll(event)
{
const form = event.target.closest('form');
if (!form) {
return;
}
for(const element of form.elements) {
// Do not clear elements with data-no-clear attribute
if(element.dataset.noClear) {

View file

@ -147,6 +147,9 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
time).
Also specify the default order of the columns. This is a comma separated list of column names. Available columns
are: `name`, `id`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `storage_location`, `amount`, `minamount`, `partUnit`, `partCustomState`, `addedDate`, `lastModified`, `needs_review`, `favorite`, `manufacturing_status`, `manufacturer_product_number`, `mass`, `tags`, `attachments`, `edit`.
* `TABLE_PROJECTS_DEFAULT_COLUMNS`: The columns in projects tables, which are visible by default (when loading table for first time).
Also specify the default order of the columns. This is a comma separated list of column names. Available columns
are: `name`, `id`, `description`, `notes`, `edit`, `addedDate`, `lastModified`.
* `TABLE_ASSEMBLIES_DEFAULT_COLUMNS`: The columns in assemblies tables, which are visible by default (when loading table for first time).
Also specify the default order of the columns. This is a comma separated list of column names. Available columns
are: `name`, `id`, `ipn`, `description`, `referencedAssemblies`, `edit`, `addedDate`, `lastModified`.

View file

@ -24,8 +24,17 @@ namespace App\Controller;
use App\DataTables\ErrorDataTable;
use App\DataTables\Filters\PartFilter;
use App\DataTables\Filters\AssemblyFilter;
use App\DataTables\Filters\ProjectFilter;
use App\DataTables\AssemblyDataTable;
use App\DataTables\Filters\AssemblySearchFilter;
use App\DataTables\Filters\PartSearchFilter;
use App\DataTables\Filters\ProjectSearchFilter;
use App\Form\Filters\AssemblyFilterType;
use App\Form\Filters\PartFilterType;
use App\Form\Filters\ProjectFilterType;
use App\DataTables\PartsDataTable;
use App\DataTables\ProjectSearchDataTable;
use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
use App\Entity\Parts\Manufacturer;
@ -33,7 +42,6 @@ use App\Entity\Parts\Part;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
use App\Exceptions\InvalidRegexException;
use App\Form\Filters\PartFilterType;
use App\Services\Parts\PartsTableActionHandler;
use App\Services\Trees\NodesListBuilder;
use App\Settings\BehaviorSettings\SidebarSettings;
@ -160,25 +168,52 @@ class PartListsController extends AbstractController
$formRequest = clone $request;
$formRequest->setMethod('GET');
$filter = new PartFilter($this->nodesListBuilder);
if($filter_changer !== null){
$filterType = $additional_table_vars['filterType'] ?? PartFilter::class;
unset($additional_table_vars['filterType']);
if ($filterType === PartFilter::class) {
$filter = new PartFilter($this->nodesListBuilder);
} elseif ($filterType === AssemblyFilter::class) {
$filter = new AssemblyFilter($this->nodesListBuilder);
} elseif ($filterType === ProjectFilter::class) {
$filter = new ProjectFilter($this->nodesListBuilder);
} else {
$filter = null;
}
if ($filter !== null && $filter_changer !== null) {
$filter_changer($filter);
}
//If we are in a post request for the tables, we only have to apply the filter form if the submit query param was set
//This saves us some time from creating this complicated term on simple list pages, where no special filter is applied
$filterForm = null;
if ($request->getMethod() !== 'POST' || $request->query->has('part_filter')) {
$filterForm = $this->createForm(PartFilterType::class, $filter, ['method' => 'GET']);
if ($form_changer !== null) {
$form_changer($filterForm);
}
if ($request->getMethod() !== 'POST' || $request->query->has('part_filter') || $request->query->has('assembly_filter') || $request->query->has('project_filter') || $request->query->has('submit')) {
$formType = match ($filterType) {
PartFilter::class => PartFilterType::class,
AssemblyFilter::class => AssemblyFilterType::class,
ProjectFilter::class => ProjectFilterType::class,
default => null,
};
$filterForm->handleRequest($formRequest);
if ($formType !== null) {
$filterForm = $this->createForm($formType, $filter, ['method' => 'GET']);
if ($form_changer !== null) {
$form_changer($filterForm);
}
$filterForm->handleRequest($formRequest);
}
}
$table = $this->dataTableFactory->createFromType(PartsDataTable::class, array_merge(
['filter' => $filter], $additional_table_vars),
$dataTableType = $additional_table_vars['dataTableType'] ?? PartsDataTable::class;
unset($additional_table_vars['dataTableType']);
$tableOptions = array_merge(
['filter' => $filter], $additional_table_vars);
$table = $this->dataTableFactory->createFromType($dataTableType, $tableOptions,
['pageLength' => $this->tableSettings->fullDefaultPageSize, 'lengthMenu' => PartsDataTable::LENGTH_MENU])
->handleRequest($request);
@ -313,25 +348,99 @@ class PartListsController extends AbstractController
);
}
private function searchRequestToFilter(Request $request): PartSearchFilter
/**
* @return PartSearchFilter|AssemblySearchFilter|ProjectSearchFilter
*/
private function searchRequestToFilter(Request $request): object
{
$filter = new PartSearchFilter($request->query->get('keyword', ''));
$datasource = $request->query->get('datasource', 'parts');
$keyword = $request->query->get('keyword', '');
if ($datasource === 'assemblies') {
$filter = new AssemblySearchFilter($keyword);
$filter->setDatasource($datasource);
$filter->setRegex($request->query->getBoolean('regex'));
//As an unchecked checkbox is not set in the query, the default value for all bools have to be false (which is the default argument value)!
//But if we are coming from a simple search (without search options set), we want to search in all fields by default
if ($request->query->has('name') || $request->query->has('description') || $request->query->has('comment') || $request->query->has('ipn') || $request->query->has('category') || $request->query->has('status') || $request->query->has('dbid')) {
$filter->setName($request->query->getBoolean('name'));
$filter->setDescription($request->query->getBoolean('description'));
$filter->setComment($request->query->getBoolean('comment') || $request->query->getBoolean('notes'));
$filter->setIPN($request->query->getBoolean('ipn'));
$filter->setCategory($request->query->getBoolean('category'));
$filter->setStatus($request->query->getBoolean('status'));
$filter->setDbId($request->query->getBoolean('dbid'));
} else {
//Simple search: search in all fields
$filter->setName(true);
$filter->setDescription(true);
$filter->setComment(true);
$filter->setIPN(true);
$filter->setStatus(true);
}
return $filter;
}
if ($datasource === 'projects') {
$filter = new ProjectSearchFilter($keyword);
$filter->setDatasource($datasource);
$filter->setRegex($request->query->getBoolean('regex'));
//As an unchecked checkbox is not set in the query, the default value for all bools have to be false (which is the default argument value)!
if ($request->query->has('name') || $request->query->has('description') || $request->query->has('comment') || $request->query->has('notes') || $request->query->has('category') || $request->query->has('status') || $request->query->has('dbid')) {
$filter->setName($request->query->getBoolean('name'));
$filter->setDescription($request->query->getBoolean('description'));
$filter->setComment($request->query->getBoolean('comment') || $request->query->getBoolean('notes'));
$filter->setCategory($request->query->getBoolean('category'));
$filter->setStatus($request->query->getBoolean('status'));
$filter->setDbId($request->query->getBoolean('dbid'));
} else {
//Simple search: search in all fields
$filter->setName(true);
$filter->setDescription(true);
$filter->setComment(true);
$filter->setStatus(true);
}
return $filter;
}
$filter = new PartSearchFilter($keyword);
$filter->setDatasource($datasource);
//As an unchecked checkbox is not set in the query, the default value for all bools have to be false (which is the default argument value)!
$filter->setName($request->query->getBoolean('name'));
$filter->setDbId($request->query->getBoolean('dbid'));
$filter->setCategory($request->query->getBoolean('category'));
$filter->setDescription($request->query->getBoolean('description'));
$filter->setMpn($request->query->getBoolean('mpn'));
$filter->setTags($request->query->getBoolean('tags'));
$filter->setStorelocation($request->query->getBoolean('storelocation'));
$filter->setComment($request->query->getBoolean('comment'));
$filter->setIPN($request->query->getBoolean('ipn'));
$filter->setOrdernr($request->query->getBoolean('ordernr'));
$filter->setSupplier($request->query->getBoolean('supplier'));
$filter->setManufacturer($request->query->getBoolean('manufacturer'));
$filter->setFootprint($request->query->getBoolean('footprint'));
$filter->setAssembly($request->query->getBoolean('assembly'));
if ($request->query->has('name') || $request->query->has('description') || $request->query->has('comment') || $request->query->has('ipn') || $request->query->has('category') || $request->query->has('dbid') || $request->query->has('mpn') || $request->query->has('tags') || $request->query->has('storelocation') || $request->query->has('ordernr') || $request->query->has('supplier') || $request->query->has('manufacturer') || $request->query->has('footprint') || $request->query->has('assembly') || $request->query->has('manufacturing_status')) {
$filter->setName($request->query->getBoolean('name'));
$filter->setDbId($request->query->getBoolean('dbid'));
$filter->setCategory($request->query->getBoolean('category'));
$filter->setDescription($request->query->getBoolean('description'));
$filter->setMpn($request->query->getBoolean('mpn'));
$filter->setTags($request->query->getBoolean('tags'));
$filter->setStorelocation($request->query->getBoolean('storelocation'));
$filter->setComment($request->query->getBoolean('comment'));
$filter->setManufacturingStatus($request->query->getBoolean('manufacturing_status'));
$filter->setIPN($request->query->getBoolean('ipn'));
$filter->setOrdernr($request->query->getBoolean('ordernr'));
$filter->setSupplier($request->query->getBoolean('supplier'));
$filter->setManufacturer($request->query->getBoolean('manufacturer'));
$filter->setFootprint($request->query->getBoolean('footprint'));
$filter->setAssembly($request->query->getBoolean('assembly'));
} else {
//Simple search: search in all fields
$filter->setName(true);
$filter->setCategory(true);
$filter->setDescription(true);
$filter->setComment(true);
$filter->setManufacturingStatus(true);
$filter->setTags(true);
$filter->setStorelocation(true);
$filter->setOrdernr(true);
$filter->setMpn(true);
$filter->setIPN(true);
$filter->setAssembly(true);
}
$filter->setRegex($request->query->getBoolean('regex'));
@ -342,17 +451,34 @@ class PartListsController extends AbstractController
public function showSearch(Request $request, DataTableFactory $dataTable): Response
{
$searchFilter = $this->searchRequestToFilter($request);
$datasource = $request->query->get('datasource', 'parts');
$dataTableType = PartsDataTable::class;
$template = 'parts/lists/search_list.html.twig';
$filterType = PartFilter::class;
if ($searchFilter instanceof AssemblySearchFilter) {
$dataTableType = AssemblyDataTable::class;
$filterType = AssemblyFilter::class;
} elseif ($searchFilter instanceof ProjectSearchFilter) {
$dataTableType = ProjectSearchDataTable::class;
$filterType = ProjectFilter::class;
}
return $this->showListWithFilter($request,
'parts/lists/search_list.html.twig',
$template,
null,
null,
[
'keyword' => $searchFilter->getKeyword(),
'searchFilter' => $searchFilter,
'dataTableType' => $dataTableType,
'datasource' => $datasource,
],
[
'search' => $searchFilter,
'dataTableType' => $dataTableType,
'filterType' => $filterType,
]
);
}

View file

@ -136,7 +136,7 @@ class TypeaheadController extends AbstractController
$partRepository = $entityManager->getRepository(Part::class);
$parts = $partRepository->autocompleteSearch($query, 100);
$parts = $partRepository->autocompleteSearch($query, 10);
/** @var Part[]|Assembly[] $data */
$data = [];
@ -167,7 +167,7 @@ class TypeaheadController extends AbstractController
if ($this->isGranted('@projects.read')) {
$projectRepository = $entityManager->getRepository(Project::class);
$projects = $projectRepository->autocompleteSearch($query, 100);
$projects = $projectRepository->autocompleteSearch($query, 10);
foreach ($projects as $project) {
$preview_attachment = $projectPreviewGenerator->getTablePreviewAttachment($project);
@ -194,7 +194,7 @@ class TypeaheadController extends AbstractController
if ($this->isGranted('@assemblies.read')) {
$assemblyRepository = $entityManager->getRepository(Assembly::class);
$assemblies = $assemblyRepository->autocompleteSearch($query, 100);
$assemblies = $assemblyRepository->autocompleteSearch($query, 10);
foreach ($assemblies as $assembly) {
$preview_attachment = $assemblyPreviewGenerator->getTablePreviewAttachment($assembly);

View file

@ -82,6 +82,8 @@ final class AssemblyDataTable implements DataTableTypeInterface
'label' => '',
'className' => 'no-colvis',
'render' => fn($value, Assembly $context) => $this->assemblyDataTableHelper->renderPicture($context),
'orderable' => false,
'searchable' => false,
], visibility_configurable: false)
->add('name', TextColumn::class, [
'label' => $this->translator->trans('assembly.table.name'),
@ -98,6 +100,10 @@ final class AssemblyDataTable implements DataTableTypeInterface
->add('description', MarkdownColumn::class, [
'label' => $this->translator->trans('assembly.table.description'),
])
->add('comment', MarkdownColumn::class, [
'label' => $this->translator->trans('assembly.table.comment'),
'render' => fn($value, Assembly $context) => $context->getComment()
])
->add('addedDate', LocaleDateTimeColumn::class, [
'label' => $this->translator->trans('assembly.table.addedDate'),
])
@ -222,11 +228,23 @@ final class AssemblyDataTable implements DataTableTypeInterface
//The join fields get prefixed with an underscore, so we can check if they are used in the query easy without confusing them for a assembly subfield
$dql = $builder->getDQL();
if (str_contains($dql, '_master_picture_attachment')) {
//Helper function to check if a join alias is already present in the QueryBuilder
$hasJoin = static function (QueryBuilder $qb, string $alias): bool {
foreach ($qb->getDQLPart('join') as $joins) {
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
}
return false;
};
if (str_contains($dql, '_master_picture_attachment') && !$hasJoin($builder, '_master_picture_attachment')) {
$builder->leftJoin('assembly.master_picture_attachment', '_master_picture_attachment');
$builder->addGroupBy('_master_picture_attachment');
}
if (str_contains($dql, '_attachments')) {
if (str_contains($dql, '_attachments') && !$hasJoin($builder, '_attachments')) {
$builder->leftJoin('assembly.attachments', '_attachments');
}

View file

@ -22,10 +22,12 @@ declare(strict_types=1);
*/
namespace App\DataTables\Filters;
use App\DataTables\Filters\Constraints\ChoiceConstraint;
use App\DataTables\Filters\Constraints\DateTimeConstraint;
use App\DataTables\Filters\Constraints\EntityConstraint;
use App\DataTables\Filters\Constraints\IntConstraint;
use App\DataTables\Filters\Constraints\TextConstraint;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\AttachmentType;
use App\Services\Trees\NodesListBuilder;
use Doctrine\ORM\QueryBuilder;
@ -40,6 +42,8 @@ class AssemblyFilter implements FilterInterface
public readonly TextConstraint $name;
public readonly TextConstraint $description;
public readonly TextConstraint $comment;
public readonly ChoiceConstraint $status;
public readonly EntityConstraint $category;
public readonly DateTimeConstraint $lastModified;
public readonly DateTimeConstraint $addedDate;
@ -52,6 +56,8 @@ class AssemblyFilter implements FilterInterface
$this->name = new TextConstraint('assembly.name');
$this->description = new TextConstraint('assembly.description');
$this->comment = new TextConstraint('assembly.comment');
$this->status = new ChoiceConstraint('assembly.status');
$this->category = new EntityConstraint($nodesListBuilder, Assembly::class, 'assembly.parent');
$this->dbId = new IntConstraint('assembly.id');
$this->ipn = new TextConstraint('assembly.ipn');
$this->addedDate = new DateTimeConstraint('assembly.addedDate');

View file

@ -30,19 +30,34 @@ class AssemblySearchFilter implements FilterInterface
protected bool $regex = false;
/** @var bool Use name field for searching */
protected bool $name = true;
protected bool $name = false;
/** @var bool Use description for searching */
protected bool $description = true;
protected bool $description = false;
/** @var bool Use comment field for searching */
protected bool $comment = true;
/** @var bool Use ordernr for searching */
protected bool $ordernr = true;
protected bool $comment = false;
/** @var bool Use Internal part number for searching */
protected bool $ipn = true;
protected bool $ipn = false;
/** @var bool Use id field for searching */
protected bool $dbId = false;
/**
* If true, we search in the name of the parent assembly (if available).
* This field is named "category" to keep the API consistent with PartSearchFilter,
* although assemblies don't have categories (they only have parents).
*/
protected bool $category = false;
/** @var bool Use status field for searching */
protected bool $status = false;
/** @var string The datasource used for searching */
protected string $datasource = 'assemblies';
protected static int $parameterCounter = 0;
public function __construct(
/** @var string The string to query for */
@ -51,6 +66,13 @@ class AssemblySearchFilter implements FilterInterface
{
}
protected function generateParameterIdentifier(string $property): string
{
//Replace all special characters with underscores
$property = preg_replace('/\W/', '_', $property);
return $property . '_' . (self::$parameterCounter++) . '_';
}
protected function getFieldsToSearch(): array
{
$fields_to_search = [];
@ -67,12 +89,41 @@ class AssemblySearchFilter implements FilterInterface
if ($this->ipn) {
$fields_to_search[] = 'assembly.ipn';
}
if ($this->status) {
$fields_to_search[] = 'assembly.status';
}
if ($this->dbId) {
$fields_to_search[] = 'assembly.id';
}
if ($this->category) {
// We search in the name of the parent assembly.
// This is named category for consistency with PartSearchFilter.
$fields_to_search[] = '_search_parent.name';
}
return $fields_to_search;
}
public function apply(QueryBuilder $queryBuilder): void
{
if ($this->category) {
// We search in the parent assembly.
// Check if the join alias is already present in the QueryBuilder
$hasJoin = false;
foreach ($queryBuilder->getDQLPart('join') as $joins) {
foreach ($joins as $join) {
if ($join->getAlias() === '_search_parent') {
$hasJoin = true;
break 2;
}
}
}
if (!$hasJoin) {
$queryBuilder->leftJoin('assembly.parent', '_search_parent');
}
}
$fields_to_search = $this->getFieldsToSearch();
//If we have nothing to search for, do nothing
@ -80,13 +131,15 @@ class AssemblySearchFilter implements FilterInterface
return;
}
$parameterIdentifier = $this->generateParameterIdentifier('search_query');
//Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field): string {
$expressions = array_map(function (string $field) use ($parameterIdentifier): string {
if ($this->regex) {
return sprintf("REGEXP(%s, :search_query) = TRUE", $field);
return sprintf("REGEXP(%s, :%s) = TRUE", $field, $parameterIdentifier);
}
return sprintf("ILIKE(%s, :search_query) = TRUE", $field);
return sprintf("ILIKE(%s, :%s) = TRUE", $field, $parameterIdentifier);
}, $fields_to_search);
//Add Or concatenation of the expressions to our query
@ -96,9 +149,11 @@ class AssemblySearchFilter implements FilterInterface
//For regex, we pass the query as is, for like we add % to the start and end as wildcards
if ($this->regex) {
$queryBuilder->setParameter('search_query', $this->keyword);
$queryBuilder->setParameter($parameterIdentifier, $this->keyword);
} else {
$queryBuilder->setParameter('search_query', '%' . $this->keyword . '%');
//Escape % and _ characters in the keyword
$keyword_escaped = str_replace(['%', '_'], ['\%', '\_'], $this->keyword);
$queryBuilder->setParameter($parameterIdentifier, '%' . $keyword_escaped . '%');
}
}
@ -162,11 +217,137 @@ class AssemblySearchFilter implements FilterInterface
return $this->comment;
}
public function setComment(bool $comment): AssemblySearchFilter
public function setComment(bool $comment): self
{
$this->comment = $comment;
return $this;
}
public function isCategory(): bool
{
return $this->category;
}
/**
* Set if the parent assembly name should be searched.
* This is named "category" for consistency with PartSearchFilter.
*/
public function setCategory(bool $category): self
{
$this->category = $category;
return $this;
}
public function isStatus(): bool
{
return $this->status;
}
public function setStatus(bool $status): self
{
$this->status = $status;
return $this;
}
public function isMpn(): bool
{
return false;
}
public function setMpn(bool $mpn): self
{
return $this;
}
public function isTags(): bool
{
return false;
}
public function setTags(bool $tags): self
{
return $this;
}
public function isStorelocation(): bool
{
return false;
}
public function setStorelocation(bool $storelocation): self
{
return $this;
}
public function isSupplier(): bool
{
return false;
}
public function setSupplier(bool $supplier): self
{
return $this;
}
public function isManufacturer(): bool
{
return false;
}
public function setManufacturer(bool $manufacturer): self
{
return $this;
}
public function isFootprint(): bool
{
return false;
}
public function setFootprint(bool $footprint): self
{
return $this;
}
public function isDbId(): bool
{
return $this->dbId;
}
public function setDbId(bool $dbId): self
{
$this->dbId = $dbId;
return $this;
}
public function isAssembly(): bool
{
return false;
}
public function setAssembly(bool $assembly): self
{
return $this;
}
public function isOrdernr(): bool
{
return false;
}
public function setOrdernr(bool $ordernr): self
{
return $this;
}
public function getDatasource(): string
{
return $this->datasource;
}
public function setDatasource(string $datasource): self
{
$this->datasource = $datasource;
return $this;
}
}

View file

@ -32,31 +32,31 @@ class PartSearchFilter implements FilterInterface
protected bool $regex = false;
/** @var bool Use name field for searching */
protected bool $name = true;
protected bool $name = false;
/** @var bool Use id field for searching */
protected bool $dbId = false;
/** @var bool Use category name for searching */
protected bool $category = true;
protected bool $category = false;
/** @var bool Use description for searching */
protected bool $description = true;
protected bool $description = false;
/** @var bool Use tags for searching */
protected bool $tags = true;
protected bool $tags = false;
/** @var bool Use storelocation name for searching */
protected bool $storelocation = true;
protected bool $storelocation = false;
/** @var bool Use comment field for searching */
protected bool $comment = true;
protected bool $comment = false;
/** @var bool Use ordernr for searching */
protected bool $ordernr = true;
protected bool $ordernr = false;
/** @var bool Use manufacturer product name for searching */
protected bool $mpn = true;
protected bool $mpn = false;
/** @var bool Use supplier name for searching */
protected bool $supplier = false;
@ -67,11 +67,19 @@ class PartSearchFilter implements FilterInterface
/** @var bool Use footprint name for searching */
protected bool $footprint = false;
/** @var bool Use manufacturing status for searching */
protected bool $manufacturingStatus = false;
/** @var bool Use Internal Part number for searching */
protected bool $ipn = true;
protected bool $ipn = false;
/** @var bool Use assembly name for searching */
protected bool $assembly = true;
protected bool $assembly = false;
/** @var string The datasource used for searching */
protected string $datasource = 'parts';
protected static int $parameterCounter = 0;
public function __construct(
/** @var string The string to query for */
@ -80,6 +88,13 @@ class PartSearchFilter implements FilterInterface
{
}
protected function generateParameterIdentifier(string $property): string
{
//Replace all special characters with underscores
$property = preg_replace('/\W/', '_', $property);
return $property . '_' . (self::$parameterCounter++) . '_';
}
protected function getFieldsToSearch(): array
{
$fields_to_search = [];
@ -88,7 +103,7 @@ class PartSearchFilter implements FilterInterface
$fields_to_search[] = 'part.name';
}
if($this->category) {
$fields_to_search[] = '_category.name';
$fields_to_search[] = '_search_category.name';
}
if($this->description) {
$fields_to_search[] = 'part.description';
@ -100,29 +115,32 @@ class PartSearchFilter implements FilterInterface
$fields_to_search[] = 'part.tags';
}
if($this->storelocation) {
$fields_to_search[] = '_storelocations.name';
$fields_to_search[] = '_search_storelocations.name';
}
if($this->ordernr) {
$fields_to_search[] = '_orderdetails.supplierpartnr';
$fields_to_search[] = '_search_orderdetails.supplierpartnr';
}
if($this->mpn) {
$fields_to_search[] = 'part.manufacturer_product_number';
}
if($this->supplier) {
$fields_to_search[] = '_suppliers.name';
$fields_to_search[] = '_search_suppliers.name';
}
if($this->manufacturer) {
$fields_to_search[] = '_manufacturer.name';
$fields_to_search[] = '_search_manufacturer.name';
}
if($this->footprint) {
$fields_to_search[] = '_footprint.name';
$fields_to_search[] = '_search_footprint.name';
}
if($this->manufacturingStatus) {
$fields_to_search[] = 'part.manufacturing_status';
}
if ($this->ipn) {
$fields_to_search[] = 'part.ipn';
}
if ($this->assembly) {
$fields_to_search[] = '_assembly.name';
$fields_to_search[] = '_assembly.ipn';
$fields_to_search[] = '_search_assembly.name';
$fields_to_search[] = '_search_assembly.ipn';
}
return $fields_to_search;
@ -130,6 +148,33 @@ class PartSearchFilter implements FilterInterface
public function apply(QueryBuilder $queryBuilder): void
{
if ($this->category) {
$queryBuilder->leftJoin('part.category', '_search_category');
}
if ($this->storelocation) {
$queryBuilder->leftJoin('part.partLots', '_search_partLots')
->leftJoin('_search_partLots.storage_location', '_search_storelocations');
}
if ($this->ordernr) {
$queryBuilder->leftJoin('part.orderdetails', '_search_orderdetails');
}
if ($this->supplier) {
if (!$this->ordernr) {
$queryBuilder->leftJoin('part.orderdetails', '_search_orderdetails');
}
$queryBuilder->leftJoin('_search_orderdetails.supplier', '_search_suppliers');
}
if ($this->manufacturer) {
$queryBuilder->leftJoin('part.manufacturer', '_search_manufacturer');
}
if ($this->footprint) {
$queryBuilder->leftJoin('part.footprint', '_search_footprint');
}
if ($this->assembly) {
$queryBuilder->leftJoin('part.assembly_bom_entries', '_search_assemblyBomEntries')
->leftJoin('_search_assemblyBomEntries.assembly', '_search_assembly');
}
$fields_to_search = $this->getFieldsToSearch();
$is_numeric = preg_match('/^\d+$/', $this->keyword) === 1;
@ -141,32 +186,34 @@ class PartSearchFilter implements FilterInterface
return;
}
$parameterIdentifier = $this->generateParameterIdentifier('search_query');
$expressions = [];
if($fields_to_search !== []) {
//Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field): string {
$expressions = array_map(function (string $field) use ($parameterIdentifier): string {
if ($this->regex) {
return sprintf("REGEXP(%s, :search_query) = TRUE", $field);
return sprintf("REGEXP(%s, :%s) = TRUE", $field, $parameterIdentifier);
}
return sprintf("ILIKE(%s, :search_query) = TRUE", $field);
return sprintf("ILIKE(%s, :%s) = TRUE", $field, $parameterIdentifier);
}, $fields_to_search);
//For regex, we pass the query as is, for like we add % to the start and end as wildcards
if ($this->regex) {
$queryBuilder->setParameter('search_query', $this->keyword);
$queryBuilder->setParameter($parameterIdentifier, $this->keyword);
} else {
//Escape % and _ characters in the keyword
$this->keyword = str_replace(['%', '_'], ['\%', '\_'], $this->keyword);
$queryBuilder->setParameter('search_query', '%' . $this->keyword . '%');
$keyword_escaped = str_replace(['%', '_'], ['\%', '\_'], $this->keyword);
$queryBuilder->setParameter($parameterIdentifier, '%' . $keyword_escaped . '%');
}
}
//Use equal expression to just search for exact numeric matches
if ($search_dbId) {
$expressions[] = $queryBuilder->expr()->eq('part.id', ':id_exact');
$queryBuilder->setParameter('id_exact', (int) $this->keyword,
$idParameterIdentifier = $this->generateParameterIdentifier('id_exact');
$expressions[] = $queryBuilder->expr()->eq('part.id', ':' . $idParameterIdentifier);
$queryBuilder->setParameter($idParameterIdentifier, (int) $this->keyword,
ParameterType::INTEGER);
}
@ -333,6 +380,17 @@ class PartSearchFilter implements FilterInterface
return $this;
}
public function isManufacturingStatus(): bool
{
return $this->manufacturingStatus;
}
public function setManufacturingStatus(bool $manufacturingStatus): PartSearchFilter
{
$this->manufacturingStatus = $manufacturingStatus;
return $this;
}
public function isComment(): bool
{
return $this->comment;
@ -354,4 +412,31 @@ class PartSearchFilter implements FilterInterface
$this->assembly = $assembly;
return $this;
}
/**
* Dummy method for compatibility with assembly/project search options in Twig.
*/
public function isStatus(): bool
{
return false;
}
/**
* Dummy method for compatibility with assembly/project search options in Twig.
*/
public function setStatus(bool $status): PartSearchFilter
{
return $this;
}
public function getDatasource(): string
{
return $this->datasource;
}
public function setDatasource(string $datasource): PartSearchFilter
{
$this->datasource = $datasource;
return $this;
}
}

View file

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\DataTables\Filters;
use App\DataTables\Filters\Constraints\ChoiceConstraint;
use App\DataTables\Filters\Constraints\DateTimeConstraint;
use App\DataTables\Filters\Constraints\EntityConstraint;
use App\DataTables\Filters\Constraints\IntConstraint;
use App\DataTables\Filters\Constraints\TextConstraint;
use App\Entity\Attachments\AttachmentType;
use App\Entity\ProjectSystem\Project;
use App\Services\Trees\NodesListBuilder;
use Doctrine\ORM\QueryBuilder;
class ProjectFilter implements FilterInterface
{
use CompoundFilterTrait;
public readonly IntConstraint $dbId;
public readonly TextConstraint $name;
public readonly TextConstraint $description;
public readonly TextConstraint $comment;
public readonly ChoiceConstraint $status;
public readonly EntityConstraint $category;
public readonly DateTimeConstraint $lastModified;
public readonly DateTimeConstraint $addedDate;
public readonly IntConstraint $attachmentsCount;
public readonly EntityConstraint $attachmentType;
public readonly TextConstraint $attachmentName;
public function __construct(NodesListBuilder $nodesListBuilder)
{
$this->name = new TextConstraint('project.name');
$this->description = new TextConstraint('project.description');
$this->comment = new TextConstraint('project.comment');
$this->status = new ChoiceConstraint('project.status');
$this->category = new EntityConstraint($nodesListBuilder, Project::class, 'project.parent');
$this->dbId = new IntConstraint('project.id');
$this->addedDate = new DateTimeConstraint('project.addedDate');
$this->lastModified = new DateTimeConstraint('project.lastModified');
$this->attachmentsCount = new IntConstraint('COUNT(_attachments)');
$this->attachmentType = new EntityConstraint($nodesListBuilder, AttachmentType::class, '_attachments.attachment_type');
$this->attachmentName = new TextConstraint('_attachments.name');
}
public function apply(QueryBuilder $queryBuilder): void
{
$this->applyAllChildFilters($queryBuilder);
}
}

View file

@ -0,0 +1,346 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\DataTables\Filters;
use Doctrine\ORM\QueryBuilder;
class ProjectSearchFilter implements FilterInterface
{
/** @var boolean Whether to use regex for searching */
protected bool $regex = false;
/** @var bool Use name field for searching */
protected bool $name = false;
/** @var bool Use description for searching */
protected bool $description = false;
/** @var bool Use comment field for searching */
protected bool $comment = false;
/** @var bool Use status field for searching */
protected bool $status = false;
/**
* If true, we search in the name of the parent project (if available).
* This field is named "category" to keep the API consistent with PartSearchFilter and AssemblySearchFilter,
* although projects don't have categories (they only have parents).
*/
protected bool $category = false;
/** @var bool Use dbId field for searching */
protected bool $dbId = false;
/** @var string The datasource used for searching */
protected string $datasource = 'projects';
protected static int $parameterCounter = 0;
public function __construct(
/** @var string The string to query for */
protected string $keyword
) {
}
protected function generateParameterIdentifier(string $property): string
{
//Replace all special characters with underscores
$property = preg_replace('/\W/', '_', $property);
return $property . '_' . (self::$parameterCounter++) . '_';
}
protected function getFieldsToSearch(): array
{
$fields_to_search = [];
if ($this->name) {
$fields_to_search[] = 'project.name';
}
if ($this->description) {
$fields_to_search[] = 'project.description';
}
if ($this->comment) {
$fields_to_search[] = 'project.comment';
}
if ($this->status) {
$fields_to_search[] = 'project.status';
}
if ($this->category) {
// We search in the name of the parent project.
// This is named category for consistency with PartSearchFilter and AssemblySearchFilter.
$fields_to_search[] = '_search_parent.name';
}
if ($this->dbId) {
$fields_to_search[] = 'project.id';
}
return $fields_to_search;
}
public function apply(QueryBuilder $queryBuilder): void
{
if ($this->category) {
// We search in the parent project.
// Check if the join alias is already present in the QueryBuilder
$hasJoin = false;
foreach ($queryBuilder->getDQLPart('join') as $joins) {
foreach ($joins as $join) {
if ($join->getAlias() === '_search_parent') {
$hasJoin = true;
break 2;
}
}
}
if (!$hasJoin) {
$queryBuilder->leftJoin('project.parent', '_search_parent');
}
}
$fields_to_search = $this->getFieldsToSearch();
//If we have nothing to search for, do nothing
if ($fields_to_search === [] || $this->keyword === '') {
return;
}
$parameterIdentifier = $this->generateParameterIdentifier('search_query');
//Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field) use ($parameterIdentifier): string {
if ($this->regex) {
return sprintf("REGEXP(%s, :%s) = TRUE", $field, $parameterIdentifier);
}
return sprintf("ILIKE(%s, :%s) = TRUE", $field, $parameterIdentifier);
}, $fields_to_search);
//Add Or concatenation of the expressions to our query
$queryBuilder->andWhere(
$queryBuilder->expr()->orX(...$expressions)
);
//For regex, we pass the query as is, for like we add % to the start and end as wildcards
if ($this->regex) {
$queryBuilder->setParameter($parameterIdentifier, $this->keyword);
} else {
//Escape % and _ characters in the keyword
$keyword_escaped = str_replace(['%', '_'], ['\%', '\_'], $this->keyword);
$queryBuilder->setParameter($parameterIdentifier, '%' . $keyword_escaped . '%');
}
}
public function getKeyword(): string
{
return $this->keyword;
}
public function setKeyword(string $keyword): self
{
$this->keyword = $keyword;
return $this;
}
public function isRegex(): bool
{
return $this->regex;
}
public function setRegex(bool $regex): self
{
$this->regex = $regex;
return $this;
}
public function isName(): bool
{
return $this->name;
}
public function setName(bool $name): self
{
$this->name = $name;
return $this;
}
public function isDescription(): bool
{
return $this->description;
}
public function setDescription(bool $description): self
{
$this->description = $description;
return $this;
}
public function isComment(): bool
{
return $this->comment;
}
public function setComment(bool $comment): self
{
$this->comment = $comment;
return $this;
}
public function isStatus(): bool
{
return $this->status;
}
public function setStatus(bool $status): self
{
$this->status = $status;
return $this;
}
public function isCategory(): bool
{
return $this->category;
}
/**
* Set if the parent project name should be searched.
* This is named "category" for consistency with PartSearchFilter and AssemblySearchFilter.
*/
public function setCategory(bool $category): self
{
$this->category = $category;
return $this;
}
public function isMpn(): bool
{
return false;
}
public function setMpn(bool $mpn): self
{
return $this;
}
public function isTags(): bool
{
return false;
}
public function setTags(bool $tags): self
{
return $this;
}
public function isStorelocation(): bool
{
return false;
}
public function setStorelocation(bool $storelocation): self
{
return $this;
}
public function isSupplier(): bool
{
return false;
}
public function setSupplier(bool $supplier): self
{
return $this;
}
public function isManufacturer(): bool
{
return false;
}
public function setManufacturer(bool $manufacturer): self
{
return $this;
}
public function isFootprint(): bool
{
return false;
}
public function setFootprint(bool $footprint): self
{
return $this;
}
public function isDbId(): bool
{
return $this->dbId;
}
public function setDbId(bool $dbId): self
{
$this->dbId = $dbId;
return $this;
}
public function isAssembly(): bool
{
return false;
}
public function setAssembly(bool $assembly): self
{
return $this;
}
public function isOrdernr(): bool
{
return false;
}
public function setOrdernr(bool $ordernr): self
{
return $this;
}
public function isIPN(): bool
{
return false;
}
public function setIPN(bool $ipn): self
{
return $this;
}
public function getDatasource(): string
{
return $this->datasource;
}
public function setDatasource(string $datasource): self
{
$this->datasource = $datasource;
return $this;
}
}

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\DataTables\Helpers;
use App\Entity\Attachments\Attachment;
use App\Entity\ProjectSystem\Project;
use App\Services\Attachments\AttachmentURLGenerator;
use App\Services\Attachments\ProjectPreviewGenerator;
use App\Services\EntityURLGenerator;
/**
* A helper service which contains common code to render columns for project related tables
*/
class ProjectDataTableHelper
{
public function __construct(
private readonly EntityURLGenerator $entityURLGenerator,
private readonly ProjectPreviewGenerator $previewGenerator,
private readonly AttachmentURLGenerator $attachmentURLGenerator
) {
}
public function renderName(Project $context): string
{
return sprintf(
'<a href="%s">%s</a>',
$this->entityURLGenerator->infoURL($context),
htmlspecialchars($context->getName())
);
}
public function renderPicture(Project $context): string
{
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($context);
if (!$preview_attachment instanceof Attachment) {
return '';
}
$title = htmlspecialchars($preview_attachment->getName());
if ($preview_attachment->getFilename()) {
$title .= ' ('.htmlspecialchars($preview_attachment->getFilename()).')';
}
return sprintf(
'<img alt="%s" src="%s" data-thumbnail="%s" class="%s" data-title="%s" data-controller="elements--hoverpic">',
'Project image',
$this->attachmentURLGenerator->getThumbnailURL($preview_attachment),
$this->attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_md'),
'hoverpic project-table-image',
$title
);
}
}

View file

@ -419,6 +419,18 @@ final class PartsDataTable implements DataTableTypeInterface
//The join fields get prefixed with an underscore, so we can check if they are used in the query easy without confusing them for a part subfield
$dql = $builder->getDQL();
//Helper function to check if a join alias is already present in the QueryBuilder
$hasJoin = static function (QueryBuilder $qb, string $alias): bool {
foreach ($qb->getDQLPart('join') as $joins) {
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
}
return false;
};
//Add the amountSum field, if it is used in the query
if (str_contains($dql, 'amountSum')) {
//Calculate amount sum using a subquery, so we can filter and sort by it
@ -433,69 +445,85 @@ final class PartsDataTable implements DataTableTypeInterface
);
}
if (str_contains($dql, '_category')) {
if (str_contains($dql, '_category') && !$hasJoin($builder, '_category')) {
$builder->leftJoin('part.category', '_category');
$builder->addGroupBy('_category');
}
if (str_contains($dql, '_master_picture_attachment')) {
if (str_contains($dql, '_master_picture_attachment') && !$hasJoin($builder, '_master_picture_attachment')) {
$builder->leftJoin('part.master_picture_attachment', '_master_picture_attachment');
$builder->addGroupBy('_master_picture_attachment');
}
if (str_contains($dql, '_partLots') || str_contains($dql, '_storelocations')) {
$builder->leftJoin('part.partLots', '_partLots');
$builder->leftJoin('_partLots.storage_location', '_storelocations');
if (!$hasJoin($builder, '_partLots')) {
$builder->leftJoin('part.partLots', '_partLots');
}
if (str_contains($dql, '_storelocations') && !$hasJoin($builder, '_storelocations')) {
$builder->leftJoin('_partLots.storage_location', '_storelocations');
}
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1
//$builder->addGroupBy('_partLots');
//$builder->addGroupBy('_storelocations');
}
if (str_contains($dql, '_footprint')) {
if (str_contains($dql, '_footprint') && !$hasJoin($builder, '_footprint')) {
$builder->leftJoin('part.footprint', '_footprint');
$builder->addGroupBy('_footprint');
}
if (str_contains($dql, '_manufacturer')) {
if (str_contains($dql, '_manufacturer') && !$hasJoin($builder, '_manufacturer')) {
$builder->leftJoin('part.manufacturer', '_manufacturer');
$builder->addGroupBy('_manufacturer');
}
if (str_contains($dql, '_orderdetails') || str_contains($dql, '_suppliers')) {
$builder->leftJoin('part.orderdetails', '_orderdetails');
$builder->leftJoin('_orderdetails.supplier', '_suppliers');
if (!$hasJoin($builder, '_orderdetails')) {
$builder->leftJoin('part.orderdetails', '_orderdetails');
}
if (str_contains($dql, '_suppliers') && !$hasJoin($builder, '_suppliers')) {
$builder->leftJoin('_orderdetails.supplier', '_suppliers');
}
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1
//$builder->addGroupBy('_orderdetails');
//$builder->addGroupBy('_suppliers');
}
if (str_contains($dql, '_attachments')) {
if (str_contains($dql, '_attachments') && !$hasJoin($builder, '_attachments')) {
$builder->leftJoin('part.attachments', '_attachments');
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1
//$builder->addGroupBy('_attachments');
}
if (str_contains($dql, '_partUnit')) {
if (str_contains($dql, '_partUnit') && !$hasJoin($builder, '_partUnit')) {
$builder->leftJoin('part.partUnit', '_partUnit');
$builder->addGroupBy('_partUnit');
}
if (str_contains($dql, '_partCustomState')) {
if (str_contains($dql, '_partCustomState') && !$hasJoin($builder, '_partCustomState')) {
$builder->leftJoin('part.partCustomState', '_partCustomState');
$builder->addGroupBy('_partCustomState');
}
if (str_contains($dql, '_parameters')) {
if (str_contains($dql, '_parameters') && !$hasJoin($builder, '_parameters')) {
$builder->leftJoin('part.parameters', '_parameters');
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1
//$builder->addGroupBy('_parameters');
}
if (str_contains($dql, '_projectBomEntries')) {
if (str_contains($dql, '_projectBomEntries') && !$hasJoin($builder, '_projectBomEntries')) {
$builder->leftJoin('part.project_bom_entries', '_projectBomEntries');
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1
//$builder->addGroupBy('_projectBomEntries');
}
if (str_contains($dql, '_assembly.')) {
$builder->leftJoin('part.assembly_bom_entries', '_assemblyBomEntries');
$builder->leftJoin('_assemblyBomEntries.assembly', '_assembly');
if (!$hasJoin($builder, '_assemblyBomEntries')) {
$builder->leftJoin('part.assembly_bom_entries', '_assemblyBomEntries');
}
if (!$hasJoin($builder, '_assembly')) {
$builder->leftJoin('_assemblyBomEntries.assembly', '_assembly');
}
}
if (str_contains($dql, '_assemblyBomEntries')) {
if (str_contains($dql, '_assemblyBomEntries') && !$hasJoin($builder, '_assemblyBomEntries')) {
$builder->leftJoin('part.assembly_bom_entries', '_assemblyBomEntries');
}
if (str_contains($dql, '_jobPart')) {
$builder->leftJoin('part.bulkImportJobParts', '_jobPart');
$builder->leftJoin('_jobPart.job', '_bulkImportJob');
if (!$hasJoin($builder, '_jobPart')) {
$builder->leftJoin('part.bulkImportJobParts', '_jobPart');
}
if (!$hasJoin($builder, '_bulkImportJob')) {
$builder->leftJoin('_jobPart.job', '_bulkImportJob');
}
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1
//$builder->addGroupBy('_jobPart');
//$builder->addGroupBy('_bulkImportJob');

View file

@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\DataTables;
use App\DataTables\Adapters\TwoStepORMAdapter;
use App\DataTables\Column\IconLinkColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Helpers\ColumnSortHelper;
use App\DataTables\Helpers\ProjectDataTableHelper;
use App\DataTables\Filters\ProjectFilter;
use App\DataTables\Filters\ProjectSearchFilter;
use App\DataTables\Column\MarkdownColumn;
use App\Entity\ProjectSystem\Project;
use App\Doctrine\Helpers\FieldHelper;
use App\Services\EntityURLGenerator;
use App\Settings\BehaviorSettings\TableSettings;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\QueryBuilder;
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
use Omines\DataTablesBundle\Column\TextColumn;
use Omines\DataTablesBundle\DataTable;
use Omines\DataTablesBundle\DataTableTypeInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
class ProjectSearchDataTable implements DataTableTypeInterface
{
public function __construct(
private readonly ProjectDataTableHelper $projectDataTableHelper,
private readonly TranslatorInterface $translator,
private readonly EntityURLGenerator $urlGenerator,
private readonly Security $security,
private readonly ColumnSortHelper $csh,
private readonly TableSettings $tableSettings
) {
}
public function configureOptions(OptionsResolver $optionsResolver): void
{
$optionsResolver->setDefaults([
'filter' => null,
'search' => null,
]);
$optionsResolver->setAllowedTypes('filter', [ProjectFilter::class, 'null']);
$optionsResolver->setAllowedTypes('search', [ProjectSearchFilter::class, 'null']);
}
public function configure(DataTable $dataTable, array $options): void
{
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
$options = $resolver->resolve($options);
$this->csh
->add('picture', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'render' => function ($value, Project $context): string {
return $this->projectDataTableHelper->renderPicture($context);
},
'orderable' => false,
'searchable' => false,
], visibility_configurable: false)
->add('name', TextColumn::class, [
'label' => $this->translator->trans('project.table.name'),
'render' => function ($value, Project $context): string {
return $this->projectDataTableHelper->renderName($context);
},
'orderField' => 'NATSORT(project.name)'
])
->add('id', TextColumn::class, [
'label' => $this->translator->trans('project.table.id'),
])
->add('description', MarkdownColumn::class, [
'label' => $this->translator->trans('project.table.description'),
])
->add('comment', MarkdownColumn::class, [
'label' => $this->translator->trans('project.table.comment'),
])
->add('addedDate', LocaleDateTimeColumn::class, [
'label' => $this->translator->trans('project.table.addedDate'),
])
->add('lastModified', LocaleDateTimeColumn::class, [
'label' => $this->translator->trans('project.table.lastModified'),
])
->add('edit', IconLinkColumn::class, [
'label' => $this->translator->trans('project.table.edit'),
'href' => fn($value, Project $context) => $this->urlGenerator->editURL($context),
'disabled' => fn($value, Project $context) => !$this->security->isGranted('edit', $context),
'title' => $this->translator->trans('project.table.edit.title'),
]);
//Apply the user configured order and visibility and add the columns to the table
$this->csh->applyVisibilityAndConfigureColumns($dataTable, $this->tableSettings->projectsDefaultColumns,
"TABLE_PROJECTS_DEFAULT_COLUMNS");
$dataTable->addOrderBy('name')
->createAdapter(TwoStepORMAdapter::class, [
'filter_query' => $this->getFilterQuery(...),
'detail_query' => $this->getDetailQuery(...),
'entity' => Project::class,
'hydrate' => AbstractQuery::HYDRATE_OBJECT,
'simple_total_query' => true,
'criteria' => [
function (QueryBuilder $builder) use ($options): void {
if ($options['search'] instanceof ProjectSearchFilter) {
$options['search']->apply($builder);
}
if ($options['filter'] instanceof ProjectFilter) {
$options['filter']->apply($builder);
}
},
new SearchCriteriaProvider(),
],
'query_modifier' => $this->addJoins(...),
]);
}
public function getFilterQuery(QueryBuilder $builder): void
{
$builder
->select('project.id')
->from(Project::class, 'project');
$this->addJoins($builder);
}
private function addJoins(QueryBuilder $builder): QueryBuilder
{
$dql = $builder->getDQL();
//Helper function to check if a join alias is already present in the QueryBuilder
$hasJoin = static function (QueryBuilder $qb, string $alias): bool {
foreach ($qb->getDQLPart('join') as $joins) {
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
}
return false;
};
if (str_contains($dql, '_master_picture_attachment') && !$hasJoin($builder, '_master_picture_attachment')) {
$builder->leftJoin('project.master_picture_attachment', '_master_picture_attachment');
}
return $builder;
}
public function getDetailQuery(QueryBuilder $builder, array $filter_results): void
{
$ids = array_map(static fn($row) => $row['id'], $filter_results);
$builder
->select('project')
->from(Project::class, 'project')
->where('project.id IN (:ids)')
->setParameter('ids', $ids);
//Get the results in the same order as the IDs were passed
FieldHelper::addOrderByFieldParam($builder, 'project.id', 'ids');
}
}

View file

@ -23,7 +23,9 @@ declare(strict_types=1);
namespace App\Form\Filters;
use App\DataTables\Filters\AssemblyFilter;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\AttachmentType;
use App\Form\Filters\Constraints\ChoiceConstraintType;
use App\Form\Filters\Constraints\DateTimeConstraintType;
use App\Form\Filters\Constraints\NumberConstraintType;
use App\Form\Filters\Constraints\StructuralEntityConstraintType;
@ -59,6 +61,24 @@ class AssemblyFilterType extends AbstractType
'label' => 'assembly.filter.description',
]);
$builder->add('category', StructuralEntityConstraintType::class, [
'label' => 'assembly.filter.parent',
'entity_class' => Assembly::class,
]);
$status_choices = [
'assembly.status.draft' => 'draft',
'assembly.status.planning' => 'planning',
'assembly.status.in_production' => 'in_production',
'assembly.status.finished' => 'finished',
'assembly.status.archived' => 'archived',
];
$builder->add('status', ChoiceConstraintType::class, [
'label' => 'assembly.filter.status',
'choices' => $status_choices,
]);
$builder->add('comment', TextConstraintType::class, [
'label' => 'assembly.filter.comment'
]);

View file

@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\Form\Filters;
use App\DataTables\Filters\ProjectFilter;
use App\Entity\Attachments\AttachmentType;
use App\Entity\ProjectSystem\Project;
use App\Form\Filters\Constraints\ChoiceConstraintType;
use App\Form\Filters\Constraints\DateTimeConstraintType;
use App\Form\Filters\Constraints\NumberConstraintType;
use App\Form\Filters\Constraints\StructuralEntityConstraintType;
use App\Form\Filters\Constraints\TextConstraintType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ResetType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProjectFilterType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'compound' => true,
'data_class' => ProjectFilter::class,
'csrf_protection' => false,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
/*
* Common tab
*/
$builder->add('name', TextConstraintType::class, [
'label' => 'project.filter.name',
]);
$builder->add('description', TextConstraintType::class, [
'label' => 'project.filter.description',
]);
$builder->add('category', StructuralEntityConstraintType::class, [
'label' => 'project.filter.parent',
'entity_class' => Project::class,
]);
$status_choices = [
'project.status.draft' => 'draft',
'project.status.planning' => 'planning',
'project.status.in_production' => 'in_production',
'project.status.finished' => 'finished',
'project.status.archived' => 'archived',
];
$builder->add('status', ChoiceConstraintType::class, [
'label' => 'project.filter.status',
'choices' => $status_choices,
]);
$builder->add('comment', TextConstraintType::class, [
'label' => 'project.filter.comment'
]);
/*
* Advanced tab
*/
$builder->add('dbId', NumberConstraintType::class, [
'label' => 'project.filter.dbId',
'min' => 1,
'step' => 1,
]);
$builder->add('lastModified', DateTimeConstraintType::class, [
'label' => 'lastModified'
]);
$builder->add('addedDate', DateTimeConstraintType::class, [
'label' => 'createdAt'
]);
/**
* Attachments count
*/
$builder->add('attachmentsCount', NumberConstraintType::class, [
'label' => 'project.filter.attachments_count',
'step' => 1,
'min' => 0,
]);
$builder->add('attachmentType', StructuralEntityConstraintType::class, [
'label' => 'attachment.attachment_type',
'entity_class' => AttachmentType::class
]);
$builder->add('attachmentName', TextConstraintType::class, [
'label' => 'project.filter.attachmentName',
]);
$builder->add('submit', SubmitType::class, [
'label' => 'filter.submit',
]);
$builder->add('discard', ResetType::class, [
'label' => 'filter.discard',
]);
}
}

View file

@ -37,13 +37,16 @@ class MarkdownParser
* Mark the markdown for rendering.
* The rendering of markdown is done on client side.
*
* @param string $markdown the Markdown text that should be parsed to html
* @param string|null $markdown the Markdown text that should be parsed to html
* @param bool $inline_mode When true, p blocks will have no margins behind them
*
* @return string the markdown in a version that can be parsed on client side
*/
public function markForRendering(string $markdown, bool $inline_mode = false): string
public function markForRendering(?string $markdown, bool $inline_mode = false): string
{
if ($markdown === null) {
$markdown = '';
}
return sprintf(
'<div class="markdown %s" data-markdown="%s" data-controller="common--markdown">%s</div>',
$inline_mode ? 'markdown-inline' : '', //Add class if inline mode is enabled, to prevent margin after p

View file

@ -33,6 +33,7 @@ enum AssemblyTableColumns : string implements TranslatableInterface
case ID = "id";
case IPN = "ipn";
case DESCRIPTION = "description";
case COMMENT = "comment";
case REFERENCED_ASSEMBLIES = "referencedAssemblies";
case ADDED_DATE = "addedDate";
case LAST_MODIFIED = "lastModified";

View file

@ -0,0 +1,46 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Settings\BehaviorSettings;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
enum ProjectTableColumns : string implements TranslatableInterface
{
case NAME = "name";
case ID = "id";
case DESCRIPTION = "description";
case COMMENT = "comment";
case ADDED_DATE = "addedDate";
case LAST_MODIFIED = "lastModified";
case EDIT = "edit";
public function trans(TranslatorInterface $translator, ?string $locale = null): string
{
$key = match($this) {
default => 'project.table.' . $this->value,
};
return $translator->trans($key, locale: $locale);
}
}

View file

@ -85,6 +85,21 @@ class TableSettings
public array $assembliesDefaultColumns = [AssemblyTableColumns::ID, AssemblyTableColumns::IPN, AssemblyTableColumns::NAME,
AssemblyTableColumns::DESCRIPTION, AssemblyTableColumns::REFERENCED_ASSEMBLIES, AssemblyTableColumns::EDIT];
/** @var ProjectTableColumns[] */
#[SettingsParameter(ArrayType::class,
label: new TM("settings.behavior.table.projects_default_columns"),
description: new TM("settings.behavior.table.projects_default_columns.help"),
options: ['type' => EnumType::class, 'options' => ['class' => ProjectTableColumns::class]],
formType: \Symfony\Component\Form\Extension\Core\Type\EnumType::class,
formOptions: ['class' => ProjectTableColumns::class, 'multiple' => true, 'ordered' => true],
envVar: "TABLE_PROJECTS_DEFAULT_COLUMNS", envVarMode: EnvVarMode::OVERWRITE, envVarMapper: [self::class, 'mapProjectsDefaultColumnsEnv']
)]
#[Assert\NotBlank()]
#[Assert\Unique()]
#[Assert\All([new Assert\Type(ProjectTableColumns::class)])]
public array $projectsDefaultColumns = [ProjectTableColumns::NAME, ProjectTableColumns::DESCRIPTION,
ProjectTableColumns::COMMENT, ProjectTableColumns::EDIT];
/** @var AssemblyBomTableColumns[] */
#[SettingsParameter(ArrayType::class,
label: new TM("settings.behavior.table.assemblies_bom_default_columns"),
@ -148,6 +163,22 @@ class TableSettings
return $ret;
}
public static function mapProjectsDefaultColumnsEnv(string $columns): array
{
$exploded = explode(',', $columns);
$ret = [];
foreach ($exploded as $column) {
$enum = ProjectTableColumns::tryFrom($column);
if (!$enum) {
throw new \InvalidArgumentException("Invalid column '$column' in TABLE_PROJECTS_DEFAULT_COLUMNS");
}
$ret[] = $enum;
}
return $ret;
}
public static function mapAssemblyBomsDefaultColumnsEnv(string $columns): array
{
$exploded = explode(',', $columns);

View file

@ -1,86 +1,230 @@
{% macro settings_drodown(show_label_instead_icon = true) %}
{% macro settings_drodown(show_label_instead_icon = true, searchFilter = null, isSearchList = false) %}
<div class="dropdown">
<div class="dropdown" {{ stimulus_controller('elements/search_options', {
'isSearchList': isSearchList or searchFilter is not null
}) }}>
<button class="btn dropdown-toggle my-2" type="button" id="navbar-search-options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-bs-auto-close="true">
{% if show_label_instead_icon %}{% trans %}search.options.label{% endtrans %}{% else %}<i class="fa-solid fa-gear"></i>{% endif %}
<span class="caret"></span>
</button>
<div class="dropdown-menu" aria-labelledby="navbar-search-options">
<div class="px-2" style="width: max-content;">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_name" name="name" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_name" class="form-check-label justify-content-start">{% trans %}name.label{% endtrans %}</label>
<div style="width: max-content;">
<div class="mb-2 px-2">
<label for="search_datasource" class="form-label">{% trans %}datasource{% endtrans %}</label>
<select class="form-select form-select-sm" id="search_datasource" name="datasource"
data-action="change->elements--search-options#onDatasourceChange"
data-elements--search-options-target="datasource">
<option value="parts" {% if (searchFilter and searchFilter.datasource == 'parts') or (not searchFilter and (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty)) %}selected{% endif %}>{% trans %}datasource.parts{% endtrans %}</option>
<option value="projects" {% if (searchFilter and searchFilter.datasource == 'projects') or (not searchFilter and app.request.get('datasource') == 'projects') %}selected{% endif %}>{% trans %}datasource.projects{% endtrans %}</option>
<option value="assemblies" {% if (searchFilter and searchFilter.datasource == 'assemblies') or (not searchFilter and app.request.get('datasource') == 'assemblies') %}selected{% endif %}>{% trans %}datasource.assemblies{% endtrans %}</option>
</select>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_dbid" name="dbid" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_dbid" class="form-check-label justify-content-start">{% trans %}id.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_category" name="category" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_category" class="form-check-label justify-content-start">{% trans %}category.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_description" name="description" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_description" class="form-check-label justify-content-start">{% trans %}description.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_mpn" name="mpn" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_mpn" class="form-check-label justify-content-start">{% trans %}part.edit.mpn{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_tags" name="tags" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_tags" class="form-check-label justify-content-start">{% trans %}tags.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_storelocation" name="storelocation" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_storelocation" class="form-check-label justify-content-start">{% trans %}storelocation.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_comment" name="comment" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_comment" class="form-check-label justify-content-start">{% trans %}comment.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_ipn" name="ipn" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_ipn" class="form-check-label justify-content-start">{% trans %}part.edit.ipn{% endtrans %}</label>
</div>
{% if true %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_supplierpartnr" name="ordernr" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_supplierpartnr" class="form-check-label justify-content-start">{% trans %}orderdetails.edit.supplierpartnr{% endtrans %}</label>
<hr data-elements--search-options-target="divider" class="my-1">
<div data-elements--search-options-target="partOptions" id="part-options-container">
<div class="px-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_name" name="name" value="1"
{% if (searchFilter and searchFilter.name) or (not searchFilter and (app.request.query.has('name') ? app.request.get('name') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_name" class="form-check-label justify-content-start">{% trans %}name.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_dbid" name="dbid" value="1"
{% if (searchFilter and searchFilter.dbId) or (not searchFilter and app.request.get('dbid') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_dbid" class="form-check-label justify-content-start">{% trans %}id.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_category" name="category" value="1"
{% if (searchFilter and searchFilter.category) or (not searchFilter and (app.request.query.has('category') ? app.request.get('category') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_category" class="form-check-label justify-content-start">{% trans %}category.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_description" name="description" value="1"
{% if (searchFilter and searchFilter.description) or (not searchFilter and (app.request.query.has('description') ? app.request.get('description') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_description" class="form-check-label justify-content-start">{% trans %}description.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_mpn" name="mpn" value="1"
{% if (searchFilter and searchFilter.mpn) or (not searchFilter and (app.request.query.has('mpn') ? app.request.get('mpn') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_mpn" class="form-check-label justify-content-start">{% trans %}part.edit.mpn{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_tags" name="tags" value="1"
{% if (searchFilter and searchFilter.tags) or (not searchFilter and (app.request.query.has('tags') ? app.request.get('tags') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_tags" class="form-check-label justify-content-start">{% trans %}tags.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_storelocation" name="storelocation" value="1"
{% if (searchFilter and searchFilter.storelocation) or (not searchFilter and (app.request.query.has('storelocation') ? app.request.get('storelocation') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_storelocation" class="form-check-label justify-content-start">{% trans %}storelocation.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_comment" name="comment" value="1"
{% if (searchFilter and searchFilter.comment is defined and searchFilter.comment) or (not searchFilter and (app.request.query.has('comment') ? app.request.get('comment') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_comment" class="form-check-label justify-content-start">{% trans %}comment.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_manufacturing_status" name="manufacturing_status" value="1"
{% if (searchFilter and searchFilter.manufacturingStatus is defined and searchFilter.manufacturingStatus) or (not searchFilter and (app.request.query.has('manufacturing_status') ? app.request.get('manufacturing_status') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_manufacturing_status" class="form-check-label justify-content-start">{% trans %}part.edit.manufacturing_status{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_ipn" name="ipn" value="1"
{% if (searchFilter and searchFilter.ipn) or (not searchFilter and (app.request.query.has('ipn') ? (app.request.get('ipn') == '1') : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_ipn" class="form-check-label justify-content-start">{% trans %}part.edit.ipn{% endtrans %}</label>
</div>
{% if true %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_ordernr" name="ordernr" value="1"
{% if (searchFilter and searchFilter.ordernr) or (not searchFilter and (app.request.query.has('ordernr') ? app.request.get('ordernr') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_ordernr" class="form-check-label justify-content-start">{% trans %}orderdetails.edit.supplierpartnr{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_supplier" name="supplier" value="1"
{% if (searchFilter and searchFilter.supplier) or (not searchFilter and app.request.get('supplier') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_supplier" class="form-check-label justify-content-start">{% trans %}supplier.label{% endtrans %}</label>
</div>
{% endif %}
{% if true %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_manufacturer" name="manufacturer" value="1"
{% if (searchFilter and searchFilter.manufacturer) or (not searchFilter and app.request.get('manufacturer') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_manufacturer" class="form-check-label justify-content-start">{% trans %}manufacturer.label{% endtrans %}</label>
</div>
{% endif %}
{% if true %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_footprint" name="footprint" value="1"
{% if (searchFilter and searchFilter.footprint) or (not searchFilter and app.request.get('footprint') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_footprint" class="form-check-label justify-content-start">{% trans %}footprint.label{% endtrans %}</label>
</div>
{% endif %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly" name="assembly" value="1"
{% if (searchFilter and searchFilter.assembly) or (not searchFilter and (app.request.query.has('assembly') ? app.request.get('assembly') == '1' : (app.request.get('datasource') == 'parts' or app.request.get('datasource') is empty))) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly" class="form-check-label justify-content-start">{% trans %}assembly.label{% endtrans %}</label>
</div>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_supplier" name="supplier" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_supplier" class="form-check-label justify-content-start">{% trans %}supplier.label{% endtrans %}</label>
</div>
<div data-elements--search-options-target="assemblyOptions" id="assembly-options-container" class="d-none">
<div class="px-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_name" name="name" value="1"
{% if (searchFilter and searchFilter.name) or (not searchFilter and (app.request.query.has('name') ? app.request.get('name') == '1' : app.request.get('datasource') == 'assemblies')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_name" class="form-check-label justify-content-start">{% trans %}name.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_dbid" name="dbid" value="1"
{% if (searchFilter and searchFilter.dbId) or (not searchFilter and app.request.get('dbid') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_dbid" class="form-check-label justify-content-start">{% trans %}id.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_ipn" name="ipn" value="1"
{% if (searchFilter and searchFilter.ipn) or (not searchFilter and (app.request.query.has('ipn') ? (app.request.get('ipn') == '1' or app.request.get('iPN') == '1') : app.request.get('datasource') == 'assemblies')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_ipn" class="form-check-label justify-content-start">{% trans %}assembly.edit.ipn{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_description" name="description" value="1"
{% if (searchFilter and searchFilter.description) or (not searchFilter and (app.request.query.has('description') ? app.request.get('description') == '1' : app.request.get('datasource') == 'assemblies')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_description" class="form-check-label justify-content-start">{% trans %}description.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_status" name="status" value="1"
{% if (searchFilter and searchFilter.status) or (not searchFilter and (app.request.query.has('status') ? app.request.get('status') == '1' : app.request.get('datasource') == 'assemblies')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_status" class="form-check-label justify-content-start">{% trans %}assembly.edit.status{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_category" name="category" value="1"
{% if (searchFilter and searchFilter.category) or (not searchFilter and (app.request.query.has('category') ? app.request.get('category') == '1' : app.request.get('datasource') == 'assemblies')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_category" class="form-check-label justify-content-start">{% trans %}assembly.filter.parent{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly_notes" name="comment" value="1"
{% if (searchFilter and searchFilter.comment) or (not searchFilter and (app.request.query.has('notes') or app.request.query.has('comment') ? (app.request.get('notes') == '1' or app.request.get('comment') == '1') : app.request.get('datasource') == 'assemblies')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_assembly_notes" class="form-check-label justify-content-start">{% trans %}assembly.filter.comment{% endtrans %}</label>
</div>
</div>
{% endif %}
{% if true %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_manufacturer" name="manufacturer" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_manufacturer" class="form-check-label justify-content-start">{% trans %}manufacturer.label{% endtrans %}</label>
</div>
<div data-elements--search-options-target="projectOptions" id="project-options-container" class="d-none">
<div class="px-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_project_name" name="name" value="1"
{% if (searchFilter and searchFilter.name) or (not searchFilter and (app.request.query.has('name') ? app.request.get('name') == '1' : app.request.get('datasource') == 'projects')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_project_name" class="form-check-label justify-content-start">{% trans %}name.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_project_dbid" name="dbid" value="1"
{% if (searchFilter and searchFilter.dbId) or (not searchFilter and app.request.get('dbid') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_project_dbid" class="form-check-label justify-content-start">{% trans %}id.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_project_description" name="description" value="1"
{% if (searchFilter and searchFilter.description) or (not searchFilter and (app.request.query.has('description') ? app.request.get('description') == '1' : app.request.get('datasource') == 'projects')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_project_description" class="form-check-label justify-content-start">{% trans %}description.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_project_notes" name="comment" value="1"
{% if (searchFilter and searchFilter.comment) or (not searchFilter and (app.request.query.has('comment') or app.request.query.has('notes') ? (app.request.get('comment') == '1' or app.request.get('notes') == '1') : app.request.get('datasource') == 'projects')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_project_notes" class="form-check-label justify-content-start">{% trans %}project.filter.comment{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_project_status" name="status" value="1"
{% if (searchFilter and searchFilter.status) or (not searchFilter and (app.request.query.has('status') ? app.request.get('status') == '1' : app.request.get('datasource') == 'projects')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_project_status" class="form-check-label justify-content-start">{% trans %}project.edit.status{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_project_category" name="category" value="1"
{% if (searchFilter and searchFilter.category) or (not searchFilter and (app.request.query.has('category') ? app.request.get('category') == '1' : app.request.get('datasource') == 'projects')) %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="search_project_category" class="form-check-label justify-content-start">{% trans %}project.filter.parent{% endtrans %}</label>
</div>
</div>
{% endif %}
{% if true %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_footprint" name="footprint" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_footprint" class="form-check-label justify-content-start">{% trans %}footprint.label{% endtrans %}</label>
</div>
{% endif %}
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_assembly" name="assembly" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="search_assembly" class="form-check-label justify-content-start">{% trans %}assembly.label{% endtrans %}</label>
</div>
<hr>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="regex" name="regex" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<label for="regex" class="form-check-label justify-content-start">{% trans %}search.regexmatching{% endtrans %}</label>
<div class="px-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="regex" name="regex" value="1"
{% if (searchFilter and searchFilter.regex) or (not searchFilter and app.request.get('regex') == '1') %}checked{% endif %}
{{ stimulus_controller('elements/sessionStorage_checkbox') }}>
<label for="regex" class="form-check-label justify-content-start">{% trans %}search.regexmatching{% endtrans %}</label>
</div>
</div>
</div>
</div>
</div>
</div>
{% endmacro %}
{# Render a complete usable search form including the form tags. mode can be "standalone" or "navbar" #}
{% macro search_form(mode = "standalone") %}
{% macro search_form(mode = "standalone", searchFilter = null, isSearchList = false) %}
{% set is_navbar = (mode == "navbar") %}
<form action="{{ path('parts_search') }}" class="d-flex {% if is_navbar %}my-lg-0{% endif%} ms-auto" method="get"
@ -89,7 +233,7 @@
{# Show the options left in navbar #}
{% if is_navbar %}
{{ _self.settings_drodown(is_navbar) }}
{{ _self.settings_drodown(is_navbar, searchFilter, isSearchList) }}
{% endif %}
<div {{ stimulus_controller('elements/part_search') }}
@ -102,14 +246,15 @@
data-autocomplete="{{ path('typeahead_parts', {'query': '__QUERY__'}) }}"
data-detail-url="{{ path('part_info', {'id': '__ID__'}) }}"
data-project-detail-url="{{ path('project_info', {'id': '__ID__'}) }}"
data-assembly-detail-url="{{ path('assembly_info', {'id': '__ID__'}) }}">
data-assembly-detail-url="{{ path('assembly_info', {'id': '__ID__'}) }}"
data-initial-query="{{ searchFilter ? searchFilter.keyword : app.request.get('keyword') }}">
<input type="hidden" name="keyword" required {{ stimulus_target('elements/part_search', 'input') }}>
<input type="hidden" name="keyword" required {{ stimulus_target('elements/part_search', 'input') }} value="{{ searchFilter ? searchFilter.keyword : app.request.get('keyword') }}">
</div>
{# And right in the standalone mode #}
{% if not is_navbar %}
{{ _self.settings_drodown(is_navbar) }}
{{ _self.settings_drodown(is_navbar, searchFilter, isSearchList) }}
{% endif %}
</form>
{% endmacro %}

View file

@ -8,24 +8,32 @@
<li class="nav-item" role="presentation">
<button class="nav-link active" id="filter-common-tab" data-bs-toggle="tab" data-bs-target="#filter-common"><i class="fas fa-id-card fa-fw"></i> {% trans %}part.edit.tab.common{% endtrans %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-manufacturer-tab" data-bs-toggle="tab" data-bs-target="#filter-manufacturer"><i class="fas fa-industry fa-fw"></i> {% trans %}part.edit.tab.manufacturer{% endtrans %}</button>
</li>
{% if filterForm.manufacturer is defined or filterForm.manufacturing_status is defined %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-manufacturer-tab" data-bs-toggle="tab" data-bs-target="#filter-manufacturer"><i class="fas fa-industry fa-fw"></i> {% trans %}part.edit.tab.manufacturer{% endtrans %}</button>
</li>
{% endif %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-advanced-tab" data-bs-toggle="tab" data-bs-target="#filter-advanced"><i class="fas fa-shapes fa-fw"></i> {% trans %}part.edit.tab.advanced{% endtrans %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-stocks-tab" data-bs-toggle="tab" data-bs-target="#filter-stocks"><i class="fas fa-boxes fa-fw"></i> {% trans %}part.edit.tab.part_lots{% endtrans %}</button>
</li>
{% if filterForm.storelocation is defined or filterForm.minAmount is defined %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-stocks-tab" data-bs-toggle="tab" data-bs-target="#filter-stocks"><i class="fas fa-boxes fa-fw"></i> {% trans %}part.edit.tab.part_lots{% endtrans %}</button>
</li>
{% endif %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-attachments-tab" data-bs-toggle="tab" data-bs-target="#filter-attachments"><i class="fas fa-paperclip fa-fw"></i> {% trans %}part.edit.tab.attachments{% endtrans %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-orderdetails-tab" data-bs-toggle="tab" data-bs-target="#filter-orderdetails"><i class="fas fa-shopping-cart fa-fw"></i> {% trans %}part.edit.tab.orderdetails{% endtrans %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-parameters-tab" data-bs-toggle="tab" data-bs-target="#filter-parameters"><i class="fas fa-atlas fa-fw"></i> {% trans %}part.edit.tab.specifications{% endtrans %}</button>
</li>
{% if filterForm.supplier is defined or filterForm.orderdetailsCount is defined %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-orderdetails-tab" data-bs-toggle="tab" data-bs-target="#filter-orderdetails"><i class="fas fa-shopping-cart fa-fw"></i> {% trans %}part.edit.tab.orderdetails{% endtrans %}</button>
</li>
{% endif %}
{% if filterForm.parameters is defined %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-parameters-tab" data-bs-toggle="tab" data-bs-target="#filter-parameters"><i class="fas fa-atlas fa-fw"></i> {% trans %}part.edit.tab.specifications{% endtrans %}</button>
</li>
{% endif %}
{% if filterForm.project is defined %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="filter-projects-tab" data-bs-toggle="tab" data-bs-target="#filter-projects"><i class="fas fa-archive fa-fw"></i> {% trans %}project.labelp{% endtrans %}</button>
@ -43,137 +51,143 @@
{% endif %}
</ul>
{{ form_start(filterForm, {"attr": {"data-controller": "helpers--form-cleanup", "data-action": "helpers--form-cleanup#submit"}}) }}
{% if filterForm %}
{{ form_start(filterForm, {"attr": {"data-controller": "helpers--form-cleanup", "data-action": "helpers--form-cleanup#submit"}}) }}
<div class="tab-content">
<div class="tab-pane active pt-3" id="filter-common" role="tabpanel" aria-labelledby="filter-common-tab" tabindex="0">
{{ form_row(filterForm.name) }}
{% if filterForm.description is defined %}{{ form_row(filterForm.description) }}{% endif %}
{% if filterForm.category is defined %}{{ form_row(filterForm.category) }}{% endif %}
{% if filterForm.footprint is defined %}{{ form_row(filterForm.footprint) }}{% endif %}
{% if filterForm.tags is defined %}{{ form_row(filterForm.tags) }}{% endif %}
{% if filterForm.status is defined %}{{ form_row(filterForm.status) }}{% endif %}
{% if filterForm.comment is defined %}{{ form_row(filterForm.comment) }}{% endif %}
{% if filterForm.notes is defined %}{{ form_row(filterForm.notes) }}{% endif %}
</div>
<div class="tab-pane pt-3" id="filter-manufacturer" role="tabpanel" aria-labelledby="filter-manufacturer-tab" tabindex="0">
{% if filterForm.manufacturer is defined %}{{ form_row(filterForm.manufacturer) }}{% endif %}
{% if filterForm.manufacturing_status is defined %}{{ form_row(filterForm.manufacturing_status) }}{% endif %}
{% if filterForm.manufacturer_product_number is defined %}{{ form_row(filterForm.manufacturer_product_number) }}{% endif %}
{% if filterForm.manufacturer_product_url is defined %}{{ form_row(filterForm.manufacturer_product_url) }}{% endif %}
</div>
<div class="tab-pane pt-3" id="filter-advanced" role="tabpanel" aria-labelledby="filter-advanced-tab" tabindex="0">
{% if filterForm.favorite is defined %}{{ form_row(filterForm.favorite) }}{% endif %}
{% if filterForm.needsReview is defined %}{{ form_row(filterForm.needsReview) }}{% endif %}
{% if filterForm.measurementUnit is defined %}{{ form_row(filterForm.measurementUnit) }}{% endif %}
{% if filterForm.partCustomState is defined %}{{ form_row(filterForm.partCustomState) }}{% endif %}
{% if filterForm.mass is defined %}{{ form_row(filterForm.mass) }}{% endif %}
{% if filterForm.dbId is defined %}{{ form_row(filterForm.dbId) }}{% endif %}
{% if filterForm.ipn is defined %}{{ form_row(filterForm.ipn) }}{% endif %}
{% if filterForm.gtin is defined %}{{ form_row(filterForm.gtin) }}{% endif %}
{% if filterForm.lastModified is defined %}{{ form_row(filterForm.lastModified) }}{% endif %}
{% if filterForm.addedDate is defined %}{{ form_row(filterForm.addedDate) }}{% endif %}
</div>
<div class="tab-pane pt-3" id="filter-stocks" role="tabpanel" aria-labelledby="filter-stocks-tab" tabindex="0">
{% if filterForm.storelocation is defined %}{{ form_row(filterForm.storelocation) }}{% endif %}
{% if filterForm.minAmount is defined %}{{ form_row(filterForm.minAmount) }}{% endif %}
{% if filterForm.amountSum is defined %}{{ form_row(filterForm.amountSum) }}{% endif %}
{% if filterForm.lessThanDesired is defined %}{{ form_row(filterForm.lessThanDesired) }}{% endif %}
{% if filterForm.lotCount is defined %}{{ form_row(filterForm.lotCount) }}{% endif %}
{% if filterForm.lotExpirationDate is defined %}{{ form_row(filterForm.lotExpirationDate) }}{% endif %}
{% if filterForm.lotDescription is defined %}{{ form_row(filterForm.lotDescription) }}{% endif %}
{% if filterForm.lotOwner is defined %}{{ form_row(filterForm.lotOwner) }}{% endif %}
{% if filterForm.lotNeedsRefill is defined %}{{ form_row(filterForm.lotNeedsRefill) }}{% endif %}
{% if filterForm.lotUnknownAmount is defined %}{{ form_row(filterForm.lotUnknownAmount) }}{% endif %}
</div>
<div class="tab-pane pt-3" id="filter-attachments" role="tabpanel" aria-labelledby="filter-attachments-tab" tabindex="0">
{% if filterForm.attachmentsCount is defined %}{{ form_row(filterForm.attachmentsCount) }}{% endif %}
{% if filterForm.attachmentType is defined %}{{ form_row(filterForm.attachmentType) }}{% endif %}
{% if filterForm.attachmentName is defined %}{{ form_row(filterForm.attachmentName) }}{% endif %}
</div>
<div class="tab-pane pt-3" id="filter-orderdetails" role="tabpanel" aria-labelledby="filter-orderdetails-tab" tabindex="0">
{% if filterForm.supplier is defined %}{{ form_row(filterForm.supplier) }}{% endif %}
{% if filterForm.orderdetailsCount is defined %}{{ form_row(filterForm.orderdetailsCount) }}{% endif %}
{% if filterForm.obsolete is defined %}{{ form_row(filterForm.obsolete) }}{% endif %}
</div>
<div class="tab-pane pt-3" id="filter-parameters" role="tabpanel" aria-labelledby="filter-parameters-tab" tabindex="0">
{% if filterForm.parameters is defined %}
{% import 'components/collection_type.macro.html.twig' as collection %}
{{ form_row(filterForm.parametersCount) }}
<div {{ collection.controller(filterForm.parameters) }}>
<table class="table table-striped table-sm" id="lots_table" {{ collection.target() }}>
<thead>
<tr>
<th>{% trans %}specifications.property{% endtrans %}</th>
<th>{% trans %}specifications.symbol{% endtrans %}</th>
<th>{% trans %}specifications.value{% endtrans %}</th>
<th>{% trans %}specifications.unit{% endtrans %}</th>
<th>{% trans %}specifications.text{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for param in filterForm.parameters %}
{{ form_widget(param) }}
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success" {{ collection.create_btn() }}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}filter.constraint.add{% endtrans %}
</button>
</div>
{% endif %}
</div>
{% if filterForm.project is defined %}
<div class="tab-pane pt-3" id="filter-projects" role="tabpanel" aria-labelledby="filter-projects-tab" tabindex="0">
{{ form_row(filterForm.project) }}
{{ form_row(filterForm.bomQuantity) }}
{{ form_row(filterForm.bomName) }}
{{ form_row(filterForm.bomComment) }}
</div>
{% endif %}
{% if filterForm.assembly is defined %}
<div class="tab-pane pt-3" id="filter-assemblies" role="tabpanel" aria-labelledby="filter-assemblies-tab" tabindex="0">
{{ form_row(filterForm.assembly) }}
{{ form_row(filterForm.assemblyBomQuantity) }}
{{ form_row(filterForm.assemblyBomName) }}
</div>
{% endif %}
{% if filterForm.inBulkImportJob is defined %}
<div class="tab-pane pt-3" id="filter-bulk-import" role="tabpanel" aria-labelledby="filter-bulk-import-tab" tabindex="0">
{{ form_row(filterForm.inBulkImportJob) }}
{{ form_row(filterForm.bulkImportJobStatus) }}
{{ form_row(filterForm.bulkImportPartStatus) }}
</div>
{% endif %}
<div class="tab-content">
<div class="tab-pane active pt-3" id="filter-common" role="tabpanel" aria-labelledby="filter-common-tab" tabindex="0">
{{ form_row(filterForm.name) }}
{{ form_row(filterForm.description) }}
{{ form_row(filterForm.category) }}
{{ form_row(filterForm.footprint) }}
{{ form_row(filterForm.tags) }}
{{ form_row(filterForm.comment) }}
</div>
<div class="tab-pane pt-3" id="filter-manufacturer" role="tabpanel" aria-labelledby="filter-manufacturer-tab" tabindex="0">
{{ form_row(filterForm.manufacturer) }}
{{ form_row(filterForm.manufacturing_status) }}
{{ form_row(filterForm.manufacturer_product_number) }}
{{ form_row(filterForm.manufacturer_product_url) }}
</div>
<div class="tab-pane pt-3" id="filter-advanced" role="tabpanel" aria-labelledby="filter-advanced-tab" tabindex="0">
{{ form_row(filterForm.favorite) }}
{{ form_row(filterForm.needsReview) }}
{{ form_row(filterForm.measurementUnit) }}
{{ form_row(filterForm.partCustomState) }}
{{ form_row(filterForm.mass) }}
{{ form_row(filterForm.dbId) }}
{{ form_row(filterForm.ipn) }}
{{ form_row(filterForm.gtin) }}
{{ form_row(filterForm.lastModified) }}
{{ form_row(filterForm.addedDate) }}
</div>
{{ form_row(filterForm.submit) }}
{{ form_row(filterForm.discard) }}
<div class="tab-pane pt-3" id="filter-stocks" role="tabpanel" aria-labelledby="filter-stocks-tab" tabindex="0">
{{ form_row(filterForm.storelocation) }}
{{ form_row(filterForm.minAmount) }}
{{ form_row(filterForm.amountSum) }}
{{ form_row(filterForm.lessThanDesired) }}
{{ form_row(filterForm.lotCount) }}
{{ form_row(filterForm.lotExpirationDate) }}
{{ form_row(filterForm.lotDescription) }}
{{ form_row(filterForm.lotOwner) }}
{{ form_row(filterForm.lotNeedsRefill) }}
{{ form_row(filterForm.lotUnknownAmount) }}
</div>
<div class="tab-pane pt-3" id="filter-attachments" role="tabpanel" aria-labelledby="filter-attachments-tab" tabindex="0">
{{ form_row(filterForm.attachmentsCount) }}
{{ form_row(filterForm.attachmentType) }}
{{ form_row(filterForm.attachmentName) }}
</div>
<div class="tab-pane pt-3" id="filter-orderdetails" role="tabpanel" aria-labelledby="filter-orderdetails-tab" tabindex="0">
{{ form_row(filterForm.supplier) }}
{{ form_row(filterForm.orderdetailsCount) }}
{{ form_row(filterForm.obsolete) }}
</div>
<div class="tab-pane pt-3" id="filter-parameters" role="tabpanel" aria-labelledby="filter-parameters-tab" tabindex="0">
{% import 'components/collection_type.macro.html.twig' as collection %}
{{ form_row(filterForm.parametersCount) }}
<div {{ collection.controller(filterForm.parameters) }}>
<table class="table table-striped table-sm" id="lots_table" {{ collection.target() }}>
<thead>
<tr>
<th>{% trans %}specifications.property{% endtrans %}</th>
<th>{% trans %}specifications.symbol{% endtrans %}</th>
<th>{% trans %}specifications.value{% endtrans %}</th>
<th>{% trans %}specifications.unit{% endtrans %}</th>
<th>{% trans %}specifications.text{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for param in filterForm.parameters %}
{{ form_widget(param) }}
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success" {{ collection.create_btn() }}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}filter.constraint.add{% endtrans %}
</button>
<div class="row mb-3">
<div class="col-sm-9 offset-sm-3">
<button type="button" class="btn btn-danger" {{ stimulus_action('helpers/form_cleanup', 'clearAll') }}>{% trans %}filter.clear_filters{% endtrans %}</button>
</div>
</div>
{% if filterForm.project is defined %}
<div class="tab-pane pt-3" id="filter-projects" role="tabpanel" aria-labelledby="filter-projects-tab" tabindex="0">
{{ form_row(filterForm.project) }}
{{ form_row(filterForm.bomQuantity) }}
{{ form_row(filterForm.bomName) }}
{{ form_row(filterForm.bomComment) }}
</div>
{% endif %}
{% if filterForm.assembly is defined %}
<div class="tab-pane pt-3" id="filter-assemblies" role="tabpanel" aria-labelledby="filter-assemblies-tab" tabindex="0">
{{ form_row(filterForm.assembly) }}
{{ form_row(filterForm.assemblyBomQuantity) }}
{{ form_row(filterForm.assemblyBomName) }}
</div>
{% endif %}
{% if filterForm.inBulkImportJob is defined %}
<div class="tab-pane pt-3" id="filter-bulk-import" role="tabpanel" aria-labelledby="filter-bulk-import-tab" tabindex="0">
{{ form_row(filterForm.inBulkImportJob) }}
{{ form_row(filterForm.bulkImportJobStatus) }}
{{ form_row(filterForm.bulkImportPartStatus) }}
</div>
{# Retain the query parameters of the search form if it is existing #}
{% if searchFilter is defined %}
{% for property, value in searchFilter|to_array %}
<input type="hidden" name="{{ property }}" data-no-clear="true" value="{{ value }}">
{% endfor %}
{% endif %}
</div>
{{ form_row(filterForm.submit) }}
{{ form_row(filterForm.discard) }}
<div class="row mb-3">
<div class="col-sm-9 offset-sm-3">
<button type="button" class="btn btn-danger" {{ stimulus_action('helpers/form_cleanup', 'clearAll') }}>{% trans %}filter.clear_filters{% endtrans %}</button>
</div>
</div>
{# Retain the query parameters of the search form if it is existing #}
{% if searchFilter is defined %}
{% for property, value in searchFilter|to_array %}
<input type="hidden" name="{{ property }}" data-no-clear="true" value="{{ value }}">
{% endfor %}
{{ form_end(filterForm) }}
{% endif %}
{{ form_end(filterForm) }}
</div>
</div>
</div>

View file

@ -1,7 +1,16 @@
{% extends "base.html.twig" %}
{% import "components/search.macro.html.twig" as search %}
{% block title %}
{% trans %}parts_list.search.title{% endtrans %}: {{ keyword }}
{% if datasource == 'assemblies' %}
{% trans %}assembly.search.title{% endtrans %}
{% elseif datasource == 'projects' %}
{% trans %}project.search.title{% endtrans %}
{% else %}
{% trans %}parts_list.search.title{% endtrans %}
{% endif %}
: {{ keyword }}
{% endblock %}
{% block content %}
@ -11,12 +20,27 @@
<div class="accordion-header">
<button class="accordion-button collapsed py-2" data-bs-toggle="collapse" data-bs-target="#searchInfo">
<i class="fa-solid fa-magnifying-glass fa-fw"></i>
{% trans %}parts_list.search.title{% endtrans %}:&nbsp;<b>{{ keyword }}</b>
{% if datasource == 'assemblies' %}
{% trans %}assembly.search.title{% endtrans %}
{% elseif datasource == 'projects' %}
{% trans %}project.search.title{% endtrans %}
{% else %}
{% trans %}parts_list.search.title{% endtrans %}
{% endif %}
:&nbsp;<b>{{ keyword }}</b>
</button>
</div>
<div id="searchInfo" class="accordion-collapse collapse" data-bs-parent="#listAccordion">
<div class="accordion-body">
<h4>{% trans with {"%keyword%": keyword|escape} %}parts_list.search.searching_for{% endtrans %}</h4>
<h4>
{% if datasource == 'assemblies' %}
{% trans with {"%keyword%": keyword|escape} %}parts_list.search.searching_for_assemblies{% endtrans %}
{% elseif datasource == 'projects' %}
{% trans with {"%keyword%": keyword|escape} %}parts_list.search.searching_for_projects{% endtrans %}
{% else %}
{% trans with {"%keyword%": keyword|escape} %}parts_list.search.searching_for{% endtrans %}
{% endif %}
</h4>
{% trans %}parts_list.search_options.caption{% endtrans %}:
@ -25,45 +49,98 @@
<label class="form-check-label justify-content-start">{% trans %}name.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.category %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}category.label{% endtrans %}</label>
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.dbId %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}id.label{% endtrans %}</label>
</div>
{% if datasource == 'parts' or datasource == 'assemblies' or datasource == 'projects' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.category %}checked{% endif %}>
<label class="form-check-label justify-content-start">
{% if datasource == 'parts' %}
{% trans %}category.label{% endtrans %}
{% elseif datasource == 'assemblies' %}
{% trans %}assembly.filter.parent{% endtrans %}
{% else %}
{% trans %}project.filter.parent{% endtrans %}
{% endif %}
</label>
</div>
{% endif %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.description %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}description.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.mpn %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}part.edit.mpn{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.tags %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}tags.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.storelocation %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}storelocation.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.comment %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}comment.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.ordernr %}checked{% endif %}>
<label for="search_supplierpartnr" class="form-check-label justify-content-start">{% trans %}orderdetails.edit.supplierpartnr{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.supplier %}checked{% endif %}>
<label for="search_supplier" class="form-check-label justify-content-start">{% trans %}supplier.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.manufacturer %}checked{% endif %}>
<label for="search_manufacturer" class="form-check-label justify-content-start">{% trans %}manufacturer.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.footprint %}checked{% endif %}>
<label for="search_footprint" class="form-check-label justify-content-start">{% trans %}footprint.label{% endtrans %}</label>
</div>
{% if datasource == 'parts' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.mpn %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}part.edit.mpn{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.tags %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}tags.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.storelocation %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}storelocation.label{% endtrans %}</label>
</div>
{% endif %}
{% if datasource == 'assemblies' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.comment %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}assembly.filter.comment{% endtrans %}</label>
</div>
{% elseif datasource == 'projects' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.comment %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}project.filter.comment{% endtrans %}</label>
</div>
{% else %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.comment %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}comment.label{% endtrans %}</label>
</div>
{% endif %}
{% if datasource == 'parts' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.manufacturingStatus %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}part.edit.manufacturing_status{% endtrans %}</label>
</div>
{% endif %}
{% if datasource == 'parts' or datasource == 'assemblies' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.ipn %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}part.edit.ipn{% endtrans %}</label>
</div>
{% endif %}
{% if datasource == 'parts' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.ordernr %}checked{% endif %}>
<label for="search_supplierpartnr" class="form-check-label justify-content-start">{% trans %}orderdetails.edit.supplierpartnr{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.supplier %}checked{% endif %}>
<label for="search_supplier" class="form-check-label justify-content-start">{% trans %}supplier.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.manufacturer %}checked{% endif %}>
<label for="search_manufacturer" class="form-check-label justify-content-start">{% trans %}manufacturer.label{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.footprint %}checked{% endif %}>
<label for="search_footprint" class="form-check-label justify-content-start">{% trans %}footprint.label{% endtrans %}</label>
</div>
{% endif %}
{% if datasource == 'assemblies' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.status %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}assembly.edit.status{% endtrans %}</label>
</div>
{% elseif datasource == 'projects' %}
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.status %}checked{% endif %}>
<label class="form-check-label justify-content-start">{% trans %}project.edit.status{% endtrans %}</label>
</div>
{% endif %}
<hr>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.regex %}checked{% endif %}>
@ -73,10 +150,13 @@
</div>
</div>
{% include "parts/lists/_filter.html.twig" %}
</div>
{% include "parts/lists/_parts_list.html.twig" %}
<form action="{{ path('parts_search') }}" method="get" class="d-none">
<input type="hidden" name="keyword" value="{{ keyword }}">
{{ search.settings_drodown(true, searchFilter, true) }}
</form>
{% endblock %}

View file

@ -1617,6 +1617,174 @@ Související prvky budou přesunuty nahoru.</target>
<target>Vyhledat díly</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Hledání sestav</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Hledání projektů</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Název</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Popis</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Poznámky</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Přidáno dne</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Naposledy upraveno</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Upravit</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Upravit projekt</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Součástky</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projekty</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Sestavy</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Hledání sestav...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Hledání projektů...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Výchozí sloupce pro tabulky projektů</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Vyberte sloupce, které se mají ve výchozím nastavení zobrazovat v tabulkách projektů.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Stav sestavy</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Nadřazená sestava</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Poznámky</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Název</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Popis</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Poznámky</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Stav projektu</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Nadřazený projekt</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Počet příloh</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Název přílohy</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2937,11 +3105,11 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<segment state="translated">
<source>part.table.addedDate</source>
@ -8722,8 +8890,8 @@ Element 3</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8776,7 +8944,7 @@ Element 3</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="6ZefVTs" name="project.bom_import.flash.invalid_file">
<segment state="translated">
@ -11472,8 +11640,8 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -11526,7 +11694,7 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
@ -12315,13 +12483,13 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1575,6 +1575,174 @@ Underelementer vil blive flyttet opad.</target>
<target>Søg komponenter</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Søgning i baugrupper</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Projektsøgning</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Navn</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Beskrivelse</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Noter</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Tilføjet den</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Sidst ændret den</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Rediger</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Rediger projekt</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Bauteile</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projekter</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Baugrupper</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Søger efter baugrupper...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Søger efter projekter...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Standardkolonner for projekttabeller</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Vælg de kolonner, der skal vises som standard i projekttabeller.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Baugruppestatus</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Overordnet baugruppe</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Noter</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Navn</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Beskrivelse</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Noter</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Projektstatus</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Overordnet projekt</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Antal vedhæftede filer</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Filnavn på vedhæftet fil</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -8671,8 +8839,8 @@ Oversættelsen
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8725,7 +8893,7 @@ Oversættelsen
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="MDJdKXv" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -10472,8 +10640,8 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -10526,7 +10694,7 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="uT5zcT1" name="assembly_list.all.title">
<segment state="translated">

View file

@ -1574,6 +1574,66 @@ Subelemente werden beim Löschen nach oben verschoben.</target>
<target>Bauteilesuche</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Baugruppensuche</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Projektsuche</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Beschreibung</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Notizen</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Hinzugefügt am</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Zuletzt geändert am</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Bearbeiten</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Projekt bearbeiten</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2446,6 +2506,24 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Datenquelle</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Bauteile</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projekte</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Baugruppen</target>
</segment>
</unit>
<unit id="uF_oOxY" name="manufacturer.labelp">
<segment state="translated">
<source>manufacturer.labelp</source>
@ -6827,6 +6905,18 @@ Element 1 -&gt; Element 1.2</target>
<target>Suche Teile mit dem Suchbegriff &lt;b&gt;%keyword%&lt;/b&gt;</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Suche Baugruppen mit dem Suchbegriff &lt;b&gt;%keyword%&lt;/b&gt;</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Suche Projekte mit dem Suchbegriff &lt;b&gt;%keyword%&lt;/b&gt;</target>
</segment>
</unit>
<unit id="4vomKLa" name="parts_list.search_options.caption">
<segment state="translated">
<source>parts_list.search_options.caption</source>
@ -8692,8 +8782,8 @@ Element 1 -&gt; Element 1.2</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8746,7 +8836,7 @@ Element 1 -&gt; Element 1.2</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="6ZefVTs" name="project.bom_import.flash.invalid_file">
<segment state="translated">
@ -10467,12 +10557,24 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
<target>Standardmäßige Spalten für Baugruppentabellen</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Standard-Spalten für Projekte</target>
</segment>
</unit>
<unit id="t4jhp3R" name="settings.behavior.table.assemblies_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.assemblies_default_columns.help</source>
<target>Die Spalten, die standardmäßig in Baugruppentabellen angezeigt werden sollen. Die Reihenfolge der Elemente kann per Drag &amp; Drop geändert werden.</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Wähle die Spalten aus, die standardmäßig in der Projektsuche angezeigt werden sollen.</target>
</segment>
</unit>
<unit id="Uj8ie5E" name="settings.behavior.table.assemblies_bom_default_columns">
<segment state="translated">
<source>settings.behavior.table.assemblies_bom_default_columns</source>
@ -11178,8 +11280,8 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -11232,7 +11334,7 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="X9HUFrv" name="part.table.actions.error">
<segment state="translated">
@ -12614,6 +12716,18 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
<target>Notizen</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Baugruppen-Status</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Übergeordnete Baugruppe</target>
</segment>
</unit>
<unit id="ux2Ws9p" name="assembly.filter.attachments_count">
<segment state="translated">
<source>assembly.filter.attachments_count</source>
@ -12656,6 +12770,12 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
<target>Beschreibung</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Notizen</target>
</segment>
</unit>
<unit id="ahzol5T" name="assembly.table.referencedAssemblies">
<segment state="translated">
<source>assembly.table.referencedAssemblies</source>
@ -12692,6 +12812,96 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
<target>Ungültiger regulärer Ausdruck (regex)</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Beschreibung</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Notizen</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Projekt-Status</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Übergeordnetes Projekt</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Anzahl Dateianhänge</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Name des Dateianhangs</target>
</segment>
</unit>
<unit id="project.table.id" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="project.table.name" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="project.table.description" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Beschreibung</target>
</segment>
</unit>
<unit id="project.table.addedDate" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Hinzugefügt</target>
</segment>
</unit>
<unit id="project.table.lastModified" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Zuletzt bearbeitet</target>
</segment>
</unit>
<unit id="project.table.edit" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Ändern</target>
</segment>
</unit>
<unit id="project.table.edit.title" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Projekt ändern</target>
</segment>
</unit>
<unit id="8uh6vTx" name="assembly.bom.table.id">
<segment state="translated">
<source>assembly.bom.table.id</source>

View file

@ -774,6 +774,174 @@
<target>Αναζήτηση εξαρτημάτων</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Αναζήτηση συναρμολογημάτων</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Αναζήτηση έργων</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Όνομα</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Περιγραφή</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Σχόλια</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Προστέθηκε στις</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Τελευταία τροποποίηση</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Επεξεργασία</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Επεξεργασία έργου</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Εξαρτήματα</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Έργα</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Συναρμολογήματα</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Αναζήτηση συναρμολογημάτων...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Αναζήτηση έργων...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Προεπιλεγμένες στήλες για πίνακες έργων</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Επιλέξτε τις στήλες που θα εμφανίζονται από προεπιλογή στους πίνακες έργων.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Κατάσταση συναρμολογήματος</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Γονικό συναρμολόγημα</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Σχόλια</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Όνομα</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Περιγραφή</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Σχόλια</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Κατάσταση έργου</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Γονικό έργο</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Αριθμός συνημμένων</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Όνομα συνημμένου</target>
</segment>
</unit>
<unit id="gwE_D3w" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -946,13 +1114,13 @@
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
@ -1599,8 +1767,8 @@
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -1653,7 +1821,7 @@
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="uT5zcT1" name="assembly_list.all.title">
<segment state="translated">
@ -1877,7 +2045,7 @@
<target>Συναρμολογήσεις</target>
</segment>
</unit>
<unit id="statistics.projects" name="statistics.projects">
<unit id="statistics.projects" name="statistics.projects">
<segment state="translated">
<source>statistics.projects</source>
<target>Έργα</target>

View file

@ -1575,6 +1575,66 @@ Sub elements will be moved upwards.</target>
<target>Search [[part]]</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Assembly search</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Project search</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Description</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Added at</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Last modified at</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Edit</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Edit project</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2447,6 +2507,24 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Data source</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Parts</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projects</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Assemblies</target>
</segment>
</unit>
<unit id="uF_oOxY" name="manufacturer.labelp">
<segment state="translated">
<source>manufacturer.labelp</source>
@ -6828,6 +6906,18 @@ Element 1 -&gt; Element 1.2</target>
<target>Searching parts with keyword &lt;b&gt;%keyword%&lt;/b&gt;</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Searching assemblies with keyword &lt;b&gt;%keyword%&lt;/b&gt;</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Searching projects with keyword &lt;b&gt;%keyword%&lt;/b&gt;</target>
</segment>
</unit>
<unit id="4vomKLa" name="parts_list.search_options.caption">
<segment state="translated">
<source>parts_list.search_options.caption</source>
@ -8687,8 +8777,8 @@ Element 1 -&gt; Element 1.2</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8741,7 +8831,7 @@ Element 1 -&gt; Element 1.2</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="MDJdKXv" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -10468,12 +10558,24 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>Default columns for assembly tables</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Default columns for projects</target>
</segment>
</unit>
<unit id="t4jhp3R" name="settings.behavior.table.assemblies_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.assemblies_default_columns.help</source>
<target>The columns to show by default in assembly tables. Order of items can be changed via drag &amp; drop.</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Select the columns that should be displayed by default in the project search.</target>
</segment>
</unit>
<unit id="Uj8ie5E" name="settings.behavior.table.assemblies_bom_default_columns">
<segment state="translated">
<source>settings.behavior.table.assemblies_bom_default_columns</source>
@ -11179,8 +11281,8 @@ Please note, that you can not impersonate a disabled user. If you try you will g
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -11233,7 +11335,7 @@ Please note, that you can not impersonate a disabled user. If you try you will g
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="X9HUFrv" name="part.table.actions.error">
<segment state="translated">
@ -12636,7 +12738,19 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<unit id="kix5dMf" name="assembly.filter.comment">
<segment state="translated">
<source>assembly.filter.comment</source>
<target>Comments</target>
<target>Notes</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Assembly status</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Parent assembly</target>
</segment>
</unit>
<unit id="ux2Ws9p" name="assembly.filter.attachments_count">
@ -12681,6 +12795,12 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>Description</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="ahzol5T" name="assembly.table.referencedAssemblies">
<segment state="translated">
<source>assembly.table.referencedAssemblies</source>
@ -12717,6 +12837,96 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>Invalid regular expression (regex)</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Description</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Project status</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Parent project</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Number of attachments</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Attachment name</target>
</segment>
</unit>
<unit id="project.table.id" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="project.table.name" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="project.table.description" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Description</target>
</segment>
</unit>
<unit id="project.table.addedDate" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Added</target>
</segment>
</unit>
<unit id="project.table.lastModified" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Last edited</target>
</segment>
</unit>
<unit id="project.table.edit" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Edit</target>
</segment>
</unit>
<unit id="project.table.edit.title" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Edit project</target>
</segment>
</unit>
<unit id="8uh6vTx" name="assembly.bom.table.id">
<segment state="translated">
<source>assembly.bom.table.id</source>

View file

@ -1617,6 +1617,174 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Buscar componentes</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Búsqueda de ensamblajes</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Búsqueda de proyectos</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Nombre</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Descripción</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Notas</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Añadido el</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Última modificación</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Editar</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Editar proyecto</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Piezas</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Proyectos</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Ensamblajes</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Buscando ensamblajes...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Buscando proyectos...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Columnas predeterminadas para tablas de proyectos</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Seleccione las columnas que se mostrarán por defecto en las tablas de proyectos.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Estado del ensamblaje</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Ensamblaje superior</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Notas</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Nombre</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Descripción</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Notas</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Estado del proyecto</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Proyecto superior</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Número de adjuntos</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Nombre del adjunto</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -8752,8 +8920,8 @@ Elemento 3</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8806,7 +8974,7 @@ Elemento 3</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="MDJdKXv" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -10697,8 +10865,8 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -10751,7 +10919,7 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="X9HUFrv" name="part.table.actions.error">
<segment state="translated">
@ -11084,13 +11252,13 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1600,6 +1600,174 @@ Show/Hide sidebar</target>
<target>Recherche de composants</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Recherche d&#039;assemblages</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Recherche de projets</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Nom</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Description</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Ajouté le</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Dernière modification</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Modifier</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Modifier le projet</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Pièces</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projets</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Recherche d&#039;assemblages...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Recherche de projets...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Colonnes par défaut pour les tableaux de projets</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Sélectionnez les colonnes à afficher par défaut dans les tableaux de projets.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Statut de l&#039;assemblage</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Assemblage parent</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Nom</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Description</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Statut du projet</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Projet parent</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Nombre de pièces jointes</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Nom de la pièce jointe</target>
</segment>
</unit>
<unit id="gwE_D3w" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2860,10 +3028,10 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
</unit>
<unit id="o4zUJc5" name="part.table.name.value.for_assembly">
<segment state="translated">
@ -7542,13 +7710,13 @@ exemple de ville</target>
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1528,6 +1528,174 @@
<target>Alkatrészek keresése</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Szerelvények keresése</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Projektek keresése</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Név</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Leírás</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Megjegyzések</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Hozzáadva</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Utoljára módosítva</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Szerkesztés</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Projekt szerkesztése</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Alkatrészek</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projektek</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Szerelvények</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Szerelvények keresése...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Projektek keresése...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Alapértelmezett oszlopok a projekttáblázatokhoz</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Válassza ki a projekttáblázatokban alapértelmezés szerint megjelenítendő oszlopokat.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Szerelvény állapota</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Szülő szerelvény</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Megjegyzések</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Név</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Leírás</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Megjegyzések</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Projekt állapota</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Szülő projekt</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Mellékletek száma</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Melléklet neve</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -11513,13 +11681,13 @@
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1617,6 +1617,174 @@ I sub elementi saranno spostati verso l'alto.</target>
<target>Ricerca componenti</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Ricerca assemblaggi</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Ricerca progetti</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Nome</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Descrizione</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Note</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Aggiunto il</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Ultima modifica</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Modifica</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Modifica progetto</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Parti</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Progetti</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Assemblaggi</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Ricerca assemblaggi in corso...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Ricerca progetti in corso...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Colonne predefinite per le tabelle dei progetti</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Seleziona le colonne da visualizzare per impostazione predefinita nelle tabelle dei progetti.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Stato assemblaggio</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Assemblaggio superiore</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Note</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Nome</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Descrizione</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Note</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Stato progetto</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Progetto superiore</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Numero di allegati</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Nome allegato</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2884,10 +3052,10 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
</unit>
<unit id="o4zUJc5" name="part.table.name.value.for_assembly">
<segment state="translated">
@ -8754,8 +8922,8 @@ Element 3</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8808,7 +8976,7 @@ Element 3</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="MDJdKXv" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -11086,13 +11254,13 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1600,6 +1600,174 @@
<target>部品を検索</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>アセンブリ検索</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>プロジェクト検索</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>名前</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>説明</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>ノート</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>追加日</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>最終更新日</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>編集</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>プロジェクトの編集</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>部品</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>プロジェクト</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>アセンブリ</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>アセンブリを検索中...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>プロジェクトを検索中...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>プロジェクトテーブルのデフォルト列</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>プロジェクトテーブルにデフォルトで表示する列を選択します。</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>アセンブリステータス</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>親アセンブリ</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>ノート</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>名前</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>説明</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>ノート</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>プロジェクトステータス</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>親プロジェクト</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>添付ファイル数</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>添付ファイル名</target>
</segment>
</unit>
<unit id="gwE_D3w" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2860,10 +3028,10 @@
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部品)</target>
</segment>
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部品)</target>
</segment>
</unit>
<unit id="o4zUJc5" name="part.table.name.value.for_assembly">
<segment state="translated">
@ -7261,13 +7429,13 @@ Exampletown</target>
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1132,7 +1132,7 @@
<p><strong>Opmerking:</strong> Er wordt geen mapping uitgevoerd met specifieke componenten uit de categoriebeheer.</p>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
@ -1317,6 +1317,168 @@
<target>Assemblage bewerken</target>
</segment>
</unit>
<unit id="V1p8X9m" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Assemblage zoeken</target>
</segment>
</unit>
<unit id="K3n7B2v" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Project zoeken</target>
</segment>
</unit>
<unit id="M9n2V5c" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Onderdelen</target>
</segment>
</unit>
<unit id="B4v7N1m" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projecten</target>
</segment>
</unit>
<unit id="X8z2L5p" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="H5n8M2v" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Zoeken naar assemblages met trefwoord %keyword%</target>
</segment>
</unit>
<unit id="J3k9P1L" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Zoeken naar projecten mit trefwoord %keyword%</target>
</segment>
</unit>
<unit id="T5v2N7c" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Standaardkolommen voor projecten</target>
</segment>
</unit>
<unit id="R9z1K4m" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Kies welke kolommen standaard worden weergegeven in de projectentabel.</target>
</segment>
</unit>
<unit id="L3n8B2v" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Assemblage-status</target>
</segment>
</unit>
<unit id="P5v2M9n" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Bovenliggende assemblage</target>
</segment>
</unit>
<unit id="N7c1V4p" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Notities</target>
</segment>
</unit>
<unit id="G3n9L2m" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="K5v8N1p" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Naam</target>
</segment>
</unit>
<unit id="M2n7B4v" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Beschrijving</target>
</segment>
</unit>
<unit id="V9c1L5p" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Notities</target>
</segment>
</unit>
<unit id="H3n2N7m" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Projectstatus</target>
</segment>
</unit>
<unit id="P8v1M2v" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Bovenliggend project</target>
</segment>
</unit>
<unit id="J5k9P4L" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Aantal bijlagen</target>
</segment>
</unit>
<unit id="T1n7V5c" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Naam bijlage</target>
</segment>
</unit>
<unit id="Q4v8B2m" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="W2n1M5v" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Projectnaam</target>
</segment>
</unit>
<unit id="S9z3L7p" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Beschrijving</target>
</segment>
</unit>
<unit id="Z5v2N4c" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Toegevoegd op</target>
</segment>
</unit>
<unit id="Y1n8M2p" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Laatst gewijzigd</target>
</segment>
</unit>
<unit id="K3v7N9m" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Bewerken</target>
</segment>
</unit>
<unit id="F2n5L1v" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Project bewerken</target>
</segment>
</unit>
<unit id="juTbw2C" name="assembly.table.invalid_regex">
<segment state="translated">
<source>assembly.table.invalid_regex</source>
@ -1540,13 +1702,13 @@
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="bd7zT1d" name="statistics.assemblies">
<segment state="translated">
@ -1554,7 +1716,7 @@
<target>Assemblages</target>
</segment>
</unit>
<unit id="statistics.projects" name="statistics.projects">
<unit id="statistics.projects" name="statistics.projects">
<segment state="translated">
<source>statistics.projects</source>
<target>Projecten</target>

View file

@ -1614,6 +1614,174 @@ Po usunięciu pod elementy zostaną przeniesione na górę.</target>
<target>Wyszukiwarka komponentów</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Wyszukiwanie zespołów</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Wyszukiwanie projektów</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Nazwa</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Opis</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Notatki</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Dodano dnia</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Ostatnia zmiana</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Edytuj</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Edytuj projekt</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Części</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Projekty</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Zespoły</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Szukanie zespołów...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Szukanie projektów...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Domyślne kolumny dla tabel projektów</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Wybierz kolumny, które mają być domyślnie wyświetlane w tabelach projektów.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Status zespołu</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Zespół nadrzędny</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Notatki</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Nazwa</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Opis</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Notatki</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Status projektu</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Projekt nadrzędny</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Liczba załączników</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Nazwa załącznika</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2879,10 +3047,10 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部品)</target>
</segment>
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部品)</target>
</segment>
</unit>
<unit id="o4zUJc5" name="part.table.name.value.for_assembly">
<segment state="translated">
@ -8749,8 +8917,8 @@ Element 3</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8803,7 +8971,7 @@ Element 3</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="MDJdKXv" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -10931,13 +11099,13 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1618,6 +1618,174 @@
<target>Поиск компонентов</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>Поиск сборок</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>Поиск проектов</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>Имя</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>Описание</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>Примечания</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>Добавлено</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>Последнее изменение</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>Редактировать</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>Редактировать проект</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>Детали</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>Проекты</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>Сборки</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>Поиск сборок...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>Поиск проектов...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>Столбцы по умолчанию для таблиц проектов</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>Выберите столбцы, которые будут отображаться в таблицах проектов по умолчанию.</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>Статус сборки</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>Родительская сборка</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>Примечания</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>Имя</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>Описание</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>Примечания</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>Статус проекта</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>Родительский проект</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>Количество вложений</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>Имя вложения</target>
</segment>
</unit>
<unit id=".nsfK4V" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2885,10 +3053,10 @@
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Часть)</target>
</segment>
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Часть)</target>
</segment>
</unit>
<unit id="o4zUJc5" name="part.table.name.value.for_assembly">
<segment state="translated">
@ -8753,8 +8921,8 @@
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8807,7 +8975,7 @@
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="MDJdKXv" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -11031,13 +11199,13 @@
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">

View file

@ -1617,6 +1617,174 @@
<target>搜索部件</target>
</segment>
</unit>
<unit id="Upyz4NL" name="assembly.search.title">
<segment state="translated">
<source>assembly.search.title</source>
<target>组合体搜索</target>
</segment>
</unit>
<unit id="deO4FsM" name="project.search.title">
<segment state="translated">
<source>project.search.title</source>
<target>项目搜索</target>
</segment>
</unit>
<unit id="Xq01S2F" name="project.table.id">
<segment state="translated">
<source>project.table.id</source>
<target>ID</target>
</segment>
</unit>
<unit id="wNWc9GW" name="project.table.name">
<segment state="translated">
<source>project.table.name</source>
<target>名称</target>
</segment>
</unit>
<unit id="9AdhysG" name="project.table.description">
<segment state="translated">
<source>project.table.description</source>
<target>描述</target>
</segment>
</unit>
<unit id="4qJkRic" name="project.table.comment">
<segment state="translated">
<source>project.table.comment</source>
<target>备注</target>
</segment>
</unit>
<unit id="NrH0WW8" name="project.table.addedDate">
<segment state="translated">
<source>project.table.addedDate</source>
<target>添加日期</target>
</segment>
</unit>
<unit id="6rN1Yf1" name="project.table.lastModified">
<segment state="translated">
<source>project.table.lastModified</source>
<target>最后修改</target>
</segment>
</unit>
<unit id="F2nNNFD" name="project.table.edit">
<segment state="translated">
<source>project.table.edit</source>
<target>编辑</target>
</segment>
</unit>
<unit id="KZCGmOp" name="project.table.edit.title">
<segment state="translated">
<source>project.table.edit.title</source>
<target>编辑项目</target>
</segment>
</unit>
<unit id="cmck5zP" name="datasource.parts">
<segment state="translated">
<source>datasource.parts</source>
<target>零件</target>
</segment>
</unit>
<unit id="3tkaYAP" name="datasource.projects">
<segment state="translated">
<source>datasource.projects</source>
<target>项目</target>
</segment>
</unit>
<unit id="aYPamdy" name="datasource.assemblies">
<segment state="translated">
<source>datasource.assemblies</source>
<target>组合体</target>
</segment>
</unit>
<unit id="searching_for_assemblies" name="parts_list.search.searching_for_assemblies">
<segment state="translated">
<source>parts_list.search.searching_for_assemblies</source>
<target>正在搜索组合体...</target>
</segment>
</unit>
<unit id="searching_for_projects" name="parts_list.search.searching_for_projects">
<segment state="translated">
<source>parts_list.search.searching_for_projects</source>
<target>正在搜索项目...</target>
</segment>
</unit>
<unit id="ObC5PW9" name="settings.behavior.table.projects_default_columns">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns</source>
<target>项目表格默认列</target>
</segment>
</unit>
<unit id="dHwCrSv" name="settings.behavior.table.projects_default_columns.help">
<segment state="translated">
<source>settings.behavior.table.projects_default_columns.help</source>
<target>选择要在项目表格中默认显示的列。</target>
</segment>
</unit>
<unit id="asdfghj" name="assembly.filter.status">
<segment state="translated">
<source>assembly.filter.status</source>
<target>组合体状态</target>
</segment>
</unit>
<unit id="qwertyu" name="assembly.filter.parent">
<segment state="translated">
<source>assembly.filter.parent</source>
<target>父级组合体</target>
</segment>
</unit>
<unit id="acEe1Tb_notes" name="assembly.table.comment">
<segment state="translated">
<source>assembly.table.comment</source>
<target>备注</target>
</segment>
</unit>
<unit id="Ag9x60T" name="project.filter.dbId">
<segment state="translated">
<source>project.filter.dbId</source>
<target>ID</target>
</segment>
</unit>
<unit id="MjSfszH" name="project.filter.name">
<segment state="translated">
<source>project.filter.name</source>
<target>名称</target>
</segment>
</unit>
<unit id="ELxZW9y" name="project.filter.description">
<segment state="translated">
<source>project.filter.description</source>
<target>描述</target>
</segment>
</unit>
<unit id="svzDJ9V" name="project.filter.comment">
<segment state="translated">
<source>project.filter.comment</source>
<target>备注</target>
</segment>
</unit>
<unit id="sCPeQFs" name="project.filter.status">
<segment state="translated">
<source>project.filter.status</source>
<target>项目状态</target>
</segment>
</unit>
<unit id="1Fcochg" name="project.filter.parent">
<segment state="translated">
<source>project.filter.parent</source>
<target>父级项目</target>
</segment>
</unit>
<unit id="s3yI9D6" name="project.filter.attachments_count">
<segment state="translated">
<source>project.filter.attachments_count</source>
<target>附件数量</target>
</segment>
</unit>
<unit id="75p958O" name="project.filter.attachmentName">
<segment state="translated">
<source>project.filter.attachmentName</source>
<target>附件名称</target>
</segment>
</unit>
<unit id="gwE_D3w" name="parts_list.storelocation.title">
<segment state="translated">
<source>parts_list.storelocation.title</source>
@ -2883,10 +3051,10 @@
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部件)</target>
</segment>
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部件)</target>
</segment>
</unit>
<unit id="o4zUJc5" name="part.table.name.value.for_assembly">
<segment state="translated">
@ -8752,8 +8920,8 @@ Element 3</target>
</segment>
</unit>
<unit id="aki9R3e" name="project.bom_import.template.kicad_pcbnew.table">
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>project.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -8806,7 +8974,7 @@ Element 3</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="ijGhrbQ" name="project.bom_import.clear_existing_bom.help">
<segment state="translated">
@ -10543,18 +10711,18 @@ Element 3</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>注意:</strong> 未对类别管理中的特定组件进行映射。</p>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="a1bK2tc" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
@ -10607,7 +10775,7 @@ Element 3</target>
</table>
]]>
</target>
</segment>
</segment>
</unit>
<unit id="uT5zcT1" name="assembly_list.all.title">
<segment state="translated">
@ -10916,13 +11084,13 @@ Element 3</target>
</segment>
</unit>
<unit id="MxKRRx_" name="datatable.datatable.lengthMenu">
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
<notes>
<note priority="1">Do not remove! Used for datatables rendering.</note>
</notes>
<segment state="translated">
<source>datatable.datatable.lengthMenu</source>
<target>_MENU_</target>
</segment>
</unit>
<unit id="v8r7X2a" name="statistics.invalid_assembly_preview_attachments_count">
<segment state="translated">