This commit is contained in:
Thomas Peterson 2025-07-30 18:48:04 +02:00
parent 918e4f9455
commit 5eb85fe090
34 changed files with 731 additions and 556 deletions

View File

@ -47,7 +47,7 @@
"mobiledetect/mobiledetectlib": "^2.8", "mobiledetect/mobiledetectlib": "^2.8",
"mpdf/mpdf": "dev-qrcode", "mpdf/mpdf": "dev-qrcode",
"mpdf/qrcode": "^1.2", "mpdf/qrcode": "^1.2",
"nelmio/api-doc-bundle": "^5", "nelmio/api-doc-bundle": "^4",
"nelmio/cors-bundle": "^2.2", "nelmio/cors-bundle": "^2.2",
"nicolab/php-ftp-client": "^1.4", "nicolab/php-ftp-client": "^1.4",
"oneup/uploader-bundle": "^3", "oneup/uploader-bundle": "^3",

91
src/new/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "9b481d5b6c4b610b15183e3afe963bd1", "content-hash": "f0609413226c0c4e60f20fbe8a606b6b",
"packages": [ "packages": [
{ {
"name": "azuyalabs/yasumi", "name": "azuyalabs/yasumi",
@ -5694,74 +5694,74 @@
}, },
{ {
"name": "nelmio/api-doc-bundle", "name": "nelmio/api-doc-bundle",
"version": "v5.4.0", "version": "v4.38.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nelmio/NelmioApiDocBundle.git", "url": "https://github.com/nelmio/NelmioApiDocBundle.git",
"reference": "49098f9e3f5e7a466b95b1d108d45857ba533c96" "reference": "fdc1cf5bc57287787db59f205a8e77485bd22072"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/49098f9e3f5e7a466b95b1d108d45857ba533c96", "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/fdc1cf5bc57287787db59f205a8e77485bd22072",
"reference": "49098f9e3f5e7a466b95b1d108d45857ba533c96", "reference": "fdc1cf5bc57287787db59f205a8e77485bd22072",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "ext-json": "*",
"phpdocumentor/reflection-docblock": "^5.0", "php": ">=7.4",
"phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0",
"phpdocumentor/type-resolver": "^1.8.2", "phpdocumentor/type-resolver": "^1.8.2",
"psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/container": "^1.0 || ^2.0", "psr/container": "^1.0 || ^2.0",
"psr/log": "^1.0 || ^2.0 || ^3.0", "psr/log": "^1.0 || ^2.0 || ^3.0",
"symfony/config": "^6.4 || ^7.1", "symfony/config": "^5.4 || ^6.4 || ^7.1",
"symfony/console": "^6.4 || ^7.1", "symfony/console": "^5.4 || ^6.4 || ^7.1",
"symfony/dependency-injection": "^6.4 || ^7.1", "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.1",
"symfony/deprecation-contracts": "^2.1 || ^3", "symfony/deprecation-contracts": "^2.1 || ^3",
"symfony/framework-bundle": "^6.4 || ^7.1", "symfony/framework-bundle": "^5.4.24 || ^6.4 || ^7.1",
"symfony/http-foundation": "^6.4 || ^7.1", "symfony/http-foundation": "^5.4 || ^6.4 || ^7.1",
"symfony/http-kernel": "^6.4 || ^7.1", "symfony/http-kernel": "^5.4 || ^6.4 || ^7.1",
"symfony/options-resolver": "^6.4 || ^7.1", "symfony/options-resolver": "^5.4 || ^6.4 || ^7.1",
"symfony/property-info": "^6.4 || ^7.1", "symfony/property-info": "^5.4.10 || ^6.4 || ^7.1",
"symfony/routing": "^6.4 || ^7.1", "symfony/routing": "^5.4 || ^6.4 || ^7.1",
"zircote/swagger-php": "^4.11.1 || ^5.0" "zircote/swagger-php": "^4.11.1 || ^5.0"
}, },
"conflict": { "conflict": {
"zircote/swagger-php": "4.8.7" "zircote/swagger-php": "4.8.7"
}, },
"require-dev": { "require-dev": {
"api-platform/core": "^3.2", "api-platform/core": "^2.7.0 || ^3",
"composer/package-versions-deprecated": "1.11.99.1",
"doctrine/annotations": "^2.0",
"friendsofphp/php-cs-fixer": "^3.52", "friendsofphp/php-cs-fixer": "^3.52",
"friendsofsymfony/rest-bundle": "^3.2.0", "friendsofsymfony/rest-bundle": "^2.8 || ^3.0",
"jms/serializer": "^3.32", "jms/serializer": "^1.14 || ^3.0",
"jms/serializer-bundle": "^5.5", "jms/serializer-bundle": "^2.3 || ^3.0 || ^4.0 || ^5.0",
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phpstan/phpstan-phpunit": "^1.3", "phpstan/phpstan-phpunit": "^1.3",
"phpstan/phpstan-strict-rules": "^1.5", "phpstan/phpstan-strict-rules": "^1.5",
"phpstan/phpstan-symfony": "^1.3", "phpstan/phpstan-symfony": "^1.3",
"phpunit/phpunit": "^10.5", "phpunit/phpunit": "^9.6 || ^10.5",
"symfony/asset": "^6.4 || ^7.1", "symfony/asset": "^5.4 || ^6.4 || ^7.1",
"symfony/browser-kit": "^6.4 || ^7.1", "symfony/browser-kit": "^5.4 || ^6.4 || ^7.1",
"symfony/cache": "^6.4 || ^7.1", "symfony/cache": "^5.4 || ^6.4 || ^7.1",
"symfony/dom-crawler": "^6.4 || ^7.1", "symfony/dom-crawler": "^5.4 || ^6.4 || ^7.1",
"symfony/expression-language": "^6.4 || ^7.1", "symfony/expression-language": "^5.4 || ^6.4 || ^7.1",
"symfony/finder": "^6.4 || ^7.1", "symfony/form": "^5.4 || ^6.4 || ^7.1",
"symfony/form": "^6.4 || ^7.1", "symfony/phpunit-bridge": "^6.4",
"symfony/phpunit-bridge": "^6.4 || ^7.1", "symfony/property-access": "^5.4 || ^6.4 || ^7.1",
"symfony/property-access": "^6.4 || ^7.1", "symfony/security-csrf": "^5.4 || ^6.4 || ^7.1",
"symfony/security-csrf": "^6.4 || ^7.1", "symfony/serializer": "^5.4 || ^6.4 || ^7.1",
"symfony/security-http": "^6.4 || ^7.1", "symfony/stopwatch": "^5.4 || ^6.4 || ^7.1",
"symfony/serializer": "^6.4 || ^7.1", "symfony/templating": "^5.4 || ^6.4 || ^7.1",
"symfony/stopwatch": "^6.4 || ^7.1", "symfony/twig-bundle": "^5.4 || ^6.4 || ^7.1",
"symfony/templating": "^6.4 || ^7.1", "symfony/uid": "^5.4 || ^6.4 || ^7.1",
"symfony/translation": "^6.4 || ^7.1", "symfony/validator": "^5.4 || ^6.4 || ^7.1",
"symfony/twig-bundle": "^6.4 || ^7.1", "willdurand/hateoas-bundle": "^1.0 || ^2.0"
"symfony/uid": "^6.4 || ^7.1",
"symfony/validator": "^6.4 || ^7.1",
"willdurand/hateoas-bundle": "^2.7",
"willdurand/negotiation": "^3.0"
}, },
"suggest": { "suggest": {
"api-platform/core": "For using an API oriented framework.", "api-platform/core": "For using an API oriented framework.",
"doctrine/annotations": "For using doctrine annotations",
"friendsofsymfony/rest-bundle": "For using the parameters annotations.", "friendsofsymfony/rest-bundle": "For using the parameters annotations.",
"jms/serializer-bundle": "For describing your models.", "jms/serializer-bundle": "For describing your models.",
"symfony/asset": "For using the Swagger UI.", "symfony/asset": "For using the Swagger UI.",
@ -5777,8 +5777,7 @@
"type": "symfony-bundle", "type": "symfony-bundle",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-4.x": "4.x-dev", "dev-4.x": "4.x-dev"
"dev-5.x": "5.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -5796,7 +5795,7 @@
"homepage": "https://github.com/nelmio/NelmioApiDocBundle/contributors" "homepage": "https://github.com/nelmio/NelmioApiDocBundle/contributors"
} }
], ],
"description": "Generates documentation for your REST API from attributes", "description": "Generates documentation for your REST API from annotations and attributes",
"keywords": [ "keywords": [
"api", "api",
"doc", "doc",
@ -5805,7 +5804,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/nelmio/NelmioApiDocBundle/issues", "issues": "https://github.com/nelmio/NelmioApiDocBundle/issues",
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.4.0" "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.38.2"
}, },
"funding": [ "funding": [
{ {
@ -5813,7 +5812,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-06-26T15:03:21+00:00" "time": "2025-03-24T15:00:53+00:00"
}, },
{ {
"name": "nelmio/cors-bundle", "name": "nelmio/cors-bundle",

View File

@ -3,54 +3,49 @@
namespace PSC\Shop\AccountBundle\Api; namespace PSC\Shop\AccountBundle\Api;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes\JsonContent;
use OpenApi\Annotations as OA; use OpenApi\Attributes\Response as OpenApiResponse;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Shop\AccountBundle\Dto\All\Output;
use PSC\Shop\AccountBundle\Model\Account as ModelAccount; use PSC\Shop\AccountBundle\Model\Account as ModelAccount;
use PSC\Shop\AccountBundle\Transformer\Account as TransformerAccount;
use PSC\Shop\EntityBundle\Entity\Account;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use PSC\Shop\AccountBundle\Dto\All\Output;
use PSC\Shop\AccountBundle\Transformer\Account as TransformerAccount;
use PSC\Shop\EntityBundle\Entity\Account;
class AllByShop extends AbstractController class AllByShop extends AbstractController
{ {
/** #[OpenApiResponse(
* get accounts by shop response: 200,
* description: 'get all accounts by shop',
* @OA\Response( content: new JsonContent(ref: new Model(type: Output::class)),
* response=200, )]
* description="account",
* @OA\JsonContent(ref=@Model(type=\PSC\Shop\AccountBundle\Dto\All\Output::class))
* )
* @OA\Tag(name="Account")
* @Security(name="Bearer")
*/
#[Route(path: 'allbyshop/{shop_uuid}', methods: ['GET'])] #[Route(path: 'allbyshop/{shop_uuid}', methods: ['GET'])]
#[IsGranted('ROLE_SHOP')] #[IsGranted('ROLE_SHOP')]
#[Tag('Account')]
#[Security('Bearer')]
public function AllAction( public function AllAction(
EntityManagerInterface $entityManager, EntityManagerInterface $entityManager,
TransformerAccount $transformerAccount, TransformerAccount $transformerAccount,
string $shop_uuid = "" string $shop_uuid = '',
): JsonResponse { ): JsonResponse {
$output = new Output(); $output = new Output();
$accountRepository = $entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Account'); $accountRepository = $entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Account');
$qb = $accountRepository->createQueryBuilder('account') $qb = $accountRepository->createQueryBuilder('account')->leftJoin('account.shops', 'shops')->orderBy(
->leftJoin('account.shops', 'shops') 'account.uid',
->orderBy('account.uid', 'desc'); 'desc',
);
$qb->andWhere('(shops.uid = :shop_id or shops.uuid = :shop_id)') $qb->andWhere('(shops.uid = :shop_id or shops.uuid = :shop_id)')->setParameter('shop_id', $shop_uuid);
->setParameter("shop_id", $shop_uuid);
$accounts = $qb->getQuery()->execute(); $accounts = $qb->getQuery()->execute();

View File

@ -2,9 +2,14 @@
namespace PSC\Shop\MediaBundle\Api\Folder; namespace PSC\Shop\MediaBundle\Api\Folder;
use Doctrine\ODM\MongoDB\DocumentManager;
use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Attribute\Security;
use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\RequestBody; use OpenApi\Attributes\RequestBody;
use OpenApi\Attributes\Response; use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag; use OpenApi\Attributes\Tag;
use PSC\Shop\MediaBundle\Document\Folder as PSCFolder;
use PSC\Shop\MediaBundle\Dto\Folder\Input; use PSC\Shop\MediaBundle\Dto\Folder\Input;
use PSC\Shop\MediaBundle\Model\Folder; use PSC\Shop\MediaBundle\Model\Folder;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
@ -14,14 +19,29 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
class Add extends AbstractController class Add extends AbstractController
{ {
public function __construct(
private readonly DocumentManager $dm,
) {}
#[Response(response: 200, description: 'add Folder', ref: Folder::class)] #[Response(response: 200, description: 'add Folder', ref: Folder::class)]
#[Route(path: '/folder/add', methods: ['POST'])] #[Route(path: '/folder/add', methods: ['POST'])]
#[Tag('Media')] #[Tag('Media')]
#[RequestBody(content: new Model(type: Input::class))] #[RequestBody(content: new Model(type: Input::class))]
#[ParamConverter('data', class: Input::class, converter: 'psc_rest.request_body')] #[ParamConverter('data', class: Input::class, converter: 'psc_rest.request_body')]
#[IsGranted('SHOP_ADMIN')] #[IsGranted('ROLE_ADMIN')]
#[Security(name: 'Security')]
public function add(Input $data) public function add(Input $data)
{ {
return $this->json($data); $cat = new PSCFolder();
$cat->setTitle($data->title);
$cat->setIcon('fa-file');
$this->dm->persist($cat);
$this->dm->flush();
$fModel = new Folder();
$fModel->setUuid($cat->getId());
$fModel->setTitle($cat->getTitle());
return $this->json($fModel);
} }
} }

View File

@ -0,0 +1,55 @@
<?php
namespace PSC\Shop\MediaBundle\Api\Folder;
use Doctrine\ODM\MongoDB\DocumentManager;
use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Attribute\Security;
use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Shop\MediaBundle\Document\Folder;
use PSC\Shop\MediaBundle\Dto\Folder\All as PSCAll;
use PSC\Shop\MediaBundle\Model\Folder as PSCFolder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class All extends AbstractController
{
public function __construct(
private readonly DocumentManager $dm,
) {}
#[Response(
response: 200,
description: 'get all Folder',
content: new JsonContent(ref: new Model(type: PSCAll::class)),
)]
#[Route(path: '/folder/all', methods: ['GET'])]
#[Tag('Media')]
#[IsGranted('ROLE_ADMIN')]
#[Security(name: 'Bearer')]
public function all()
{
$folders = $this->dm
->getRepository(Folder::class)
->createQueryBuilder('folder')
->field('parent_id')
->exists(false)
->sort('title', 'ASC')
->getQuery()
->execute();
$output = new PSCAll();
foreach ($folders as $folder) {
$f = new PSCFolder();
$f->setTitle($folder->getTitle());
$f->setUuid($folder->getId());
$output->data[] = $f;
}
return $this->json($output);
}
}

View File

@ -3,19 +3,19 @@
namespace PSC\Shop\MediaBundle\Api; namespace PSC\Shop\MediaBundle\Api;
use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\DocumentManager;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Annotation\Security;
use OpenApi\Annotations as OA;
use PSC\Shop\MediaBundle\Document\Media; use PSC\Shop\MediaBundle\Document\Media;
use PSC\Shop\MediaBundle\Model\Media as MediaModel;
use PSC\Shop\MediaBundle\Helper\MediaManager; use PSC\Shop\MediaBundle\Helper\MediaManager;
use PSC\Shop\MediaBundle\Model\Media as MediaModel;
use PSC\System\SettingsBundle\Service\Shop; use PSC\System\SettingsBundle\Service\Shop;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use OpenApi\Annotations as OA;
use Nelmio\ApiDocBundle\Annotation\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Nelmio\ApiDocBundle\Annotation\Model;
class Upload extends AbstractController class Upload extends AbstractController
{ {
@ -53,27 +53,29 @@ class Upload extends AbstractController
* @Security(name="Bearer") * @Security(name="Bearer")
*/ */
#[Route(path: '/create', methods: ['POST'])] #[Route(path: '/create', methods: ['POST'])]
public function create(MediaManager $mediaManager, Shop $shopService, DocumentManager $documentManager, Request $req): JsonResponse public function create(
{ MediaManager $mediaManager,
Shop $shopService,
DocumentManager $documentManager,
Request $req,
): JsonResponse {
$selectedShop = $shopService->getShopByDomain(); $selectedShop = $shopService->getShopByDomain();
$selectedFolder = $documentManager $selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder') ->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy(array('id' => $req->get('folder'))); ->findOneBy(['id' => $req->get('folder')]);
$handler = $mediaManager->getHandlerForType('pdf'); $handler = $mediaManager->getHandlerForType('pdf');
$media = new Media(); $media = new Media();
$helper = $handler->getFormHelper($media); $helper = $handler->getFormHelper($media);
$media->setContent($req->files->get('file')); $media->setContent($req->files->get('file'));
$handler->prepareMedia($media); $handler->prepareMedia($media);
$media->setFolder($selectedFolder); $media->setFolder($selectedFolder);
$media->setShop($selectedShop->getUid()); $media->setShop($selectedShop->getUid());
$documentManager->persist($media); $documentManager->persist($media);
$documentManager->flush(); $documentManager->flush();
$mediaModel = new MediaModel(); $mediaModel = new MediaModel();
$mediaModel->setUuid($media->getId()); $mediaModel->setUuid($media->getId());
$mediaModel->setUrl($media->getUrl()); $mediaModel->setUrl($media->getUrl());
$mediaModel->setTitle($media->getTitle()); $mediaModel->setTitle($media->getTitle());
return $this->json($mediaModel); return $this->json($mediaModel);
} }
} }

View File

@ -0,0 +1,14 @@
<?php
namespace PSC\Shop\MediaBundle\Dto\Folder;
use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Attributes\Items;
use OpenApi\Attributes\Property;
use PSC\Shop\MediaBundle\Model\Folder;
final class All
{
#[Property(type: 'array', items: new Items(ref: new Model(type: Folder::class)))]
public array $data;
}

View File

@ -7,5 +7,5 @@ use OpenApi\Attributes\Property;
final class Input final class Input
{ {
#[Property(type: 'string')] #[Property(type: 'string')]
public string $name; public string $title;
} }

View File

@ -2,19 +2,19 @@
namespace PSC\Shop\MediaBundle\Model; namespace PSC\Shop\MediaBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
class Media class Media
{ {
#[OA\Property(type: 'string')] #[OA\Property(type: 'string')]
private string $title = ""; private string $title = '';
#[OA\Property(type: 'string')] #[OA\Property(type: 'string')]
private string $url = ""; private string $url = '';
#[OA\Property(type: 'string')] #[OA\Property(type: 'string')]
private string $uuid = ""; private string $uuid = '';
#[OA\Property(type: 'array', items: new OA\Items(ref: new Model(type: Variant::class)))] #[OA\Property(type: 'array', items: new OA\Items(ref: new Model(type: Variant::class)))]
private array $variants = []; private array $variants = [];
@ -67,6 +67,7 @@ class Media
$this->uuid = $uuid; $this->uuid = $uuid;
} }
#[\Override]
public function __toString(): string public function __toString(): string
{ {
return $this->uuid; return $this->uuid;
@ -75,9 +76,9 @@ class Media
public function getArray(): array public function getArray(): array
{ {
return [ return [
'uuid' => $this->uuid, 'uuid' => $this->uuid,
'url' => $this->url, 'url' => $this->url,
'title' => $this->title 'title' => $this->title,
]; ];
} }

View File

@ -3,22 +3,22 @@
namespace PSC\Shop\ProductBundle\Api\Product; namespace PSC\Shop\ProductBundle\Api\Product;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Annotation\Security;
use OpenApi\Annotations as OA;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Shop\EntityBundle\Entity\Paper; use PSC\Shop\EntityBundle\Entity\Paper;
use PSC\Shop\EntityBundle\Entity\Product; use PSC\Shop\EntityBundle\Entity\Product;
use PSC\Shop\EntityBundle\Entity\Productgroup; use PSC\Shop\EntityBundle\Entity\Productgroup;
use PSC\Shop\ProductBundle\Dto\Product\GetAllByProductGroup\Output; use PSC\Shop\ProductBundle\Dto\Product\GetAllByProductGroup\Output;
use PSC\Shop\ProductBundle\Hydrate\Product as PSCProduct; use PSC\Shop\ProductBundle\Hydrate\Product as PSCProduct;
use PSC\System\SettingsBundle\Model\Paper as PSCPaper;
use PSC\System\SettingsBundle\Model\Papercontainer; use PSC\System\SettingsBundle\Model\Papercontainer;
use PSC\System\SettingsBundle\Service\Shop; use PSC\System\SettingsBundle\Service\Shop;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use OpenApi\Annotations as OA;
use Nelmio\ApiDocBundle\Annotation\Model;
use PSC\System\SettingsBundle\Model\Paper as PSCPaper;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Nelmio\ApiDocBundle\Annotation\Security;
class GetAllByTerm extends AbstractController class GetAllByTerm extends AbstractController
{ {
@ -31,6 +31,7 @@ class GetAllByTerm extends AbstractController
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->productHydrate = $productHydrate; $this->productHydrate = $productHydrate;
} }
/** /**
* get product * get product
* *
@ -44,7 +45,7 @@ class GetAllByTerm extends AbstractController
#[Route(path: '/product/getallbyterm/{shop_uuid}/{term}', methods: ['GET'])] #[Route(path: '/product/getallbyterm/{shop_uuid}/{term}', methods: ['GET'])]
public function getAll(string $term, string $shop_uuid): JsonResponse public function getAll(string $term, string $shop_uuid): JsonResponse
{ {
$temp = []; $temp = [];
$products = $this->entityManager $products = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Product') ->getRepository('PSC\Shop\EntityBundle\Entity\Product')
@ -52,15 +53,34 @@ class GetAllByTerm extends AbstractController
->leftJoin('product.shop', 'shop') ->leftJoin('product.shop', 'shop')
->andWhere(" ->andWhere("
shop.uid = :shop_id and shop.uid = :shop_id and
(product.uid LIKE '%" . $term . "%' OR (product.uid LIKE '%" .
product.title LIKE '%" . $term . "%' OR $term .
product.calcXml LIKE '%" . $term . "%' OR "%' OR
product.textFormat LIKE '%" . $term . "%' OR product.title LIKE '%" .
product.description LIKE '%" . $term . "%' OR $term .
product.nrIntern LIKE '%" . $term . "%' OR "%' OR
product.nrExtern LIKE '%" . $term . "%' OR product.calcXml LIKE '%" .
product.textArt LIKE '%" . $term . "%') $term .
")->setParameter('shop_id', $shop_uuid)->getQuery()->execute(); "%' OR
product.textFormat LIKE '%" .
$term .
"%' OR
product.description LIKE '%" .
$term .
"%' OR
product.nrIntern LIKE '%" .
$term .
"%' OR
product.nrExtern LIKE '%" .
$term .
"%' OR
product.textArt LIKE '%" .
$term .
"%')
")
->setParameter('shop_id', $shop_uuid)
->getQuery()
->execute();
/** @var Product $product */ /** @var Product $product */
foreach ($products as $product) { foreach ($products as $product) {

View File

@ -2,6 +2,8 @@
namespace App\Tests\PSC\Shop\Media\Api; namespace App\Tests\PSC\Shop\Media\Api;
use Faker\Factory;
use Faker\Generator;
use PSC\Shop\ContactBundle\Repository\ContactRepository; use PSC\Shop\ContactBundle\Repository\ContactRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
@ -11,6 +13,13 @@ class FolderTest extends WebTestCase
{ {
use RefreshDatabaseTrait; use RefreshDatabaseTrait;
private Generator $faker;
public function setUp(): void
{
$this->faker = Factory::create(locale: 'de_DE');
}
public function testCreateFolder(): void public function testCreateFolder(): void
{ {
$client = static::createClient(); $client = static::createClient();
@ -21,16 +30,43 @@ class FolderTest extends WebTestCase
$client->loginUser($testUser, 'api'); $client->loginUser($testUser, 'api');
$client->request( $name = $this->faker->slug();
$client->jsonRequest(
'POST', 'POST',
'/api/media/folder/create', '/api/media/folder/add',
[ [
'name' => 'Test1', 'title' => $name,
], ],
[], [],
); );
self::assertResponseIsSuccessful($client->getResponse()); $this->assertResponseIsSuccessful();
$media = json_decode($client->getResponse()->getContent(), true); $data = json_decode($client->getResponse()->getContent(), true);
self::assertSame($name, $data['title']);
}
public function testCreateAndGetFolders(): void
{
$client = static::createClient();
$userRepository = static::getContainer()->get(ContactRepository::class);
$testUser = $userRepository->loadUserByUsername('admin@shop.de');
$client->loginUser($testUser, 'api');
for ($i = 0; $i < 5; $i++) {
$client->jsonRequest(
'POST',
'/api/media/folder/add',
[
'title' => $this->faker->slug(),
],
[],
);
$this->assertResponseIsSuccessful();
}
$client->jsonRequest('GET', '/api/media/folder/all', [], []);
self::assertCount(5, json_decode($client->getResponse()->getContent(), true)['data']);
} }
} }

View File

@ -22,16 +22,17 @@ class UploadTest extends WebTestCase
$client->loginUser($testUser, 'api'); $client->loginUser($testUser, 'api');
$uploadedFile = new UploadedFile( $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny.jpg', 'kenney.jpg');
__DIR__.'/../../../../kenny.jpg', $client->request(
'kenney.jpg' 'POST',
'/api/media/create',
[],
[
'file' => $uploadedFile,
],
); );
$client->request('POST', '/api/media/create', [], [
'file' => $uploadedFile
]);
$media = json_decode($client->getResponse()->getContent(), true); $media = json_decode($client->getResponse()->getContent(), true);
self::assertSame('kenney.jpg', $media['title']); self::assertSame('kenney.jpg', $media['title']);
} }
} }

View File

@ -14,6 +14,7 @@ use PSC\Shop\EntityBundle\Document\Instance;
use PSC\Shop\EntityBundle\Document\Order; use PSC\Shop\EntityBundle\Document\Order;
use PSC\Shop\EntityBundle\Document\Position; use PSC\Shop\EntityBundle\Document\Position;
use PSC\Shop\EntityBundle\Document\Shop; use PSC\Shop\EntityBundle\Document\Shop;
use PSC\Shop\MediaBundle\Document\Folder;
use PSC\Shop\MediaBundle\Document\Media; use PSC\Shop\MediaBundle\Document\Media;
use PSC\System\PluginBundle\Document\Plugin; use PSC\System\PluginBundle\Document\Plugin;
use PSC\System\SettingsBundle\Document\Help; use PSC\System\SettingsBundle\Document\Help;
@ -38,16 +39,17 @@ trait RefreshDatabaseTrait
$container = static::$kernel->getContainer(); $container = static::$kernel->getContainer();
/** /**
* @var EntityManagerInterface $doc * @var EntityManagerInterface $doc
*/ */
$em = $container->get('doctrine.orm.entity_manager'); $em = $container->get('doctrine.orm.entity_manager');
/** /**
* @var DocumentManager $doc * @var DocumentManager $doc
*/ */
$doc = $container->get('doctrine_mongodb.odm.document_manager'); $doc = $container->get('doctrine_mongodb.odm.document_manager');
$doc->getSchemaManager()->dropDocumentCollection(Media::class); $doc->getSchemaManager()->dropDocumentCollection(Media::class);
$doc->getSchemaManager()->dropDocumentCollection(Folder::class);
$doc->getSchemaManager()->dropDocumentCollection(Order::class); $doc->getSchemaManager()->dropDocumentCollection(Order::class);
$doc->getSchemaManager()->dropDocumentCollection(Position::class); $doc->getSchemaManager()->dropDocumentCollection(Position::class);
$doc->getSchemaManager()->dropDocumentCollection(Instance::class); $doc->getSchemaManager()->dropDocumentCollection(Instance::class);
@ -69,7 +71,7 @@ trait RefreshDatabaseTrait
} }
$instance = new Instance(); $instance = new Instance();
$instance->setAppId("1"); $instance->setAppId('1');
$instance->setInvoiceNumberStart(1); $instance->setInvoiceNumberStart(1);
$instance->setOfferNumberStart(1); $instance->setOfferNumberStart(1);
$instance->setCancelationNumberStart(1); $instance->setCancelationNumberStart(1);
@ -87,7 +89,9 @@ trait RefreshDatabaseTrait
$doc->persist($instance); $doc->persist($instance);
$shopEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Shop::class)->findOneBy(['title' => 'Printchampion']); $shopEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Shop::class)->findOneBy([
'title' => 'Printchampion',
]);
$shop = new Shop(); $shop = new Shop();
$shop->setUid($shopEntity->getUid()); $shop->setUid($shopEntity->getUid());
@ -113,12 +117,12 @@ trait RefreshDatabaseTrait
$shop->setParcelCancelationNumberPattern('STS-{{ "now"|date("Ym") }}-{{number}}'); $shop->setParcelCancelationNumberPattern('STS-{{ "now"|date("Ym") }}-{{number}}');
$shop->setParcelInvoiceNumberPattern('STR-{{ "now"|date("Ym") }}-{{number}}'); $shop->setParcelInvoiceNumberPattern('STR-{{ "now"|date("Ym") }}-{{number}}');
$shop->setSenderZip(12345); $shop->setSenderZip(12345);
$shop->setSenderCity("ShopMusterOrt"); $shop->setSenderCity('ShopMusterOrt');
$shop->setSenderCompany("ShopMusterCompany"); $shop->setSenderCompany('ShopMusterCompany');
$shop->setSenderLId("ShopMusterLeitwegId"); $shop->setSenderLId('ShopMusterLeitwegId');
$shop->setSenderIban("ShopMusterIban"); $shop->setSenderIban('ShopMusterIban');
$shop->setSenderEmail("ShopMusterEmail"); $shop->setSenderEmail('ShopMusterEmail');
$shop->setSenderSteuerId("ShopMusterSteuerId"); $shop->setSenderSteuerId('ShopMusterSteuerId');
$doc->persist($shop); $doc->persist($shop);
@ -130,18 +134,22 @@ trait RefreshDatabaseTrait
$doc->persist($country); $doc->persist($country);
$contactEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Contact::class)->findOneBy(['username' => 'company@shop.de']); $contactEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Contact::class)->findOneBy([
'username' => 'company@shop.de',
]);
$contact = new Contact(); $contact = new Contact();
$contact->setAccountType(AccountType::COMPANY); $contact->setAccountType(AccountType::COMPANY);
$contact->setUid((string)$contactEntity->getUid()); $contact->setUid((string) $contactEntity->getUid());
$doc->persist($contact); $doc->persist($contact);
$contactEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Contact::class)->findOneBy(['username' => 'association@shop.de']); $contactEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Contact::class)->findOneBy([
'username' => 'association@shop.de',
]);
$contact = new Contact(); $contact = new Contact();
$contact->setAccountType(AccountType::ASSOCIATION); $contact->setAccountType(AccountType::ASSOCIATION);
$contact->setUid((string)$contactEntity->getUid()); $contact->setUid((string) $contactEntity->getUid());
$doc->persist($contact); $doc->persist($contact);
$help1 = new Help(); $help1 = new Help();
@ -170,5 +178,4 @@ trait RefreshDatabaseTrait
return $kernel; return $kernel;
} }
} }

View File

@ -13,7 +13,6 @@ import { ElementProperties, SpecialElementProperties } from '../components/app/e
import { ElementDependency } from '../components/app/elementdependency' import { ElementDependency } from '../components/app/elementdependency'
import { Main } from '../components/app/main' import { Main } from '../components/app/main'
import FormulaVisualizer from './app/FormulaVisualizer.vue' import FormulaVisualizer from './app/FormulaVisualizer.vue'
import JsonView from './app/JsonView.vue'
import XmlView from './app/XmlView.vue' import XmlView from './app/XmlView.vue'
import ParameterView from './app/ParameterView.vue' import ParameterView from './app/ParameterView.vue'
import PaperDBView from './app/PaperDBView.vue' import PaperDBView from './app/PaperDBView.vue'

View File

@ -3,7 +3,6 @@ import { ref, watch } from 'vue'
import { Codemirror } from 'vue-codemirror' import { Codemirror } from 'vue-codemirror'
import { php } from '@codemirror/lang-php' import { php } from '@codemirror/lang-php'
import { useGlobalStore } from '../../stores/Global' import { useGlobalStore } from '../../stores/Global'
import { debounce } from 'ts-debounce'
import { Button } from '../ui/button' import { Button } from '../ui/button'
const store = useGlobalStore(); const store = useGlobalStore();

View File

@ -4,7 +4,6 @@ import { Codemirror } from 'vue-codemirror'
import { xml } from '@codemirror/lang-xml' import { xml } from '@codemirror/lang-xml'
import { useGlobalStore } from '../../stores/Global' import { useGlobalStore } from '../../stores/Global'
import xmlFormat from 'xml-formatter' import xmlFormat from 'xml-formatter'
import { debounce } from 'ts-debounce'
import { Button } from '../ui/button' import { Button } from '../ui/button'
const store = useGlobalStore(); const store = useGlobalStore();

View File

@ -3,7 +3,6 @@ import { ref, watch } from 'vue'
import { Codemirror } from 'vue-codemirror' import { Codemirror } from 'vue-codemirror'
import { php } from '@codemirror/lang-php' import { php } from '@codemirror/lang-php'
import { useGlobalStore } from '../../stores/Global' import { useGlobalStore } from '../../stores/Global'
import { debounce } from 'ts-debounce'
import { Button } from '../ui/button' import { Button } from '../ui/button'
const store = useGlobalStore(); const store = useGlobalStore();

View File

@ -1,10 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { Button } from '../ui/button' import { Button } from '../ui/button'
import { useGlobalStore } from '../../stores/Global' import { useGlobalStore } from '../../stores/Global'
import { useItemStore } from '../../stores/Items'
const globalStore = useGlobalStore() const globalStore = useGlobalStore()
const itemStore = useItemStore()
function manualSave() { function manualSave() {
globalStore.manualSave() globalStore.manualSave()

View File

@ -4,7 +4,6 @@ import { Codemirror } from 'vue-codemirror'
import { xml } from '@codemirror/lang-xml' import { xml } from '@codemirror/lang-xml'
import { useGlobalStore } from '../../stores/Global' import { useGlobalStore } from '../../stores/Global'
import xmlFormat from 'xml-formatter' import xmlFormat from 'xml-formatter'
import { debounce } from 'ts-debounce'
import { Button } from '../ui/button' import { Button } from '../ui/button'
const store = useGlobalStore(); const store = useGlobalStore();

View File

@ -52,7 +52,7 @@ watch(openModal, (newOpenModal) => {
<SheetDescription> <SheetDescription>
</SheetDescription> </SheetDescription>
</SheetHeader> </SheetHeader>
<div class="flex flex-col w-full m-2"> <div class="flex flex-col w-full p-2">
<HeadlineElementProperties <HeadlineElementProperties
v-if="globalStore.getActiveItem.type === 6" v-if="globalStore.getActiveItem.type === 6"
v-model="globalStore.getActiveItem as HeadlineElement" v-model="globalStore.getActiveItem as HeadlineElement"

View File

@ -1,16 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import Fieldset from '../../../model/Fieldset'; import FieldsetElement from '../../../model/FieldsetElement';
import BaseElement from '../../../model/BaseElement';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import EmptyElementForm from './EmptyElementForm.vue'
import { RenderElements } from './../renderelements' import { RenderElements } from './../renderelements'
import { useItemStore } from '../../../stores/Items'
import { useGlobalStore } from '../../../stores/Global' import { useGlobalStore } from '../../../stores/Global'
import Parser from '../../../lib/parser' import Parser from '../../../lib/parser'
import { CirclePlus } from 'lucide-vue-next'; import { CirclePlus } from 'lucide-vue-next';
interface Props { interface Props {
modelValue: Fieldset modelValue: FieldsetElement
} }
const props = defineProps<Props>() const props = defineProps<Props>()
@ -19,7 +16,6 @@ let emit = defineEmits(['update:modelValue']);
const dragUuid = ref(""); const dragUuid = ref("");
let isPreview = ref(false) let isPreview = ref(false)
const itemStore = useItemStore()
const globalStore = useGlobalStore() const globalStore = useGlobalStore()
const theModel = computed({ const theModel = computed({
@ -27,7 +23,7 @@ const theModel = computed({
set: (value) => emit('update:modelValue', value), set: (value) => emit('update:modelValue', value),
}); });
const onDrop = (event: DragEvent, targetUuid: string, fieldset: Fieldset) => { const onDrop = (event: DragEvent, targetUuid: string, fieldset: FieldsetElement) => {
if(globalStore.getDragMode == "insert") { if(globalStore.getDragMode == "insert") {
const itemId = Number(event.dataTransfer?.getData('itemId')); const itemId = Number(event.dataTransfer?.getData('itemId'));
@ -37,7 +33,7 @@ const onDrop = (event: DragEvent, targetUuid: string, fieldset: Fieldset) => {
} }
} }
const dragLeaveEmpty = (event: DragEvent, uuid: string) => { const dragLeaveEmpty = (event: DragEvent) => {
dragUuid.value = "" dragUuid.value = ""
event.stopImmediatePropagation() event.stopImmediatePropagation()
} }
@ -61,8 +57,8 @@ const dragEnterEmpty = (event: DragEvent, uuid: string) => {
<template> <template>
<fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-full border p-4"> <fieldset class="fieldset bg-base-200 border-base-300 rounded-box w-full border p-4">
<legend v-if="theModel!.name != ''" class="fieldset-legend">{{ theModel!.name}}</legend> <legend v-if="theModel!.label != ''" class="fieldset-legend">{{ theModel!.label}}</legend>
<div class="h-8 group items-center content-justify w-full mb-2" v-if="theModel!.items.length == 0" @drop="onDrop($event, theModel!.uuid, theModel!)" @dragleave="dragLeaveEmpty($event)" @dragenter="dragEnterEmpty($event)"> <div class="h-8 group items-center content-justify w-full mb-2" v-if="theModel!.items.length == 0" @drop="onDrop($event, theModel!.uuid, theModel!)" @dragleave="dragLeaveEmpty($event)" @dragenter="dragEnterEmpty($event, theModel!.uuid)">
<div class="inline-flex items-center justify-center w-full pointer-events-none"> <div class="inline-flex items-center justify-center w-full pointer-events-none">
<hr class="w-64 h-px my-2 bg-gray-200 border-0 dark:bg-gray-700 transition duration-200 pointer-events-none" :class="{ 'bg-orange-500': dragUuid == theModel!.uuid }" > <hr class="w-64 h-px my-2 bg-gray-200 border-0 dark:bg-gray-700 transition duration-200 pointer-events-none" :class="{ 'bg-orange-500': dragUuid == theModel!.uuid }" >
<span class="absolute px-3 font-medium text-gray-900 bg-white dark:text-white dark:bg-gray-900 pointer-events-none"><CirclePlus :class="{ 'text-orange-500': dragUuid == theModel!.uuid }" class="transition duration-200 pointer-events-none" /></span> <span class="absolute px-3 font-medium text-gray-900 bg-white dark:text-white dark:bg-gray-900 pointer-events-none"><CirclePlus :class="{ 'text-orange-500': dragUuid == theModel!.uuid }" class="transition duration-200 pointer-events-none" /></span>

View File

@ -1,125 +1,25 @@
<script lang="ts" setup> <script lang="ts" setup>
import { fetchMediaDirectories, uploadFile } from '../../../lib/api'; import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue'; import MediaElement from '../../../model/MediaElement';
import { computed } from 'vue';
import { ImageOff } from 'lucide-vue-next'
interface Props { const props = defineProps<{
modelValue: MediaElement modelValue: MediaElement
} }>()
const props = defineProps<Props>()
let emit = defineEmits(['update:modelValue']); let emit = defineEmits(['update:modelValue']);
const theModel = computed({ const theModel = computed<InputElement>({
get: () => props.modelValue, get: () => props.modelValue,
set: (value) => emit('update:modelValue', value), set: (value) => emit('update:modelValue', value),
}); });
const uploadProgress = ref(0);
const isDragging = ref(false);
const directories = ref<string[]>([]);
const selectedDirectory = ref<string>('');
const onDragOver = () => {
isDragging.value = true;
};
const onDragLeave = () => {
isDragging.value = false;
};
const onDrop = (event: DragEvent) => {
isDragging.value = false;
const files = event.dataTransfer?.files;
if (files && files.length > 0) {
handleFile(files[0]);
}
};
const onFileChange = (event: Event) => {
const target = event.target as HTMLInputElement;
const files = target.files;
if (files && files.length > 0) {
handleFile(files[0]);
}
};
onMounted(async () => {
try {
directories.value = await fetchMediaDirectories();
if (directories.value.length > 0) {
selectedDirectory.value = directories.value[0];
}
} catch (error) {
console.error('Failed to fetch directories', error);
}
});
const handleFile = async (file: File) => {
uploadProgress.value = 0;
try {
await uploadFile(file, (progress) => {
uploadProgress.value = progress;
});
console.log('Upload successful');
// Handle successful upload
} catch (error) {
console.error('Upload failed', error);
// Handle failed upload
} finally {
setTimeout(() => (uploadProgress.value = 0), 2000);
}
};
</script> </script>
<template> <template>
<div class="flex gap-2 flex-row"> <div class="flex gap-2 flex-row">
<label class="w-60 flex-inital">{{theModel.name}}</label> <div v-if="theModel!.url == ''" class="w-full rounded bg-gray-300 justify-center content-center flex"><ImageOff class="size-20 m-10 place-self-center" /></div>
<div <img v-if="theModel!.url != ''" class="" :src="theModel!.url"/>
class="flex items-center justify-center w-full"
@dragover.prevent="onDragOver"
@dragleave.prevent="onDragLeave"
@drop.prevent="onDrop"
:class="{ 'border-blue-500': isDragging }"
>
<label
for="dropzone-file"
class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
>
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg
class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
/>
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span class="font-semibold">Click to upload</span> or drag and drop
</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
SVG, PNG, JPG or GIF (MAX. 800x400px)
</p>
</div>
<input id="dropzone-file" type="file" class="hidden" @change="onFileChange" />
</label>
</div>
<div class="flex items-center justify-center w-full">
<select v-model="selectedDirectory" class="w-full p-2 border rounded-md">
<option v-for="dir in directories" :key="dir" :value="dir">{{ dir }}</option>
</select>
</div>
<div v-if="uploadProgress > 0" class="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
<div class="bg-blue-600 h-2.5 rounded-full" :style="{ width: uploadProgress + '%' }"></div>
</div>
</div> </div>
</template> </template>

View File

@ -69,7 +69,7 @@ const dragEnterEmpty = (event: DragEvent, uuid: string) => {
} }
} }
const addColumn = (mode: int, model: Row, uuid: string) => { const addColumn = (mode: number, model: Row, uuid: string) => {
if(mode == 1) { if(mode == 1) {
model.addColumnAtTheBeginning(new Column()); model.addColumnAtTheBeginning(new Column());

View File

@ -2,15 +2,6 @@
import FieldsetElement from '../../../model/FieldsetElement'; import FieldsetElement from '../../../model/FieldsetElement';
import { computed } from 'vue'; import { computed } from 'vue';
import { Input } from '../../../components/ui/input' import { Input } from '../../../components/ui/input'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '../../../components/ui/select'
const props = defineProps({ const props = defineProps({
modelValue: FieldsetElement modelValue: FieldsetElement
}) })
@ -27,6 +18,6 @@ const theModel = computed({
<template> <template>
<label>{{ $t('id') }}</label> <label>{{ $t('id') }}</label>
<Input v-model="theModel!.id" /> <Input v-model="theModel!.id" />
<label>{{ $t('name') }}</label> <label>{{ $t('label') }}</label>
<Input v-model="theModel!.name"/> <Input v-model="theModel!.label" />
</template> </template>

View File

@ -2,7 +2,9 @@
import MediaElement from '../../../model/MediaElement'; import MediaElement from '../../../model/MediaElement';
import { computed } from 'vue'; import { computed } from 'vue';
import { Input } from '../../../components/ui/input' import { Input } from '../../../components/ui/input'
//import { useMedia } from '../../../composables/useMedia' import { Button } from '../../../components/ui/button'
import { fetchMediaDirectories, uploadFile } from '../../../lib/api';
import { onMounted, ref } from 'vue';
const props = defineProps({ const props = defineProps({
modelValue: MediaElement modelValue: MediaElement
@ -15,15 +17,121 @@ const theModel = computed({
set: (value) => emit('update:modelValue', value), set: (value) => emit('update:modelValue', value),
}); });
//const { media, loading, error } = useMedia() interface Folder {
uuid: string
title: string
}
const uploadProgress = ref(0);
const isDragging = ref(false);
const directories = ref<Folder[]>([]);
const selectedDirectory = ref<string>('');
const onDragOver = () => {
isDragging.value = true;
};
const onDragLeave = () => {
isDragging.value = false;
};
const onDrop = (event: DragEvent) => {
isDragging.value = false;
const files = event.dataTransfer?.files;
if (files && files.length > 0) {
handleFile(files[0]);
}
};
const onFileChange = (event: Event) => {
const target = event.target as HTMLInputElement;
const files = target.files;
if (files && files.length > 0) {
handleFile(files[0]);
}
};
onMounted(async () => {
try {
let response: any = await fetchMediaDirectories()
directories.value = response.data
if (response.data.length > 0) {
selectedDirectory.value = directories.value[0].uuid;
}
} catch (error) {
console.error('Failed to fetch directories', error);
}
});
const handleFile = async (file: File) => {
uploadProgress.value = 0;
if(selectedDirectory) {
try {
let response = await uploadFile(file, selectedDirectory.value, (progress) => {
uploadProgress.value = progress;
});
theModel.value.url = response.url
theModel.value.default = response.uuid
// Handle successful upload
} catch (error) {
console.error('Upload failed', error);
// Handle failed upload
} finally {
setTimeout(() => (uploadProgress.value = 0), 2000);
}
}
};
</script> </script>
<template> <template>
<label>{{ $t('id') }}</label> <div>
<Input v-model="theModel!.id" /> <label>{{ $t('id') }}</label>
<label>{{ $t('default') }}</label> <Input v-model="theModel!.id" />
<Input v-model="theModel!.default"/> <Button class="my-2 w-full">Mediabrowser</Button>
<label>{{ $t('name') }}</label> <div
<Input v-model="theModel!.name"/> class="flex items-center justify-center w-full"
@dragover.prevent="onDragOver"
@dragleave.prevent="onDragLeave"
@drop.prevent="onDrop"
:class="{ 'border-blue-500': isDragging }"
>
<label
for="dropzone-file"
class="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
>
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg
class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
/>
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span class="font-semibold">Click to upload</span> or drag and drop
</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
SVG, PNG, JPG or GIF (MAX. 800x400px)
</p>
</div>
<input id="dropzone-file" type="file" class="hidden" @change="onFileChange" />
</label>
</div>
<div class="flex items-center justify-center w-full">
<select v-model="selectedDirectory" class="w-full p-2 border rounded-md">
<option v-for="folder in directories" :key="folder.uuid" :value="folder.uuid">{{ folder.title }}</option>
</select>
</div>
<div v-if="uploadProgress > 0" class="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
<div class="bg-blue-600 h-2.5 rounded-full" :style="{ width: uploadProgress + '%' }"></div>
</div>
</div>
</template> </template>

View File

@ -143,7 +143,7 @@ const editElementDependency = (item: BaseElement) => {
/> />
<FieldsetElementForm <FieldsetElementForm
v-if="item.type === 12" v-if="item.type === 12"
v-model="item as Fieldset" v-model="item as FieldsetElement"
/> />
<RowElementForm <RowElementForm
v-if="item.type === 7" v-if="item.type === 7"

View File

@ -3,9 +3,6 @@ import ky from 'ky';
const api = ky.create({ const api = ky.create({
prefixUrl: '/apps', prefixUrl: '/apps',
headers: {
'Content-Type': 'application/json',
},
timeout: 10000, timeout: 10000,
hooks: { hooks: {
afterResponse: [ afterResponse: [
@ -108,12 +105,13 @@ export const savePaperContainerToApi = async (uuid: string, paperContainer: stri
} }
} }
export const uploadFile = async (file: File, onProgress: (progress: number) => void) => { export const uploadFile = async (file: File, folder: string, onProgress: (progress: number) => void) => {
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
formData.append('folder', folder);
try { try {
const response = await api.post('api/plugin/media/upload', { // Changed to a more appropriate media upload endpoint const response = await api.post('api/media/create', {
body: formData, body: formData,
onDownloadProgress: (progress) => { onDownloadProgress: (progress) => {
onProgress(Math.round(progress.percent * 100)); onProgress(Math.round(progress.percent * 100));
@ -128,7 +126,7 @@ export const uploadFile = async (file: File, onProgress: (progress: number) => v
export const fetchMediaDirectories = async () => { export const fetchMediaDirectories = async () => {
try { try {
const response = await api.get('api/plugin/media/directories'); const response = await api.get('api/media/folder/all');
return await response.json(); return await response.json();
} catch (error) { } catch (error) {
console.error('Error fetching media directories:', error); console.error('Error fetching media directories:', error);

View File

@ -1,4 +1,4 @@
import {v4 as uuidv4} from 'uuid' import { v4 as uuidv4 } from 'uuid'
import Dependency from './Dependency.ts' import Dependency from './Dependency.ts'
export enum ElementType { export enum ElementType {
@ -11,6 +11,7 @@ export enum ElementType {
Column = 8, Column = 8,
Row = 7, Row = 7,
Media = 9, Media = 9,
FieldsetElement = 12,
} }
export default class BaseElement { export default class BaseElement {
@ -51,15 +52,14 @@ export default class BaseElement {
} }
changeFocus(uuid: string) { changeFocus(uuid: string) {
if(this.uuid == uuid) { if (this.uuid == uuid) {
this.isFocused = true this.isFocused = true
}else{ } else {
this.isFocused = false this.isFocused = false
} }
} }
addDependency(dep: Dependency) addDependency(dep: Dependency) {
{
this.dependencys.push(dep) this.dependencys.push(dep)
} }

View File

@ -1,9 +1,10 @@
import BaseElement from "./BaseElement"; import BaseElement from "./BaseElement";
export default class MediaElement extends BaseElement { export default class MediaElement extends BaseElement {
default: string = "" default: string = ""
name: string = "" name: string = ""
xmlType: string = "img" xmlType: string = "img"
url: string = ""
constructor() { constructor() {
super(); super();

View File

@ -24,7 +24,7 @@ export const useGlobalStore = defineStore('global', {
shopUuid: "", shopUuid: "",
saving: false, saving: false,
syncing: false, syncing: false,
currentTab: 'designer', currentTab: 'designer' as string | number,
}), }),
getters: { getters: {
getActiveItem: (state) => state.activeItem as BaseElement, getActiveItem: (state) => state.activeItem as BaseElement,
@ -152,7 +152,7 @@ export const useGlobalStore = defineStore('global', {
} }
}, },
setCurrentTab(tab: string) { setCurrentTab(tab: string | number) {
this.currentTab = tab this.currentTab = tab
} }
} }

View File

@ -24,12 +24,16 @@ export default defineConfig({
}, },
server: { server: {
proxy: { proxy: {
'/uploads': {
target: 'http://type-dev-tp.local',
changeOrigin: true,
},
'/apps': { '/apps': {
target: 'http://type-dev-tp.local', target: 'http://type-dev-tp.local',
changeOrigin: true, changeOrigin: true,
configure: (proxy) => { configure: (proxy) => {
proxy.on('proxyReq', (proxyReq) => { proxy.on('proxyReq', (proxyReq) => {
proxyReq.setHeader('Authorization', 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3NTM2ODg4MTksImV4cCI6MTc1MzY5MjQxOSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfU0hPUF9PUEVSQVRPUiIsIlJPTEVfVVNFUiIsIlJPTEVfVVNFUiIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9FZGl0IiwiUk9MRV9QU0NfQ29sbGVjdF9Db250YWN0X0FkZCIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9EZWxldGUiLCJST0xFX1BTQ19Db2xsZWN0X0NvbnRhY3RfTG9jayIsIlJPTEVfUFNDX1IyX1NlbmRjbG91ZF9TaG93Il0sInVpZCI6MX0.j8BCuZxhIqw5G6TVgV9znMkZCF98T96rm6uYyj158-vZWykPbCyLtrNRk1wJXNA5gsGKUQYKffpqOlMFIkc9YlSKcYTSZjyablGlue34ELuoT7h4JXrKSD8SYBB99wphsV0aViAoyabn5L7QZLSzhwqZYxvJrmCW_KcrCcAr1Uy-AGYJNOE39bknwwNolRSPh2Gu5x2tE85LugazW0jo9ghWuPVOB35BB9NN1HHmjwzO4uinv8oorlGSWAsnLOOdjPAY7LjcoAY59v5r3U4FmMkHj5QfxxUsBaN_0ostJWxDfqr2cJqBEk-XimsaGQdyjuD6bintNO_N2m4DxuIjgw'); proxyReq.setHeader('Authorization', 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3NTM4ODEwMjUsImV4cCI6MTc1Mzg4NDYyNSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfU0hPUF9PUEVSQVRPUiIsIlJPTEVfVVNFUiIsIlJPTEVfVVNFUiIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9FZGl0IiwiUk9MRV9QU0NfQ29sbGVjdF9Db250YWN0X0FkZCIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9EZWxldGUiLCJST0xFX1BTQ19Db2xsZWN0X0NvbnRhY3RfTG9jayIsIlJPTEVfUFNDX1IyX1NlbmRjbG91ZF9TaG93Il0sInVpZCI6MX0.T0JzZ8qoZOqOOGtHXtuGvY5xAwwWGSAHh9MXgGJlyngjsmA_RWUnL8wVW3Ah827sJQNL2W1JPEz_f9CuCvfCSVjoLml9T_n5N5xtB98wVdcksgh3PvtFrYs-NVUat9QlwJ54F0cUXIkyuimEc2op0Y2sSC9Pyw6d9m8WtYrPX657uXZ3U8KcX8FIqCMWpjzIsbLK2QemT-fgkJpVnSdvo8nFvComZ1yDPvj5Zl_m0NEF3CybYdxOOwz42egI297BM_qGp6_cZLeSrl2EwphzeFqPo8-q9wmuHj33HZozlwjlu_Uvp4vq0Jr98-WZkIUwK7706E1t_TdSc46nONyxVA');
}); });
}, },
}, },

File diff suppressed because one or more lines are too long

View File

@ -6,20 +6,20 @@ var delivery_date = "";
var delivery_info = ""; var delivery_info = "";
$(function(){ $(function() {
$(".printOffer").click(function(event) { $(".printOffer").click(function(event) {
var formElements = $('#CALCFORM').serializeArray(); var formElements = $('#CALCFORM').serializeArray();
var formArray = {}; var formArray = {};
$.each(formElements, function( index, value ) { $.each(formElements, function(index, value) {
var name = value.name; var name = value.name;
var val = value.value; var val = value.value;
if(name != "xmlProduct") { if (name != "xmlProduct") {
formArray[name] = val; formArray[name] = val;
} }
}); });
var xmlProduct = ""; var xmlProduct = "";
if($('#CALCFORM select[name="xmlProduct"]').length > 0) { if ($('#CALCFORM select[name="xmlProduct"]').length > 0) {
xmlProduct = $('#CALCFORM select[name="xmlProduct"]').val(); xmlProduct = $('#CALCFORM select[name="xmlProduct"]').val();
} }
@ -28,8 +28,8 @@ $(function(){
contentType: "application/json", contentType: "application/json",
dataType: 'json', dataType: 'json',
method: 'post', method: 'post',
data: JSON.stringify({productUUId: productUUId, values: formArray, count: 1, uploadMode: $('#upload_mode').val(), deliveryDate: delivery_date, deliveryInfo: delivery_info, xmlProduct: xmlProduct }), data: JSON.stringify({ productUUId: productUUId, values: formArray, count: 1, uploadMode: $('#upload_mode').val(), deliveryDate: delivery_date, deliveryInfo: delivery_info, xmlProduct: xmlProduct }),
success: function(result){ success: function(result) {
window.open('/apps/product/offer/' + productUUId); window.open('/apps/product/offer/' + productUUId);
} }
}); });
@ -39,54 +39,54 @@ $(function(){
$("#in_basket").click(function(event) { $("#in_basket").click(function(event) {
if(($('#upload_mode').val() == 'none' || $('#upload_mode').val() == '') && $('#myUpload .modal-body > div').length > 0 ) { if (($('#upload_mode').val() == 'none' || $('#upload_mode').val() == '') && $('#myUpload .modal-body > div').length > 0) {
$('#myUpload').modal(); $('#myUpload').modal();
return; return;
} }
var formElements = $('#CALCFORM').serializeArray(); var formElements = $('#CALCFORM').serializeArray();
var formArray = {}; var formArray = {};
$.each(formElements, function( index, value ) { $.each(formElements, function(index, value) {
var name = value.name; var name = value.name;
var val = value.value; var val = value.value;
if(name != "xmlProduct") { if (name != "xmlProduct") {
formArray[name] = val; formArray[name] = val;
} }
}); });
var xmlProduct = ""; var xmlProduct = "";
if($('#CALCFORM select[name="xmlProduct"]').length > 0) { if ($('#CALCFORM select[name="xmlProduct"]').length > 0) {
xmlProduct = $('#CALCFORM select[name="xmlProduct"]').val(); xmlProduct = $('#CALCFORM select[name="xmlProduct"]').val();
} }
if(productLoaded === 1) { if (productLoaded === 1) {
$.ajax({ $.ajax({
url: "/apps/api/basket/legacy/update", url: "/apps/api/basket/legacy/update",
contentType: "application/json", contentType: "application/json",
dataType: 'json', dataType: 'json',
method: 'post', method: 'post',
data: JSON.stringify({productUUId: productUUId, values: formArray, count: 1, uploadMode: $('#upload_mode').val(), deliveryDate: delivery_date, deliveryInfo: delivery_info, xmlProduct: xmlProduct }), data: JSON.stringify({ productUUId: productUUId, values: formArray, count: 1, uploadMode: $('#upload_mode').val(), deliveryDate: delivery_date, deliveryInfo: delivery_info, xmlProduct: xmlProduct }),
success: function(result){ success: function(result) {
document.location="/basket"; document.location = "/basket";
} }
}); });
return false; return false;
}else{ } else {
$.ajax({ $.ajax({
url: "/apps/api/basket/legacy/add", url: "/apps/api/basket/legacy/add",
contentType: "application/json", contentType: "application/json",
dataType: 'json', dataType: 'json',
method: 'post', method: 'post',
data: JSON.stringify({productUUId: productUUId, values: formArray, count: 1, uploadMode: $('#upload_mode').val(), deliveryDate: delivery_date, deliveryInfo: delivery_info, xmlProduct: xmlProduct }), data: JSON.stringify({ productUUId: productUUId, values: formArray, count: 1, uploadMode: $('#upload_mode').val(), deliveryDate: delivery_date, deliveryInfo: delivery_info, xmlProduct: xmlProduct }),
success: function(result){ success: function(result) {
document.location="/basket"; document.location = "/basket";
} }
}); });
return false; return false;
} }
}); });
if(productLoaded === 1 || productReBuy === 1) { if (productLoaded === 1 || productReBuy === 1) {
loadCalc(productUUId, true); loadCalc(productUUId, true);
}else{ } else {
loadCalc(productUUId); loadCalc(productUUId);
} }
}) })
@ -96,12 +96,12 @@ function loadCalc(productUUId, firstLoad = false) {
calcHasError = false; calcHasError = false;
if(firstLoad) { if (firstLoad) {
formArray = productValues; formArray = productValues;
} }
$.each($('#CALCFORM select'), function(index, value) { $.each($('#CALCFORM select'), function(index, value) {
if(value.name != "xmlProduct") { if (value.name != "xmlProduct") {
formArray[value.name] = $(value).val(); formArray[value.name] = $(value).val();
} }
}); });
@ -109,15 +109,15 @@ function loadCalc(productUUId, firstLoad = false) {
formArray[value.name] = $(value).val(); formArray[value.name] = $(value).val();
}); });
$.each($('#CALCFORM input[type=checkbox]:checked'), function(index, value) { $.each($('#CALCFORM input[type=checkbox]:checked'), function(index, value) {
if(!Array.isArray(formArray[value.name])) { if (!Array.isArray(formArray[value.name])) {
formArray[value.name] = Array.from($(value).val()); formArray[value.name] = Array.from($(value).val());
}else{ } else {
formArray[value.name].push($(value).val()); formArray[value.name].push($(value).val());
} }
}); });
$.each($('#CALCFORM input[type=checkbox]'), function(index, value) { $.each($('#CALCFORM input[type=checkbox]'), function(index, value) {
if(!Array.isArray(formArray[value.name])) { if (!Array.isArray(formArray[value.name])) {
formArray[value.name] = []; formArray[value.name] = [];
} }
}); });
@ -125,116 +125,116 @@ function loadCalc(productUUId, firstLoad = false) {
$('#CALCFORM .invalid-feedback').remove(); $('#CALCFORM .invalid-feedback').remove();
var xmlProduct = ""; var xmlProduct = "";
if($('#CALCFORM select[name="xmlProduct"]').length > 0) { if ($('#CALCFORM select[name="xmlProduct"]').length > 0) {
xmlProduct = $('#CALCFORM select[name="xmlProduct"]').val(); xmlProduct = $('#CALCFORM select[name="xmlProduct"]').val();
} }
$.ajax({ $.ajax({
url: "/apps/api/plugin/system/psc/xmlcalc/price", url: "/apps/api/plugin/system/psc/xmlcalc/price",
contentType: "application/json", contentType: "application/json",
dataType: 'json', dataType: 'json',
method: 'post', method: 'post',
data: JSON.stringify({product: productUUId, test: false, values : formArray, xmlProduct: xmlProduct}), data: JSON.stringify({ product: productUUId, test: false, values: formArray, xmlProduct: xmlProduct }),
success: function(result){ success: function(result) {
var validElements = []; var validElements = [];
var groupsFound = []; var groupsFound = [];
if(result.xmlProductTypes.length > 1) { if (result.xmlProductTypes.length > 1) {
var $label = $('<label>').addClass('form-label').text("Typ"); var $label = $('<label>').addClass('form-label').text("Typ");
$container = $('<div class="form-group" id="container_xmlProduct"></div>'); $container = $('<div class="form-group" id="container_xmlProduct"></div>');
var $obj = $('<select>', { var $obj = $('<select>', {
id: 'xmlProduct', id: 'xmlProduct',
title: 'Typ', title: 'Typ',
name: 'xmlProduct' name: 'xmlProduct'
}); });
$.each(result.xmlProductTypes, function(ind, opt) { $.each(result.xmlProductTypes, function(ind, opt) {
$obj.append(new Option(opt, opt, result.xmlProduct == opt, result.xmlProduct == opt)); $obj.append(new Option(opt, opt, result.xmlProduct == opt, result.xmlProduct == opt));
}); });
$obj.addClass('form-control'); $obj.addClass('form-control');
$element = $('<div>').addClass("form-controls"); $element = $('<div>').addClass("form-controls");
$inputGroup = $('<div class="input-group input-group-sm">'); $inputGroup = $('<div class="input-group input-group-sm">');
$inputGroup.appendTo($element); $inputGroup.appendTo($element);
$obj.appendTo($inputGroup); $obj.appendTo($inputGroup);
addOrReplaceFormElement("", 'xmlProduct', $container, $element, $label, true, ""); addOrReplaceFormElement("", 'xmlProduct', $container, $element, $label, true, "");
} }
buildForm(result.elements, "", validElements, result.colorDb); buildForm(result.elements, "", validElements, result.colorDb);
$.each(result.displayGroups, function(index, group) { $.each(result.displayGroups, function(index, group) {
buildcollapseHeader(group); buildcollapseHeader(group);
buildForm(result.elements, group.id, validElements, result.colorDb); buildForm(result.elements, group.id, validElements, result.colorDb);
buildcollapseFooter(); buildcollapseFooter();
groupsFound.push(group.id); groupsFound.push(group.id);
}); });
$('button.toogle-group').each(function(i, val) { $('button.toogle-group').each(function(i, val) {
var groupId = $(this).attr('group'); var groupId = $(this).attr('group');
if(groupsFound.indexOf(groupId) === -1) { if (groupsFound.indexOf(groupId) === -1) {
$(this).remove(); $(this).remove();
$('div[group-id="' + groupId + '"]').remove(); $('div[group-id="' + groupId + '"]').remove();
} }
}); });
$.each($('#CALCFORM select'), function(index, value) { $.each($('#CALCFORM select'), function(index, value) {
if(value.name != "xmlProduct" && validElements.indexOf(value.name) === -1) { if (value.name != "xmlProduct" && validElements.indexOf(value.name) === -1) {
removeElement(value) removeElement(value)
} }
}) })
$.each($('#CALCFORM input'), function(index, value) { $.each($('#CALCFORM input'), function(index, value) {
if(value.name != "xmlProduct" && validElements.indexOf(value.name) === -1) { if (value.name != "xmlProduct" && validElements.indexOf(value.name) === -1) {
removeElement(value) removeElement(value)
} }
}) })
$('#CALCFORM input').each(function() { $('#CALCFORM input').each(function() {
var $elm = $(this); var $elm = $(this);
var elementHasError = false; var elementHasError = false;
if($elm.data('minValue') && $elm.data('minValue') > formArray[$elm.attr('id')]) { if ($elm.data('minValue') && $elm.data('minValue') > formArray[$elm.attr('id')]) {
$elm.parent().after($('<div class="invalid-feedback d-block">Wert muss größer oder gleich sein als: ' + $elm.data('minValue') + '</div>')); $elm.parent().after($('<div class="invalid-feedback d-block">Wert muss größer oder gleich sein als: ' + $elm.data('minValue') + '</div>'));
calcHasError = true; calcHasError = true;
elementHasError = true; elementHasError = true;
} }
if($elm.data('maxValue') && $elm.data('maxValue') < formArray[$elm.attr('id')]) { if ($elm.data('maxValue') && $elm.data('maxValue') < formArray[$elm.attr('id')]) {
$elm.parent().after($('<div class="invalid-feedback d-block">Wert muss kleiner oder gleich sein als: ' + $elm.data('maxValue') + '</div>')); $elm.parent().after($('<div class="invalid-feedback d-block">Wert muss kleiner oder gleich sein als: ' + $elm.data('maxValue') + '</div>'));
calcHasError = true; calcHasError = true;
elementHasError = true; elementHasError = true;
} }
if(elementHasError) { if (elementHasError) {
$elm.addClass("is-invalid"); $elm.addClass("is-invalid");
}else{ } else {
$elm.removeClass("is-invalid"); $elm.removeClass("is-invalid");
} }
}); });
if(calcHasError) { if (calcHasError) {
$('#netto').html("ERR"); $('#netto').html("ERR");
$('#mwert').html("ERR"); $('#mwert').html("ERR");
$('#brutto').html("ERR"); $('#brutto').html("ERR");
$("#in_basket").prop("disabled",true); $("#in_basket").prop("disabled", true);
// PRECALC AUSBLENDEN? // PRECALC AUSBLENDEN?
//loadPreCalc(productUUId, false, result.preCalc); //loadPreCalc(productUUId, false, result.preCalc);
}else{ } else {
$('#netto').html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.netto/100)); $('#netto').html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.netto / 100));
$('#mwert').html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.steuer/100)); $('#mwert').html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.steuer / 100));
$('#brutto').html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.brutto/100)); $('#brutto').html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.brutto / 100));
if($('#weight_display').length > 0) { if ($('#weight_display').length > 0) {
$('#weight_display span').html(new Intl.NumberFormat().format(result.weight/1000)); $('#weight_display span').html(new Intl.NumberFormat().format(result.weight / 1000));
} }
$("#in_basket").prop("disabled",false); $("#in_basket").prop("disabled", false);
loadPreCalc(productUUId, false, result.preCalc); loadPreCalc(productUUId, false, result.preCalc);
} }
bindCalcButtons(); bindCalcButtons();
productXml = result.xmlProduct; productXml = result.xmlProduct;
}, },
error: function(err){ error: function(err) {
} }
}); });
} }
@ -245,13 +245,14 @@ function bindCalcButtons() {
}); });
$('#CALCFORM input, #CALCFORM select').unbind(); $('#CALCFORM input, #CALCFORM select').unbind();
$('#CALCFORM input, #CALCFORM select').change(function() { $('#CALCFORM input, #CALCFORM select').change(function() {
loadCalc(productUUId); $("#in_basket").prop("disabled", true);
loadCalc(productUUId);
}); });
$('[data-toggle="popover"]').popover({html:true, animation: false}); $('[data-toggle="popover"]').popover({ html: true, animation: false });
} }
function buildcollapseHeader(group) { function buildcollapseHeader(group) {
if($('#group-' + group.id).length > 0) { if ($('#group-' + group.id).length > 0) {
$('button[group="' + group.id + '"]').text(group.name) $('button[group="' + group.id + '"]').text(group.name)
return; return;
} }
@ -268,9 +269,9 @@ function buildcollapseFooter(group) {
function loadPreCalc(productUUId, testMode, preCalc) { function loadPreCalc(productUUId, testMode, preCalc) {
$('#preCalc').html(''); $('#preCalc').html('');
if(preCalc.length === 0) { if (preCalc.length === 0) {
$('#preCalcContainer').hide(); $('#preCalcContainer').hide();
}else{ } else {
$('#preCalcContainer').show(); $('#preCalcContainer').show();
} }
$.each(preCalc, function(indexGroup, group) { $.each(preCalc, function(indexGroup, group) {
@ -282,12 +283,12 @@ function loadPreCalc(productUUId, testMode, preCalc) {
var formElements = $('#CALCFORM').serializeArray(); var formElements = $('#CALCFORM').serializeArray();
var formArray = {}; var formArray = {};
$.each(formElements, function( index, value ) { $.each(formElements, function(index, value) {
var name = value.name; var name = value.name;
var val = value.value; var val = value.value;
formArray[name] = val; formArray[name] = val;
}); });
$.each(variant.values, function(indexValue,value) { $.each(variant.values, function(indexValue, value) {
formArray[value.key] = value.value; formArray[value.key] = value.value;
}); });
$.ajax({ $.ajax({
@ -295,9 +296,9 @@ function loadPreCalc(productUUId, testMode, preCalc) {
contentType: "application/json", contentType: "application/json",
dataType: 'json', dataType: 'json',
method: 'post', method: 'post',
data: JSON.stringify({product: productUUId, test: testMode, values : formArray}), data: JSON.stringify({ product: productUUId, test: testMode, values: formArray }),
success: function(result){ success: function(result) {
$('#variant_' + indexGroup + '_' + indexVariant).html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.netto/100)); $('#variant_' + indexGroup + '_' + indexVariant).html(new Intl.NumberFormat('de-DE', { style: 'currency', currency: productLanguage }).format(result.netto / 100));
} }
}) })
}); });
@ -306,24 +307,24 @@ function loadPreCalc(productUUId, testMode, preCalc) {
function buildForm(elements, display_group = "", validElements = [], colorDb = []) { function buildForm(elements, display_group = "", validElements = [], colorDb = []) {
var previousId = null; var previousId = null;
$.each(elements, function(index, element) { $.each(elements, function(index, element) {
if(element.displayGroup != display_group) { if (element.displayGroup != display_group) {
return; return;
} }
if(element.valid === false) { if (element.valid === false) {
removeElement(element); removeElement(element);
return; return;
} }
validElements.push(element.id); validElements.push(element.id);
var $label = $('<label>').addClass('form-label').text(element.name); var $label = $('<label>').addClass('form-label').text(element.name);
$container = $('<div class="form-group" id="container_' + element.id + '"></div>'); $container = $('<div class="form-group" id="container_' + element.id + '"></div>');
if(element.htmlType == "hidden") { if (element.htmlType == "hidden") {
var $obj = $('<input>', { var $obj = $('<input>', {
type: 'hidden', type: 'hidden',
value: element.rawValue, value: element.rawValue,
id: element.id, id: element.id,
name: element.id name: element.id
}); });
$element = $('<div>').addClass("col-8"); $element = $('<div>').addClass("col-8");
$obj.appendTo($element); $obj.appendTo($element);
@ -331,71 +332,71 @@ function buildForm(elements, display_group = "", validElements = [], colorDb = [
addOrReplaceFormElement(previousId, element.id, $container, $element, null, element.valid); addOrReplaceFormElement(previousId, element.id, $container, $element, null, element.valid);
return; return;
} }
if(element.htmlType == "select") { if (element.htmlType == "select") {
var $obj = $('<select>', { var $obj = $('<select>', {
id: element.id, id: element.id,
title: element.help, title: element.help,
name: element.id name: element.id
}); });
if(element.colorSystem != "") { if (element.colorSystem != "") {
$.each(colorDb[element.colorSystem], function(ind, opt) { $.each(colorDb[element.colorSystem], function(ind, opt) {
if(opt.valid) { if (opt.valid) {
$obj.append(new Option(opt.name, opt.id, element.rawValue == opt.id, element.rawValue == opt.id)); $obj.append(new Option(opt.name, opt.id, element.rawValue == opt.id, element.rawValue == opt.id));
} }
}); });
}else{ } else {
$.each(element.options, function(ind, opt) { $.each(element.options, function(ind, opt) {
if(opt.valid) { if (opt.valid) {
if (typeof opt.deliveryDate !== 'undefined' && opt.selected) { if (typeof opt.deliveryDate !== 'undefined' && opt.selected) {
$('.versandhint').html('<div class="bg-info m-2 p-2"><b>' + opt.info + " " + opt.deliveryDate + '</b></div>'); $('.versandhint').html('<div class="bg-info m-2 p-2"><b>' + opt.info + " " + opt.deliveryDate + '</b></div>');
delivery_date = opt.deliveryDate; delivery_date = opt.deliveryDate;
delivery_info = opt.info; delivery_info = opt.info;
$obj.append(new Option(opt.name, opt.id, opt.selected, opt.selected)); $obj.append(new Option(opt.name, opt.id, opt.selected, opt.selected));
} else { } else {
$obj.append(new Option(opt.name, opt.id, opt.selected, opt.selected)); $obj.append(new Option(opt.name, opt.id, opt.selected, opt.selected));
} }
} }
}); });
} }
$obj.addClass('form-control'); $obj.addClass('form-control');
} }
if(element.htmlType == "text") { if (element.htmlType == "text") {
var $obj = $('<input>', { var $obj = $('<input>', {
type: 'text', type: 'text',
value: element.defaultValue, value: element.defaultValue,
id: element.id, id: element.id,
name: element.id, name: element.id,
title: element.help, title: element.help,
disabled: true disabled: true
}); });
$obj.addClass("form-control"); $obj.addClass("form-control");
} }
if(element.htmlType == "input") { if (element.htmlType == "input") {
var $obj = $('<input>', { var $obj = $('<input>', {
type: 'text', type: 'text',
value: element.rawValue, value: element.rawValue,
id: element.id, id: element.id,
title: element.help, title: element.help,
name: element.id name: element.id
}); });
if(element.minValue != null) { if (element.minValue != null) {
$obj.data('minValue', element.minValue); $obj.data('minValue', element.minValue);
} }
if(element.maxValue != null) { if (element.maxValue != null) {
$obj.data('maxValue', element.maxValue); $obj.data('maxValue', element.maxValue);
} }
$obj.addClass("form-control"); $obj.addClass("form-control");
} }
if(element.htmlType == "checkbox") { if (element.htmlType == "checkbox") {
var $obj = $('<div>', { var $obj = $('<div>', {
id: element.id id: element.id
}); });
$.each(element.options, function(ind, opt) { $.each(element.options, function(ind, opt) {
if(opt.valid) { if (opt.valid) {
var $objCheckbox = $('<div class="form-check">'); var $objCheckbox = $('<div class="form-check">');
let $checkbox = $('<input>', { let $checkbox = $('<input>', {
type: 'checkbox', type: 'checkbox',
@ -412,12 +413,12 @@ function buildForm(elements, display_group = "", validElements = [], colorDb = [
}); });
} }
if(element.htmlType == "radio") { if (element.htmlType == "radio") {
var $obj = $('<div>', { var $obj = $('<div>', {
id: element.id id: element.id
}); });
$.each(element.options, function(ind, opt) { $.each(element.options, function(ind, opt) {
if(opt.valid) { if (opt.valid) {
var $objCheckbox = $('<div class="form-check">'); var $objCheckbox = $('<div class="form-check">');
let $checkbox = $('<input>', { let $checkbox = $('<input>', {
type: 'radio', type: 'radio',
@ -435,12 +436,12 @@ function buildForm(elements, display_group = "", validElements = [], colorDb = [
} }
if(element.htmlType == "textarea") { if (element.htmlType == "textarea") {
var $obj = $('<textarea>', { var $obj = $('<textarea>', {
value: element.rawValue, value: element.rawValue,
id: element.id, id: element.id,
title: element.help, title: element.help,
name: element.id name: element.id
}); });
} }
@ -450,12 +451,12 @@ function buildForm(elements, display_group = "", validElements = [], colorDb = [
$obj.appendTo($inputGroup); $obj.appendTo($inputGroup);
addOrReplaceFormElement(previousId, element.id, $container, $element, $label, element.valid, display_group); addOrReplaceFormElement(previousId, element.id, $container, $element, $label, element.valid, display_group);
if(element.helpLink) { if (element.helpLink) {
$obj.after('<div class="input-group-append"><a class="btn btn-outline-primary btn-circle" onclick="modalopen(\'' + element.helpLink + '\');return false;" target="_blank" href="' + element.helpLink + '">?</a></div>'); $obj.after('<div class="input-group-append"><a class="btn btn-outline-primary btn-circle" onclick="modalopen(\'' + element.helpLink + '\');return false;" target="_blank" href="' + element.helpLink + '">?</a></div>');
} }
if(element.help) { if (element.help) {
if(element.helpTitle === undefined) { if (element.helpTitle === undefined) {
element.helpTitle = ''; element.helpTitle = '';
} }
$obj.after('<div class="input-group-append"><button class="btn btn-outline-primary btn-circle" type="button" data-container="body" data-toggle="popover" data-placement="right" title="' + element.helpTitle + '" data-content="' + element.help + '">?</button></div>'); $obj.after('<div class="input-group-append"><button class="btn btn-outline-primary btn-circle" type="button" data-container="body" data-toggle="popover" data-placement="right" title="' + element.helpTitle + '" data-content="' + element.help + '">?</button></div>');
@ -472,33 +473,34 @@ function removeElement(element) {
} }
function addOrReplaceFormElement(previousId, id, $container, $element, $label, valid, display_group) { function addOrReplaceFormElement(previousId, id, $container, $element, $label, valid, display_group) {
if($('#container_' + id).length > 0) { if ($('#container_' + id).length > 0) {
if(!valid) { if (!valid) {
$('#container_' + id).remove(); $('#container_' + id).remove();
}else{ } else {
$('#container_' + id).html(''); $('#container_' + id).html('');
if($label !== null) { if ($label !== null) {
$label.appendTo($('#container_' + id)); $label.appendTo($('#container_' + id));
} }
$element.appendTo($('#container_' + id)); $element.appendTo($('#container_' + id));
} }
}else{ } else {
if(previousId !== null && $('#container_' + previousId).length > 0) { if (previousId !== null && $('#container_' + previousId).length > 0) {
$container.insertAfter($('#container_' + previousId)); $container.insertAfter($('#container_' + previousId));
}else{ } else {
if(display_group != "") { if (display_group != "") {
$('#group-' + display_group).append($container); $('#group-' + display_group).append($container);
}else{ } else {
$('#CALCFORM').append($container); $('#CALCFORM').append($container);
} }
} }
if($label !== null) { if ($label !== null) {
$label.appendTo($container); $label.appendTo($container);
} }
$element.appendTo($container); $element.appendTo($container);
} }
} }
function modalopen(href) { function modalopen(href) {
$('.help-modal').load(href,function(){ $('.help-modal').load(href, function() {
$('#myModal').modal({show:true});}); $('#myModal').modal({ show: true });
});
} }