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 @@
+
+
+
+ Media Browser
+
+