diff --git a/assets/controllers/elements/tagsinput_controller.js b/assets/controllers/elements/tagsinput_controller.js
index 4454089f..88f1626a 100644
--- a/assets/controllers/elements/tagsinput_controller.js
+++ b/assets/controllers/elements/tagsinput_controller.js
@@ -8,7 +8,6 @@ export default class extends Controller {
_tomSelect;
connect() {
-
let settings = {
plugins: {
remove_button:{
diff --git a/assets/controllers/pages/parameters_autocomplete_controller.js b/assets/controllers/pages/parameters_autocomplete_controller.js
new file mode 100644
index 00000000..2d75bc57
--- /dev/null
+++ b/assets/controllers/pages/parameters_autocomplete_controller.js
@@ -0,0 +1,95 @@
+import {Controller} from "@hotwired/stimulus";
+import TomSelect from "tom-select";
+import katex from "katex";
+import "katex/dist/katex.css";
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller
+{
+ static values = {
+ url: String,
+ }
+
+ static targets = ["name", "symbol", "unit"]
+
+ onItemAdd(value, item) {
+ //Retrieve the unit and symbol from the item
+ const symbol = item.dataset.symbol;
+ const unit = item.dataset.unit;
+
+ if (this.symbolTarget && symbol !== undefined) {
+ this.symbolTarget.value = symbol;
+ }
+ if (this.unitTarget && unit !== undefined) {
+ this.unitTarget.value = unit;
+ }
+ }
+
+ connect() {
+ const settings = {
+ plugins: {
+ clear_button:{}
+ },
+ persistent: false,
+ maxItems: 1,
+ //This a an ugly solution to disable the delimiter parsing of the TomSelect plugin
+ delimiter: 'VERY_L0NG_D€LIMITER_WHICH_WILL_NEVER_BE_ENCOUNTERED_IN_A_STRING',
+ createOnBlur: true,
+ create: true,
+ searchField: "name",
+ //labelField: "name",
+ valueField: "name",
+ onItemAdd: this.onItemAdd.bind(this),
+ render: {
+ option: (data, escape) => {
+ let tmp = '
'
+ + '' + escape(data.name) + '
';
+
+ if (data.symbol) {
+ tmp += '' + katex.renderToString(data.symbol) + ''
+ }
+ if (data.unit) {
+ tmp += '' + katex.renderToString('[' + data.unit + ']') + ''
+ }
+
+
+ //+ '' + escape(data.unit) + ''
+ tmp += '
';
+
+ return tmp;
+ },
+ item: (data, escape) => {
+ //We use the item to transfert data to the onItemAdd function using data attributes
+ const element = document.createElement('div');
+ element.innerText = data.name;
+ if(data.unit !== undefined) {
+ element.dataset.unit = data.unit;
+ }
+ if (data.symbol !== undefined) {
+ element.dataset.symbol = data.symbol;
+ }
+
+ return element.outerHTML;
+ }
+ }
+ };
+
+ if(this.urlValue) {
+ const base_url = this.urlValue;
+ settings.load = (query, callback) => {
+ const url = base_url.replace('__QUERY__', encodeURIComponent(query));
+
+ fetch(url)
+ .then(response => response.json())
+ .then(json => {
+ //const data = json.map(x => {return {"value": x, "text": x}});
+ callback(json);
+ }).catch(()=>{
+ callback();
+ });
+ }
+ }
+
+ this._tomSelect = new TomSelect(this.nameTarget, settings);
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/TypeaheadController.php b/src/Controller/TypeaheadController.php
index f69f137e..77edae80 100644
--- a/src/Controller/TypeaheadController.php
+++ b/src/Controller/TypeaheadController.php
@@ -42,9 +42,22 @@ declare(strict_types=1);
namespace App\Controller;
+use App\Entity\Parameters\AttachmentTypeParameter;
+use App\Entity\Parameters\CategoryParameter;
+use App\Entity\Parameters\DeviceParameter;
+use App\Entity\Parameters\FootprintParameter;
+use App\Entity\Parameters\GroupParameter;
+use App\Entity\Parameters\ManufacturerParameter;
+use App\Entity\Parameters\MeasurementUnitParameter;
+use App\Entity\Parameters\PartParameter;
+use App\Entity\Parameters\StorelocationParameter;
+use App\Entity\Parameters\SupplierParameter;
+use App\Entity\PriceInformations\Currency;
+use App\Repository\ParameterRepository;
use App\Services\Attachments\AttachmentURLGenerator;
use App\Services\Attachments\BuiltinAttachmentsFinder;
use App\Services\TagFinder;
+use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Asset\Packages;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -99,6 +112,58 @@ class TypeaheadController extends AbstractController
return new JsonResponse($data, 200, [], true);
}
+ /**
+ * This functions map the parameter type to the class, so we can access its repository
+ * @param string $type
+ * @return class-string
+ */
+ private function typeToParameterClass(string $type): string
+ {
+ switch ($type) {
+ case 'category':
+ return CategoryParameter::class;
+ case 'part':
+ return PartParameter::class;
+ case 'device':
+ return DeviceParameter::class;
+ case 'footprint':
+ return FootprintParameter::class;
+ case 'manufacturer':
+ return ManufacturerParameter::class;
+ case 'storelocation':
+ return StorelocationParameter::class;
+ case 'supplier':
+ return SupplierParameter::class;
+ case 'attachment_type':
+ return AttachmentTypeParameter::class;
+ case 'group':
+ return GroupParameter::class;
+ case 'measurement_unit':
+ return MeasurementUnitParameter::class;
+ case 'currency':
+ return Currency::class;
+
+ default:
+ throw new \InvalidArgumentException('Invalid parameter type: '.$type);
+ }
+ }
+
+ /**
+ * @Route("/parameters/{type}/search/{query}", name="typeahead_parameters", requirements={"type" = ".+"})
+ * @param string $query
+ * @return JsonResponse
+ */
+ public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse
+ {
+ $class = $this->typeToParameterClass($type);
+ /** @var ParameterRepository $repository */
+ $repository = $entityManager->getRepository($class);
+
+ $data = $repository->autocompleteParamName($query);
+
+ return new JsonResponse($data);
+ }
+
/**
* @Route("/tags/search/{query}", name="typeahead_tags", requirements={"query"= ".+"})
*/
diff --git a/src/Entity/Parameters/AbstractParameter.php b/src/Entity/Parameters/AbstractParameter.php
index ba13f623..b7427985 100644
--- a/src/Entity/Parameters/AbstractParameter.php
+++ b/src/Entity/Parameters/AbstractParameter.php
@@ -33,7 +33,7 @@ use Symfony\Component\Validator\Constraints as Assert;
use function sprintf;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @ORM\Table("parameters")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="type", type="smallint")
diff --git a/src/Entity/Parameters/AttachmentTypeParameter.php b/src/Entity/Parameters/AttachmentTypeParameter.php
index d1ad9094..1a7e4f37 100644
--- a/src/Entity/Parameters/AttachmentTypeParameter.php
+++ b/src/Entity/Parameters/AttachmentTypeParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class AttachmentTypeParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/CategoryParameter.php b/src/Entity/Parameters/CategoryParameter.php
index 86925755..1165f985 100644
--- a/src/Entity/Parameters/CategoryParameter.php
+++ b/src/Entity/Parameters/CategoryParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class CategoryParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/CurrencyParameter.php b/src/Entity/Parameters/CurrencyParameter.php
index 28f3c934..8651ec13 100644
--- a/src/Entity/Parameters/CurrencyParameter.php
+++ b/src/Entity/Parameters/CurrencyParameter.php
@@ -30,7 +30,7 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* A attachment attached to a category element.
*
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class CurrencyParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/DeviceParameter.php b/src/Entity/Parameters/DeviceParameter.php
index 724ad3be..1534c84a 100644
--- a/src/Entity/Parameters/DeviceParameter.php
+++ b/src/Entity/Parameters/DeviceParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class DeviceParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/FootprintParameter.php b/src/Entity/Parameters/FootprintParameter.php
index d92ed8cb..ef354ef1 100644
--- a/src/Entity/Parameters/FootprintParameter.php
+++ b/src/Entity/Parameters/FootprintParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class FootprintParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/GroupParameter.php b/src/Entity/Parameters/GroupParameter.php
index c6b62aa1..361a60cd 100644
--- a/src/Entity/Parameters/GroupParameter.php
+++ b/src/Entity/Parameters/GroupParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class GroupParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/ManufacturerParameter.php b/src/Entity/Parameters/ManufacturerParameter.php
index b06633f0..fbdf8fe6 100644
--- a/src/Entity/Parameters/ManufacturerParameter.php
+++ b/src/Entity/Parameters/ManufacturerParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class ManufacturerParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/MeasurementUnitParameter.php b/src/Entity/Parameters/MeasurementUnitParameter.php
index 91ce5809..52af7a13 100644
--- a/src/Entity/Parameters/MeasurementUnitParameter.php
+++ b/src/Entity/Parameters/MeasurementUnitParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class MeasurementUnitParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/PartParameter.php b/src/Entity/Parameters/PartParameter.php
index d7d08cfa..7f30cd02 100644
--- a/src/Entity/Parameters/PartParameter.php
+++ b/src/Entity/Parameters/PartParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class PartParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/StorelocationParameter.php b/src/Entity/Parameters/StorelocationParameter.php
index 44077d48..76c209a6 100644
--- a/src/Entity/Parameters/StorelocationParameter.php
+++ b/src/Entity/Parameters/StorelocationParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class StorelocationParameter extends AbstractParameter
diff --git a/src/Entity/Parameters/SupplierParameter.php b/src/Entity/Parameters/SupplierParameter.php
index e61137a0..3bc42a56 100644
--- a/src/Entity/Parameters/SupplierParameter.php
+++ b/src/Entity/Parameters/SupplierParameter.php
@@ -28,7 +28,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
- * @ORM\Entity()
+ * @ORM\Entity(repositoryClass="App\Repository\ParameterRepository")
* @UniqueEntity(fields={"name", "group", "element"})
*/
class SupplierParameter extends AbstractParameter
diff --git a/src/Repository/ParameterRepository.php b/src/Repository/ParameterRepository.php
new file mode 100644
index 00000000..36ee2ff8
--- /dev/null
+++ b/src/Repository/ParameterRepository.php
@@ -0,0 +1,33 @@
+createQueryBuilder('parameter');
+
+ $qb->distinct()
+ ->select('parameter.name')
+ ->addSelect('parameter.symbol')
+ ->addSelect('parameter.unit')
+ ->where('parameter.name LIKE :name');
+ if ($exact) {
+ $qb->setParameter('name', $name);
+ } else {
+ $qb->setParameter('name', '%'.$name.'%');
+ }
+
+ $qb->setMaxResults($max_results);
+
+ return $qb->getQuery()->getArrayResult();
+ }
+}
\ No newline at end of file
diff --git a/src/Services/TagFinder.php b/src/Services/TagFinder.php
deleted file mode 100644
index 4038bc34..00000000
--- a/src/Services/TagFinder.php
+++ /dev/null
@@ -1,117 +0,0 @@
-.
- */
-
-declare(strict_types=1);
-
-/**
- * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
- *
- * Copyright (C) 2019 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 General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-namespace App\Services;
-
-use App\Entity\Parts\Part;
-use Doctrine\ORM\EntityManagerInterface;
-use Symfony\Component\OptionsResolver\OptionsResolver;
-
-use function array_slice;
-
-/**
- * A service related for searching for tags. Mostly useful for autocomplete reasons.
- */
-class TagFinder
-{
- protected $em;
-
- public function __construct(EntityManagerInterface $entityManager)
- {
- $this->em = $entityManager;
- }
-
- /**
- * Search tags that begins with the certain keyword.
- *
- * @param string $keyword The keyword the tag must begin with
- * @param array $options Some options specifying the search behavior. See configureOptions for possible options.
- *
- * @return string[] an array containing the tags that match the given keyword
- */
- public function searchTags(string $keyword, array $options = []): array
- {
- $results = [];
- $keyword_regex = '/^'.preg_quote($keyword, '/').'/';
-
- $resolver = new OptionsResolver();
- $this->configureOptions($resolver);
-
- $options = $resolver->resolve($options);
-
- //If the keyword is too short we will get to much results, which takes too much time...
- if (mb_strlen($keyword) < $options['min_keyword_length']) {
- return [];
- }
-
- //Build a query to get all
- $qb = $this->em->createQueryBuilder();
-
- $qb->select('p.tags')
- ->from(Part::class, 'p')
- ->where('p.tags LIKE ?1')
- ->setMaxResults($options['query_limit'])
- //->orderBy('RAND()')
- ->setParameter(1, '%'.$keyword.'%');
-
- $possible_tags = $qb->getQuery()->getArrayResult();
-
- //Iterate over each possible tags (which are comma separated) and extract tags which match our keyword
- foreach ($possible_tags as $tags) {
- $tags = explode(',', $tags['tags']);
- $results = array_merge($results, preg_grep($keyword_regex, $tags));
- }
-
- $results = array_unique($results);
- //Limit the returned tag count to specified value.
- return array_slice($results, 0, $options['return_limit']);
- }
-
- protected function configureOptions(OptionsResolver $resolver): void
- {
- $resolver->setDefaults([
- 'query_limit' => 75,
- 'return_limit' => 75,
- 'min_keyword_length' => 2,
- ]);
- }
-}
diff --git a/templates/Parts/edit/edit_form_styles.html.twig b/templates/Parts/edit/edit_form_styles.html.twig
index 8fef44b4..003be938 100644
--- a/templates/Parts/edit/edit_form_styles.html.twig
+++ b/templates/Parts/edit/edit_form_styles.html.twig
@@ -68,13 +68,13 @@
{% block parameter_widget %}
{% import 'components/collection_type.macro.html.twig' as collection %}
-
- {{ form_widget(form.name) }}{{ form_errors(form.name) }} |
- {{ form_widget(form.symbol) }}{{ form_errors(form.symbol) }} |
+
+ {{ form_widget(form.name, {"attr": {"data-pages--parameters-autocomplete-target": "name"}}) }}{{ form_errors(form.name) }} |
+ {{ form_widget(form.symbol, {"attr": {"data-pages--parameters-autocomplete-target": "symbol"}}) }}{{ form_errors(form.symbol) }} |
{{ form_widget(form.value_min) }}{{ form_errors(form.value_min) }} |
{{ form_widget(form.value_typical) }}{{ form_errors(form.value_typical) }} |
{{ form_widget(form.value_max) }}{{ form_errors(form.value_max) }} |
- {{ form_widget(form.unit) }}{{ form_errors(form.unit) }} |
+ {{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit"}}) }}{{ form_errors(form.unit) }} |
{{ form_widget(form.value_text) }}{{ form_errors(form.value_text) }} |
{{ form_widget(form.group) }}{{ form_errors(form.group) }} |
|