diff --git a/src/new/config/packages/framework.php b/src/new/config/packages/framework.php index 4b9ac4d19..8dcb64462 100755 --- a/src/new/config/packages/framework.php +++ b/src/new/config/packages/framework.php @@ -5,14 +5,15 @@ declare(strict_types=1); use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->extension('framework', ['secret' => '%env(APP_SECRET)%', - 'csrf_protection' => true, - 'session' => [ - 'handler_id' => null, - 'cookie_secure' => false, - 'cookie_httponly' => true, - 'cookie_samesite' => 'lax', - ], - 'php_errors' => ['log' => true] + $containerConfigurator->extension('framework', [ + 'secret' => '%env(APP_SECRET)%', + 'csrf_protection' => true, + 'session' => [ + 'handler_id' => null, + 'cookie_secure' => false, + 'cookie_httponly' => true, + 'cookie_samesite' => 'lax', + ], + 'php_errors' => ['log' => true], ]); }; diff --git a/src/new/src/PSC/Shop/MediaBundle/Api/All.php b/src/new/src/PSC/Shop/MediaBundle/Api/All.php new file mode 100644 index 000000000..74f480655 --- /dev/null +++ b/src/new/src/PSC/Shop/MediaBundle/Api/All.php @@ -0,0 +1,79 @@ +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()); + $this->getSubFolder($f); + $output->data[] = $f; + } + return $this->json($output); + } + + private function getSubFolder(PSCFolder $f): void + { + $folders = $this->dm + ->getRepository(Folder::class) + ->createQueryBuilder('folder') + ->field('parent_id') + ->equals(new ObjectId($f->getUuid())) + ->sort('title', 'ASC') + ->getQuery() + ->execute(); + + $tmp = []; + + foreach ($folders as $folder) { + $s = new PSCFolder(); + $s->setTitle($folder->getTitle()); + $s->setUuid($folder->getId()); + $tmp[] = $s; + } + + $f->setSubFolders($tmp); + } +} diff --git a/src/new/src/PSC/Shop/MediaBundle/Api/AllFolderPage.php b/src/new/src/PSC/Shop/MediaBundle/Api/AllFolderPage.php new file mode 100644 index 000000000..6e4377e61 --- /dev/null +++ b/src/new/src/PSC/Shop/MediaBundle/Api/AllFolderPage.php @@ -0,0 +1,58 @@ +dm + ->getRepository(Media::class) + ->createQueryBuilder('media') + ->field('folder.$id') + ->equals(new ObjectId($uuid)) + ->sort('title', 'ASC'); + $pagination = $this->paginator->paginate($query = $qb->getQuery(), $page, $max); + + $output = new Folder(); + $output->count = $pagination->getTotalItemCount(); + $output->currentPage = $pagination->getCurrentPageNumber(); + $output->lastPage = ceil($output->count / $max); + foreach ($pagination->getItems() as $media) { + $f = new PSCMedia(); + $f->setTitle($media->getTitle()); + $f->setUrl($media->getUrl()); + $output->data[] = $f; + } + return $this->json($output); + } +} diff --git a/src/new/src/PSC/Shop/MediaBundle/Api/Folder/Add.php b/src/new/src/PSC/Shop/MediaBundle/Api/Folder/Add.php index 8fc50bd82..9afeae221 100644 --- a/src/new/src/PSC/Shop/MediaBundle/Api/Folder/Add.php +++ b/src/new/src/PSC/Shop/MediaBundle/Api/Folder/Add.php @@ -35,6 +35,9 @@ class Add extends AbstractController $cat = new PSCFolder(); $cat->setTitle($data->title); $cat->setIcon('fa-file'); + if ($data->parentUuid) { + $cat->setParentId($data->parentUuid); + } $this->dm->persist($cat); $this->dm->flush(); diff --git a/src/new/src/PSC/Shop/MediaBundle/Api/Folder/All.php b/src/new/src/PSC/Shop/MediaBundle/Api/Folder/All.php index f904122ad..d9593fea2 100644 --- a/src/new/src/PSC/Shop/MediaBundle/Api/Folder/All.php +++ b/src/new/src/PSC/Shop/MediaBundle/Api/Folder/All.php @@ -3,6 +3,7 @@ namespace PSC\Shop\MediaBundle\Api\Folder; use Doctrine\ODM\MongoDB\DocumentManager; +use MongoDB\BSON\ObjectId; use Nelmio\ApiDocBundle\Attribute\Model; use Nelmio\ApiDocBundle\Attribute\Security; use OpenApi\Attributes\JsonContent; @@ -47,9 +48,33 @@ class All extends AbstractController $f = new PSCFolder(); $f->setTitle($folder->getTitle()); $f->setUuid($folder->getId()); + $this->getSubFolder($f); $output->data[] = $f; } - return $this->json($output); } + + private function getSubFolder(PSCFolder $f): void + { + $folders = $this->dm + ->getRepository(Folder::class) + ->createQueryBuilder('folder') + ->field('parent_id') + ->equals(new ObjectId($f->getUuid())) + ->sort('title', 'ASC') + ->getQuery() + ->execute(); + + $tmp = []; + + foreach ($folders as $folder) { + $s = new PSCFolder(); + $s->setTitle($folder->getTitle()); + $s->setUuid($folder->getId()); + $this->getSubFolder($s); + $tmp[] = $s; + } + + $f->setSubFolders($tmp); + } } diff --git a/src/new/src/PSC/Shop/MediaBundle/Api/Upload.php b/src/new/src/PSC/Shop/MediaBundle/Api/Upload.php index b0444adfc..a94e66af2 100644 --- a/src/new/src/PSC/Shop/MediaBundle/Api/Upload.php +++ b/src/new/src/PSC/Shop/MediaBundle/Api/Upload.php @@ -3,66 +3,70 @@ namespace PSC\Shop\MediaBundle\Api; use Doctrine\ODM\MongoDB\DocumentManager; -use Nelmio\ApiDocBundle\Annotation\Model; -use Nelmio\ApiDocBundle\Annotation\Security; -use OpenApi\Annotations as OA; +use Nelmio\ApiDocBundle\Attribute\Security; +use OpenApi\Attributes\MediaType; +use OpenApi\Attributes\Property; +use OpenApi\Attributes\RequestBody; +use OpenApi\Attributes\Response; +use OpenApi\Attributes\Schema; +use OpenApi\Attributes\Tag; use PSC\Shop\MediaBundle\Document\Media; use PSC\Shop\MediaBundle\Helper\MediaManager; use PSC\Shop\MediaBundle\Model\Media as MediaModel; use PSC\System\SettingsBundle\Service\Shop; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsGranted; class Upload extends AbstractController { - /** - * create media - * - * @OA\Response( - * response=200, - * description="media", - * @OA\JsonContent(ref=@Model(type=\PSC\Shop\MediaBundle\Model\Media::class)) - * ) - * @OA\RequestBody( - * description="This is a request body", - * @OA\MediaType( - * mediaType="multipart/form-data", - * @OA\Schema( - * @OA\Property( - * description="folder of file", - * property="folder", - * type="string" - * ), - * @OA\Property( - * description="Binary content of file", - * property="file", - * type="string", - * format="binary", - * ), - * required={"file", "folder"} - * ) - * ) - * ) - * @OA\Tag(name="Media") - * @IsGranted("ROLE_USER") - * @Security(name="ApiKeyAuth") - * @Security(name="Bearer") - */ + #[RequestBody(description: 'file and folder', content: [ + new MediaType('multipart/form-data', new Schema( + properties: [new Property( + property: 'file', + format: 'binary', + type: 'file', + description: 'media file', + ), new Property( + property: 'folder', + description: 'folder', + )], + required: ['file', 'folder'], + )), + ])] + #[Response(response: 200, description: 'add upload to folder', ref: MediaModel::class)] + #[Response(response: 400, description: 'bad request no folder or folder not exists')] #[Route(path: '/create', methods: ['POST'])] + #[Tag(name: 'Media')] + #[IsGranted('ROLE_USER')] + #[Security(name: 'Bearer')] + #[Security(name: 'ApiKeyAuth')] public function create( MediaManager $mediaManager, Shop $shopService, DocumentManager $documentManager, Request $req, ): JsonResponse { + if (!$req->get('folder', false)) { + return $this->json( + data: ['error' => 'folder not provided'], + status: JsonResponse::HTTP_BAD_REQUEST, + ); + } $selectedShop = $shopService->getShopByDomain(); $selectedFolder = $documentManager ->getRepository('PSC\Shop\MediaBundle\Document\Folder') ->findOneBy(['id' => $req->get('folder')]); + + if (!$selectedFolder) { + return $this->json( + data: ['error' => 'folder not found'], + status: JsonResponse::HTTP_BAD_REQUEST, + ); + } $handler = $mediaManager->getHandlerForType('pdf'); $media = new Media(); $helper = $handler->getFormHelper($media); diff --git a/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/All.php b/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/All.php index b0bc8c3b3..e6379616c 100644 --- a/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/All.php +++ b/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/All.php @@ -6,9 +6,11 @@ use Nelmio\ApiDocBundle\Attribute\Model; use OpenApi\Attributes\Items; use OpenApi\Attributes\Property; use PSC\Shop\MediaBundle\Model\Folder; +use Symfony\Component\Serializer\Attribute\MaxDepth; final class All { #[Property(type: 'array', items: new Items(ref: new Model(type: Folder::class)))] + #[MaxDepth(4)] public array $data; } diff --git a/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/Input.php b/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/Input.php index d00fe06b7..c67c8e438 100644 --- a/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/Input.php +++ b/src/new/src/PSC/Shop/MediaBundle/Dto/Folder/Input.php @@ -8,4 +8,12 @@ final class Input { #[Property(type: 'string')] public string $title; + + #[Property(type: 'string')] + public null|string $parentUuid; + + public function __construct() + { + $this->parentUuid = null; + } } diff --git a/src/new/src/PSC/Shop/MediaBundle/Dto/Media/Folder.php b/src/new/src/PSC/Shop/MediaBundle/Dto/Media/Folder.php new file mode 100644 index 000000000..2d924474c --- /dev/null +++ b/src/new/src/PSC/Shop/MediaBundle/Dto/Media/Folder.php @@ -0,0 +1,23 @@ +faker = Factory::create(locale: 'de_DE'); + + $this->client = static::createClient(); + + $userRepository = static::getContainer()->get(ContactRepository::class); + + $testUser = $userRepository->loadUserByUsername('admin@shop.de'); + + $this->client->loginUser($testUser, 'api'); + + $name = $this->faker->slug(); + $this->client->jsonRequest( + 'POST', + '/api/media/folder/add', + [ + 'title' => $name, + ], + [], + ); + + $data = json_decode($this->client->getResponse()->getContent(), true); + + $subName = $this->faker->slug(); + $this->client->jsonRequest( + 'POST', + '/api/media/folder/add', + [ + 'title' => $subName, + 'parentUuid' => $data['uuid'], + ], + [], + ); + + $subData = json_decode($this->client->getResponse()->getContent(), true); + + $this->subFolderUuid = $subData['uuid']; + } + + public function testUploadMediaToSubFolder(): void + { + $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny.jpg', 'kenney.jpg'); + $this->client->request( + 'POST', + '/api/media/create', + ['folder' => $this->subFolderUuid], + [ + 'file' => $uploadedFile, + ], + ); + + $media = json_decode($this->client->getResponse()->getContent(), true); + self::assertSame('kenney.jpg', $media['title']); + + $this->client->request('GET', sprintf('/api/media/folder/%s/page/%s', $this->subFolderUuid, 1), [], []); + + $this->assertResponseIsSuccessful(); + + $media = json_decode($this->client->getResponse()->getContent(), true); + self::assertCount(1, $media['data']); + } +} diff --git a/src/new/tests/PSC/Shop/Media/Api/FolderTest.php b/src/new/tests/PSC/Shop/Media/Api/FolderTest.php index 9b1e325a1..2851b0f9f 100644 --- a/src/new/tests/PSC/Shop/Media/Api/FolderTest.php +++ b/src/new/tests/PSC/Shop/Media/Api/FolderTest.php @@ -45,6 +45,57 @@ class FolderTest extends WebTestCase self::assertSame($name, $data['title']); } + public function testCreateFolderWithSubFolders(): void + { + $client = static::createClient(); + + $userRepository = static::getContainer()->get(ContactRepository::class); + + $testUser = $userRepository->loadUserByUsername('admin@shop.de'); + + $client->loginUser($testUser, 'api'); + + $name = $this->faker->slug(); + $client->jsonRequest( + 'POST', + '/api/media/folder/add', + [ + 'title' => $name, + ], + [], + ); + + $this->assertResponseIsSuccessful(); + $data = json_decode($client->getResponse()->getContent(), true); + self::assertSame($name, $data['title']); + + $subName = $this->faker->slug(); + $client->jsonRequest( + 'POST', + '/api/media/folder/add', + [ + 'title' => $subName, + 'parentUuid' => $data['uuid'], + ], + [], + ); + + $this->assertResponseIsSuccessful(); + $subData = json_decode($client->getResponse()->getContent(), true); + self::assertSame($subName, $subData['title']); + + $client->jsonRequest('GET', '/api/media/folder/all', [], []); + self::assertCount(1, json_decode($client->getResponse()->getContent(), true)['data']); + self::assertCount(1, json_decode($client->getResponse()->getContent(), true)['data'][0]['subFolders']); + + self::assertSame($name, json_decode($client->getResponse()->getContent(), true)['data'][0]['title']); + + self::assertSame( + $subName, + json_decode($client->getResponse()->getContent(), true)['data'][0]['subFolders'][0]['title'], + ); + } + public function testCreateAndGetFolders(): void { $client = static::createClient(); diff --git a/src/new/tests/PSC/Shop/Media/Api/UploadTest.php b/src/new/tests/PSC/Shop/Media/Api/UploadTest.php index 256997805..4a945aee0 100644 --- a/src/new/tests/PSC/Shop/Media/Api/UploadTest.php +++ b/src/new/tests/PSC/Shop/Media/Api/UploadTest.php @@ -22,11 +22,24 @@ class UploadTest extends WebTestCase $client->loginUser($testUser, 'api'); + $client->jsonRequest( + 'POST', + '/api/media/folder/add', + [ + 'title' => 'testFolder', + ], + [], + ); + + $folder = json_decode($client->getResponse()->getContent(), true); + $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny.jpg', 'kenney.jpg'); $client->request( 'POST', '/api/media/create', - [], + [ + 'folder' => $folder['uuid'], + ], [ 'file' => $uploadedFile, ], diff --git a/src/new/tests/PSC/Shop/Media/Api/UploadVariantTest.php b/src/new/tests/PSC/Shop/Media/Api/UploadVariantTest.php index 30d7cf55b..0fd0217d8 100644 --- a/src/new/tests/PSC/Shop/Media/Api/UploadVariantTest.php +++ b/src/new/tests/PSC/Shop/Media/Api/UploadVariantTest.php @@ -22,61 +22,80 @@ class UploadVariantTest extends WebTestCase $client->loginUser($testUser, 'api'); - $uploadedFile = new UploadedFile( - __DIR__.'/../../../../kenny.jpg', - 'kenney.jpg' + $client->jsonRequest( + 'POST', + '/api/media/folder/add', + [ + 'title' => 'testFolder', + ], + [], + ); + + $folder = json_decode($client->getResponse()->getContent(), true); + + $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny.jpg', 'kenney.jpg'); + $client->request( + 'POST', + '/api/media/create', + ['folder' => $folder['uuid']], + [ + 'file' => $uploadedFile, + ], ); - $client->request('POST', '/api/media/create', [], [ - 'file' => $uploadedFile - ]); $media = json_decode($client->getResponse()->getContent(), true); self::assertSame('kenney.jpg', $media['title']); self::assertNotEmpty($media['url']); - $uploadedFile = new UploadedFile( - __DIR__.'/../../../../kenny_crop.jpg', - 'kenny_crop.jpg' + $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny_crop.jpg', 'kenny_crop.jpg'); + $client->request( + 'POST', + '/api/media/variant/create', + [ + 'uuid' => $media['uuid'], + 'settings' => '1/2', + ], + [ + 'file' => $uploadedFile, + ], ); - $client->request('POST', '/api/media/variant/create', [ - 'uuid' => $media['uuid'], - 'settings' => '1/2' - ], [ - 'file' => $uploadedFile - ]); $mediaVariant = json_decode($client->getResponse()->getContent(), true); self::assertNotEmpty($mediaVariant['variants'][0]['url']); self::assertSame('kenny-crop.jpg', $mediaVariant['variants'][0]['title']); self::assertCount(1, $mediaVariant['variants']); - $uploadedFile = new UploadedFile( - __DIR__.'/../../../../kenny_crop1.jpg', - 'kenny_crop1.jpg' + $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny_crop1.jpg', 'kenny_crop1.jpg'); + $client->request( + 'POST', + '/api/media/variant/create', + [ + 'uuid' => $media['uuid'], + 'settings' => '1/4', + ], + [ + 'file' => $uploadedFile, + ], ); - $client->request('POST', '/api/media/variant/create', [ - 'uuid' => $media['uuid'], - 'settings' => '1/4' - ], [ - 'file' => $uploadedFile - ]); $mediaVariant = json_decode($client->getResponse()->getContent(), true); self::assertNotEmpty($mediaVariant['variants'][1]['url']); self::assertSame('kenny-crop1.jpg', $mediaVariant['variants'][1]['title']); self::assertCount(2, $mediaVariant['variants']); - $uploadedFile = new UploadedFile( - __DIR__.'/../../../../kenny_crop_better.jpg', - 'kenny_crop_better.jpg' + $uploadedFile = new UploadedFile(__DIR__ . '/../../../../kenny_crop_better.jpg', 'kenny_crop_better.jpg'); + $client->request( + 'POST', + '/api/media/variant/create', + [ + 'uuid' => $media['uuid'], + 'settings' => '1/2', + ], + [ + 'file' => $uploadedFile, + ], ); - $client->request('POST', '/api/media/variant/create', [ - 'uuid' => $media['uuid'], - 'settings' => '1/2' - ], [ - 'file' => $uploadedFile - ]); $mediaVariant = json_decode($client->getResponse()->getContent(), true); self::assertNotEmpty($mediaVariant['variants'][1]['url']); diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/bun.lock b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/bun.lock index 3d465abde..7bc854e56 100644 --- a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/bun.lock +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/bun.lock @@ -8,13 +8,13 @@ "@codemirror/lang-php": "^6.0.2", "@codemirror/lang-xml": "^6.1.0", "@tailwindcss/vite": "^4.1.10", - "@vueuse/core": "^13.5.0", + "@vueuse/core": "^13.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "ky": "^1.8.1", "lucide-vue-next": "^0.514.0", "pinia": "^3.0.3", - "reka-ui": "^2.3.2", + "reka-ui": "^2.4.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.10", "ts-debounce": "^4.0.0", @@ -364,11 +364,11 @@ "@vue/tsconfig": ["@vue/tsconfig@0.7.0", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="], - "@vueuse/core": ["@vueuse/core@13.5.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.5.0", "@vueuse/shared": "13.5.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g=="], + "@vueuse/core": ["@vueuse/core@13.6.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.6.0", "@vueuse/shared": "13.6.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A=="], - "@vueuse/metadata": ["@vueuse/metadata@13.5.0", "", {}, "sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw=="], + "@vueuse/metadata": ["@vueuse/metadata@13.6.0", "", {}, "sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ=="], - "@vueuse/shared": ["@vueuse/shared@13.5.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g=="], + "@vueuse/shared": ["@vueuse/shared@13.6.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg=="], "alien-signals": ["alien-signals@1.0.13", "", {}, "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg=="], @@ -560,7 +560,7 @@ "pretty-ms": ["pretty-ms@9.2.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg=="], - "reka-ui": ["reka-ui@2.3.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@floating-ui/vue": "^1.1.6", "@internationalized/date": "^3.5.0", "@internationalized/number": "^3.5.0", "@tanstack/vue-virtual": "^3.12.0", "@vueuse/core": "^12.5.0", "@vueuse/shared": "^12.5.0", "aria-hidden": "^1.2.4", "defu": "^6.1.4", "ohash": "^2.0.11" }, "peerDependencies": { "vue": ">= 3.2.0" } }, "sha512-lCysSCILH2uqShEnt93/qzlXnB7ySvK7scR0Q5C+a2iXwFVzHhvZQsMaSnbQYueoCihx6yyUZTYECepnmKrbRA=="], + "reka-ui": ["reka-ui@2.4.1", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@floating-ui/vue": "^1.1.6", "@internationalized/date": "^3.5.0", "@internationalized/number": "^3.5.0", "@tanstack/vue-virtual": "^3.12.0", "@vueuse/core": "^12.5.0", "@vueuse/shared": "^12.5.0", "aria-hidden": "^1.2.4", "defu": "^6.1.4", "ohash": "^2.0.11" }, "peerDependencies": { "vue": ">= 3.2.0" } }, "sha512-NB7DrCsODN8MH02BWtgiExygfFcuuZ5/PTn6fMgjppmFHqePvNhmSn1LEuF35nel6PFbA4v+gdj0IoGN1yZ+vw=="], "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/package.json b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/package.json index e39d77e3b..fc2745b93 100644 --- a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/package.json +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/package.json @@ -11,16 +11,16 @@ }, "dependencies": { "@codemirror/lang-json": "^6.0.2", - "@codemirror/lang-xml": "^6.1.0", "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-xml": "^6.1.0", "@tailwindcss/vite": "^4.1.10", - "@vueuse/core": "^13.5.0", + "@vueuse/core": "^13.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "ky": "^1.8.1", "lucide-vue-next": "^0.514.0", "pinia": "^3.0.3", - "reka-ui": "^2.3.2", + "reka-ui": "^2.4.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.10", "ts-debounce": "^4.0.0", diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/media/FolderTree.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/media/FolderTree.vue new file mode 100644 index 000000000..b98f798cc --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/media/FolderTree.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/media/MediaBrowser.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/media/MediaBrowser.vue new file mode 100644 index 000000000..7e342d48d --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/media/MediaBrowser.vue @@ -0,0 +1,110 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/properties/MediaElement.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/properties/MediaElement.vue index deeaa2170..1e51480cd 100644 --- a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/properties/MediaElement.vue +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/app/properties/MediaElement.vue @@ -3,8 +3,10 @@ import MediaElement from '../../../model/MediaElement'; import { computed } from 'vue'; import { Input } from '../../../components/ui/input' import { Button } from '../../../components/ui/button' -import { fetchMediaDirectories, uploadFile } from '../../../lib/api'; +import { fetchMediaFolders, uploadFile } from '../../../lib/api'; import { onMounted, ref } from 'vue'; +import { Dialog, DialogContent, DialogTrigger } from '../../../components/ui/dialog' +import MediaBrowser from '../media/MediaBrowser.vue'; const props = defineProps({ modelValue: MediaElement @@ -52,7 +54,7 @@ const onFileChange = (event: Event) => { onMounted(async () => { try { - let response: any = await fetchMediaDirectories() + let response: any = await fetchMediaFolders() directories.value = response.data if (response.data.length > 0) { selectedDirectory.value = directories.value[0].uuid; @@ -87,7 +89,14 @@ const handleFile = async (file: File) => {
- + + + + + + + +
+import type { HTMLAttributes } from 'vue' +import { reactiveOmit } from '@vueuse/core' +import { PaginationRoot, type PaginationRootEmits, type PaginationRootProps, useForwardPropsEmits } from 'reka-ui' +import { cn } from '@/lib/utils' + +const props = defineProps() +const emits = defineEmits() + +const delegatedProps = reactiveOmit(props, 'class') +const forwarded = useForwardPropsEmits(delegatedProps, emits) + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationContent.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationContent.vue new file mode 100644 index 000000000..062e202fd --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationContent.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationEllipsis.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationEllipsis.vue new file mode 100644 index 000000000..5db8a335f --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationEllipsis.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationFirst.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationFirst.vue new file mode 100644 index 000000000..c3fc193bf --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationFirst.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationItem.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationItem.vue new file mode 100644 index 000000000..d272ac720 --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationItem.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationLast.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationLast.vue new file mode 100644 index 000000000..699822fa3 --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationLast.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationNext.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationNext.vue new file mode 100644 index 000000000..7c4ce503b --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationNext.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationPrevious.vue b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationPrevious.vue new file mode 100644 index 000000000..df2e90a78 --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/PaginationPrevious.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/index.ts b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/index.ts new file mode 100644 index 000000000..8eba5fee6 --- /dev/null +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/components/ui/pagination/index.ts @@ -0,0 +1,8 @@ +export { default as Pagination } from './Pagination.vue' +export { default as PaginationContent } from './PaginationContent.vue' +export { default as PaginationEllipsis } from './PaginationEllipsis.vue' +export { default as PaginationFirst } from './PaginationFirst.vue' +export { default as PaginationItem } from './PaginationItem.vue' +export { default as PaginationLast } from './PaginationLast.vue' +export { default as PaginationNext } from './PaginationNext.vue' +export { default as PaginationPrevious } from './PaginationPrevious.vue' diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/lib/api.ts b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/lib/api.ts index ecd19f78e..1f30567cc 100644 --- a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/lib/api.ts +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/src/lib/api.ts @@ -124,7 +124,7 @@ export const uploadFile = async (file: File, folder: string, onProgress: (progre } }; -export const fetchMediaDirectories = async () => { +export const fetchMediaFolders = async () => { try { const response = await api.get('api/media/folder/all'); return await response.json(); @@ -133,4 +133,15 @@ export const fetchMediaDirectories = async () => { throw error; } }; + +export const fetchMediaByFolder = async (folderId: string, page: number = 1) => { + try { + const response = await api.get(`api/media/folder/${folderId}/page/${page}/12`); + return await response.json(); + } catch (error) { + console.error(`Error fetching media for folder ${folderId}:`, error); + throw error; + } +}; + export default api; diff --git a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/vite.config.ts b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/vite.config.ts index ec76ca0ef..05da45019 100644 --- a/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/vite.config.ts +++ b/src/new/var/plugins/Custom/PSC/FormBuilder/FormBuilderTS/vite.config.ts @@ -33,7 +33,7 @@ export default defineConfig({ changeOrigin: true, configure: (proxy) => { proxy.on('proxyReq', (proxyReq) => { - proxyReq.setHeader('Authorization', 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3NTM4ODEwMjUsImV4cCI6MTc1Mzg4NDYyNSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfU0hPUF9PUEVSQVRPUiIsIlJPTEVfVVNFUiIsIlJPTEVfVVNFUiIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9FZGl0IiwiUk9MRV9QU0NfQ29sbGVjdF9Db250YWN0X0FkZCIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9EZWxldGUiLCJST0xFX1BTQ19Db2xsZWN0X0NvbnRhY3RfTG9jayIsIlJPTEVfUFNDX1IyX1NlbmRjbG91ZF9TaG93Il0sInVpZCI6MX0.T0JzZ8qoZOqOOGtHXtuGvY5xAwwWGSAHh9MXgGJlyngjsmA_RWUnL8wVW3Ah827sJQNL2W1JPEz_f9CuCvfCSVjoLml9T_n5N5xtB98wVdcksgh3PvtFrYs-NVUat9QlwJ54F0cUXIkyuimEc2op0Y2sSC9Pyw6d9m8WtYrPX657uXZ3U8KcX8FIqCMWpjzIsbLK2QemT-fgkJpVnSdvo8nFvComZ1yDPvj5Zl_m0NEF3CybYdxOOwz42egI297BM_qGp6_cZLeSrl2EwphzeFqPo8-q9wmuHj33HZozlwjlu_Uvp4vq0Jr98-WZkIUwK7706E1t_TdSc46nONyxVA'); + proxyReq.setHeader('Authorization', 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3NTM5NTU4MzMsImV4cCI6MTc1Mzk1OTQzMywicm9sZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfU0hPUF9PUEVSQVRPUiIsIlJPTEVfVVNFUiIsIlJPTEVfVVNFUiIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9FZGl0IiwiUk9MRV9QU0NfQ29sbGVjdF9Db250YWN0X0FkZCIsIlJPTEVfUFNDX0NvbGxlY3RfQ29udGFjdF9EZWxldGUiLCJST0xFX1BTQ19Db2xsZWN0X0NvbnRhY3RfTG9jayIsIlJPTEVfUFNDX1IyX1NlbmRjbG91ZF9TaG93Il0sInVpZCI6MX0.rUlkpFBa-RKQAxSa6e0UmggiqPlDj-Mz4I56T-kTOT5j5veI3JFYHOvptbeEjITPdmHM_Dm86CpEuIkezQC1UBsuzGkSYLQRy8Cb_YnNEEbaoCsg28q60Wu_dfBbDEFRjXnbEVSrwbfawRMq4FUTuky7r8qstORRhL1bPtfL3cN_lPEruffSreuvUReM7inkjOHU4utGuVhMm1D3oK1p398YmZACmQfiYLYOM6c07Isop5IZ0Mfq0LZ_BEVptQi7Thb4nAHiEUZfyBqhLrrWxmUFcpF2Mp7-_dZZeiA9w2ZYZBA-sGMAF0EwCDU1_WK-o0BDZDk-qKMj4zrFHe8TEg'); }); }, },