updates
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Http\Requests\MediaRequest;
|
|||||||
use App\Models\Media;
|
use App\Models\Media;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Laravel\Sanctum\PersonalAccessToken;
|
use Laravel\Sanctum\PersonalAccessToken;
|
||||||
|
|
||||||
class MediaController extends ApiController
|
class MediaController extends ApiController
|
||||||
@@ -119,19 +120,36 @@ class MediaController extends ApiController
|
|||||||
if (MediaConductor::updatable($medium) === true) {
|
if (MediaConductor::updatable($medium) === true) {
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
if ($file !== null) {
|
if ($file !== null) {
|
||||||
if ($file->getSize() > Media::maxUploadSize()) {
|
if ($file->isValid() !== true) {
|
||||||
return $this->respondTooLarge();
|
switch ($file->getError()) {
|
||||||
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
|
return $this->respondTooLarge();
|
||||||
|
case UPLOAD_ERR_PARTIAL:
|
||||||
|
return $this->respondWithErrors(['file' => 'The file upload was interrupted.']);
|
||||||
|
default:
|
||||||
|
return $this->respondWithErrors(['file' => 'An error occurred uploading the file to the server.']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($medium->updateFile($file) === false) {
|
if ($file->getSize() > Media::getMaxUploadSize()) {
|
||||||
|
return $this->respondTooLarge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$medium->update($request->all());
|
||||||
|
|
||||||
|
if ($file !== null) {
|
||||||
|
try {
|
||||||
|
$medium->updateWithUploadedFile($file);
|
||||||
|
} catch (\Exception $e) {
|
||||||
return $this->respondWithErrors(
|
return $this->respondWithErrors(
|
||||||
['file' => 'The file could not be stored on the server'],
|
['file' => $e->getMessage()],
|
||||||
HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR
|
HttpResponseCodes::HTTP_INTERNAL_SERVER_ERROR
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}//end if
|
}
|
||||||
|
|
||||||
$medium->update($request->all());
|
|
||||||
return $this->respondAsResource(MediaConductor::model($request, $medium));
|
return $this->respondAsResource(MediaConductor::model($request, $medium));
|
||||||
}//end if
|
}//end if
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,12 @@ class StoreUploadedFileJob implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
protected $replaceExisting;
|
protected $replaceExisting;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
* @param Media $media The media model.
|
* @param Media $media The media model.
|
||||||
* @param string $filePath The uploaded file.
|
* @param string $filePath The uploaded file.
|
||||||
* @param boolean $replaceExisting Replace existing files.
|
* @param boolean $replaceExisting Replace existing files.
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@@ -74,31 +75,30 @@ class StoreUploadedFileJob implements ShouldQueue
|
|||||||
$this->media->save();
|
$this->media->save();
|
||||||
|
|
||||||
if (strlen($this->uploadedFilePath) > 0) {
|
if (strlen($this->uploadedFilePath) > 0) {
|
||||||
if (Storage::disk($storageDisk)->exists($fileName) == false || $this->replaceExisting == true) {
|
if (Storage::disk($storageDisk)->exists($fileName) === false || $this->replaceExisting === true) {
|
||||||
Storage::disk($storageDisk)->putFileAs('/', new SplFileInfo($this->uploadedFilePath), $fileName);
|
Storage::disk($storageDisk)->putFileAs('/', new SplFileInfo($this->uploadedFilePath), $fileName);
|
||||||
Log::info("uploading file {$storageDisk} / {$fileName} / {$this->uploadedFilePath}");
|
Log::info("uploading file {$storageDisk} / {$fileName} / {$this->uploadedFilePath}");
|
||||||
} else {
|
} else {
|
||||||
Log::info("file {$fileName} already exists in {$storageDisk} / {$this->uploadedFilePath}. Not replacing file and using local {$fileName} for variants.");
|
Log::info("file {$fileName} already exists in {$storageDisk} / {$this->uploadedFilePath}. Not replacing file and using local {$fileName} for variants.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Storage::disk($storageDisk)->exists($fileName) == true) {
|
if (Storage::disk($storageDisk)->exists($fileName) === true) {
|
||||||
Log::info("file {$fileName} already exists in {$storageDisk} / {$this->uploadedFilePath}. No local {$fileName} for variants, downloading from CDN.");
|
Log::info("file {$fileName} already exists in {$storageDisk} / {$this->uploadedFilePath}. No local {$fileName} for variants, downloading from CDN.");
|
||||||
$readStream = Storage::disk($storageDisk)->readStream($fileName);
|
$readStream = Storage::disk($storageDisk)->readStream($fileName);
|
||||||
$tempFilePath = tempnam(sys_get_temp_dir(), 'download-');
|
$tempFilePath = tempnam(sys_get_temp_dir(), 'download-');
|
||||||
$writeStream = fopen($tempFilePath, 'w');
|
$writeStream = fopen($tempFilePath, 'w');
|
||||||
while (!feof($readStream)) {
|
while (feof($readStream) !== true) {
|
||||||
fwrite($writeStream, fread($readStream, 8192));
|
fwrite($writeStream, fread($readStream, 8192));
|
||||||
}
|
}
|
||||||
fclose($readStream);
|
fclose($readStream);
|
||||||
fclose($writeStream);
|
fclose($writeStream);
|
||||||
$this->uploadedFilePath = $tempFilePath;
|
$this->uploadedFilePath = $tempFilePath;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$errorStr = "cannot upload file {$storageDisk} / {$fileName} / {$this->uploadedFilePath} as temp file is empty";
|
$errorStr = "cannot upload file {$storageDisk} / {$fileName} / {$this->uploadedFilePath} as temp file is empty";
|
||||||
Log::info($errorStr);
|
Log::info($errorStr);
|
||||||
throw new \Exception($errorStr);
|
throw new \Exception($errorStr);
|
||||||
}
|
}
|
||||||
}
|
}//end if
|
||||||
|
|
||||||
if (strpos($this->media->mime_type, 'image/') === 0) {
|
if (strpos($this->media->mime_type, 'image/') === 0) {
|
||||||
$this->media->status = "Optimizing image";
|
$this->media->status = "Optimizing image";
|
||||||
@@ -160,7 +160,7 @@ class StoreUploadedFileJob implements ShouldQueue
|
|||||||
}//end if
|
}//end if
|
||||||
} else {
|
} else {
|
||||||
Log::info("variant {$variantName} already exists for file {$fileName}");
|
Log::info("variant {$variantName} already exists for file {$fileName}");
|
||||||
}
|
}//end if
|
||||||
}//end foreach
|
}//end foreach
|
||||||
|
|
||||||
// Set missing variants to the largest available variant
|
// Set missing variants to the largest available variant
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ class Media extends Model
|
|||||||
*/
|
*/
|
||||||
public function getUrlAttribute()
|
public function getUrlAttribute()
|
||||||
{
|
{
|
||||||
if(isset($this->attributes['name'])) {
|
if (isset($this->attributes['name']) === true) {
|
||||||
$url = config("filesystems.disks.$this->storage.url");
|
$url = config("filesystems.disks.$this->storage.url");
|
||||||
return "$url/$this->name";
|
return "$url/$this->name";
|
||||||
}
|
}
|
||||||
@@ -247,6 +247,28 @@ class Media extends Model
|
|||||||
* @return null|Media The result or null if not successful.
|
* @return null|Media The result or null if not successful.
|
||||||
*/
|
*/
|
||||||
public static function createFromUploadedFile(Request $request, UploadedFile $file)
|
public static function createFromUploadedFile(Request $request, UploadedFile $file)
|
||||||
|
{
|
||||||
|
$request->merge([
|
||||||
|
'title' => $request->get('title', ''),
|
||||||
|
'name' => '',
|
||||||
|
'size' => 0,
|
||||||
|
'mime_type' => '',
|
||||||
|
'status' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mediaItem = $request->user()->media()->create($request->all());
|
||||||
|
$mediaItem->updateWithUploadedFile($file);
|
||||||
|
|
||||||
|
return $mediaItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Media with UploadedFile data.
|
||||||
|
*
|
||||||
|
* @param Illuminate\Http\UploadedFile $file The file.
|
||||||
|
* @return null|Media The media item.
|
||||||
|
*/
|
||||||
|
public function updateWithUploadedFile(UploadedFile $file)
|
||||||
{
|
{
|
||||||
if ($file === null || $file->isValid() !== true) {
|
if ($file === null || $file->isValid() !== true) {
|
||||||
throw new \Exception('The file is invalid.', self::INVALID_FILE_ERROR);
|
throw new \Exception('The file is invalid.', self::INVALID_FILE_ERROR);
|
||||||
@@ -261,34 +283,40 @@ class Media extends Model
|
|||||||
throw new \Exception('The file name already exists in storage.', self::FILE_NAME_EXISTS_ERROR);
|
throw new \Exception('The file name already exists in storage.', self::FILE_NAME_EXISTS_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->merge([
|
// remove file if there is an existing entry in this medium item
|
||||||
'title' => $request->get('title', $name),
|
if (strlen($this->name) > 0 && strlen($this->storage) > 0) {
|
||||||
'name' => $name,
|
Storage::disk($this->storage)->delete($this->name);
|
||||||
'size' => $file->getSize(),
|
foreach ($this->variants as $variantName => $fileName) {
|
||||||
'mime_type' => $file->getMimeType(),
|
Storage::disk($this->storage)->delete($fileName);
|
||||||
'status' => 'Processing media',
|
}
|
||||||
]);
|
|
||||||
|
|
||||||
$mediaItem = $request->user()->media()->create($request->all());
|
$this->name = '';
|
||||||
|
$this->variants = [];
|
||||||
try {
|
|
||||||
$temporaryFilePath = tempnam(sys_get_temp_dir(), 'upload');
|
|
||||||
$temporaryDirectoryPath = dirname($temporaryFilePath);
|
|
||||||
$file->move($temporaryDirectoryPath, basename($temporaryFilePath));
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
throw new \Exception('Could not temporarily store file. ' . $e->getMessage(), self::TEMP_FILE_ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strlen($this->title) === 0) {
|
||||||
|
$this->title = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->name = $name;
|
||||||
|
$this->size = $file->getSize();
|
||||||
|
$this->mime_type = $file->getMimeType();
|
||||||
|
$this->status = 'Processing media';
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
$temporaryFilePath = tempnam(sys_get_temp_dir(), 'upload');
|
||||||
|
copy($file->path(), $temporaryFilePath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StoreUploadedFileJob::dispatch($mediaItem, $temporaryFilePath)->onQueue('media');
|
StoreUploadedFileJob::dispatch($this, $temporaryFilePath)->onQueue('media');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$mediaItem->delete();
|
$this->status = 'Error';
|
||||||
$mediaItem = null;
|
$this->save();
|
||||||
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}//end try
|
}//end try
|
||||||
|
|
||||||
return $mediaItem;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
14
composer.lock
generated
14
composer.lock
generated
@@ -62,16 +62,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.263.14",
|
"version": "3.268.16",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "7a6a43fad8899e3be3c46471fa3802331620e36b"
|
"reference": "b59134c9ca64dcb9de6f7dbbcb9d5a75ed665a98"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7a6a43fad8899e3be3c46471fa3802331620e36b",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b59134c9ca64dcb9de6f7dbbcb9d5a75ed665a98",
|
||||||
"reference": "7a6a43fad8899e3be3c46471fa3802331620e36b",
|
"reference": "b59134c9ca64dcb9de6f7dbbcb9d5a75ed665a98",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -151,9 +151,9 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.263.14"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.268.16"
|
||||||
},
|
},
|
||||||
"time": "2023-04-20T18:21:44+00:00"
|
"time": "2023-04-21T21:37:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -9508,5 +9508,5 @@
|
|||||||
"php": "^8.0.2"
|
"php": "^8.0.2"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.1.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ return [
|
|||||||
'disks' => [
|
'disks' => [
|
||||||
'local' => [
|
'local' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app/uploads'),
|
'root' => storage_path('app'),
|
||||||
'throw' => false,
|
|
||||||
'url' => env('STORAGE_LOCAL_URL'),
|
|
||||||
'public' => true,
|
|
||||||
],
|
],
|
||||||
|
|
||||||
'cdn' => [
|
'cdn' => [
|
||||||
|
|||||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -475,9 +475,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.38.0",
|
"version": "8.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
|
||||||
"integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
|
"integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
@@ -729,9 +729,9 @@
|
|||||||
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
|
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.15.12",
|
"version": "18.15.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz",
|
||||||
"integrity": "sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg=="
|
"integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.3.13",
|
"version": "7.3.13",
|
||||||
@@ -1607,9 +1607,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001480",
|
"version": "1.0.30001481",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz",
|
||||||
"integrity": "sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==",
|
"integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1936,9 +1936,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.368",
|
"version": "1.4.369",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.369.tgz",
|
||||||
"integrity": "sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==",
|
"integrity": "sha512-LfxbHXdA/S+qyoTEA4EbhxGjrxx7WK2h6yb5K2v0UCOufUKX+VZaHbl3svlzZfv9sGseym/g3Ne4DpsgRULmqg==",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
@@ -2032,15 +2032,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.38.0",
|
"version": "8.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
|
||||||
"integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
|
"integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.4.0",
|
"@eslint-community/regexpp": "^4.4.0",
|
||||||
"@eslint/eslintrc": "^2.0.2",
|
"@eslint/eslintrc": "^2.0.2",
|
||||||
"@eslint/js": "8.38.0",
|
"@eslint/js": "8.39.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.8",
|
"@humanwhocodes/config-array": "^0.11.8",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
@@ -2050,7 +2050,7 @@
|
|||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
"doctrine": "^3.0.0",
|
"doctrine": "^3.0.0",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
"eslint-scope": "^7.1.1",
|
"eslint-scope": "^7.2.0",
|
||||||
"eslint-visitor-keys": "^3.4.0",
|
"eslint-visitor-keys": "^3.4.0",
|
||||||
"espree": "^9.5.1",
|
"espree": "^9.5.1",
|
||||||
"esquery": "^1.4.2",
|
"esquery": "^1.4.2",
|
||||||
@@ -3473,9 +3473,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.20.6",
|
"version": "3.20.7",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.6.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.7.tgz",
|
||||||
"integrity": "sha512-2yEB3nQXp/tBQDN0hJScJQheXdvU2wFhh6ld7K/aiZ1vYcak6N/BKjY1QrU6BvO2JWYS8bEs14FRaxXosxy2zw==",
|
"integrity": "sha512-P7E2zezKSLhWnTz46XxjSmInrbOCiul1yf+kJccMxT56vxjHwCbDfoLbiqFgu+WQoo9ij2PkraYaBstgB2prBA==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
public/uploadabiwAz
Normal file
BIN
public/uploadabiwAz
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
@@ -114,7 +114,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flex-row-reverse {
|
.flex-row-reverse {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-column {
|
.flex-column {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
props.size,
|
props.size,
|
||||||
{ 'button-block': block },
|
{ 'button-block': block },
|
||||||
{ 'button-dropdown': dropdown },
|
{ 'button-dropdown': dropdown },
|
||||||
|
{ 'button-loading': loading },
|
||||||
]"
|
]"
|
||||||
ref="buttonRef"
|
ref="buttonRef"
|
||||||
:style="{ minWidth: minWidth }"
|
:style="{ minWidth: minWidth }"
|
||||||
@@ -146,8 +147,10 @@ if (props.form !== undefined) {
|
|||||||
watch(
|
watch(
|
||||||
() => props.form.loading(),
|
() => props.form.loading(),
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
loading.value = newValue;
|
|
||||||
disabled.value = newValue;
|
disabled.value = newValue;
|
||||||
|
if (buttonType === "submit") {
|
||||||
|
loading.value = newValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -265,7 +268,7 @@ const handleClickItem = (item: string) => {
|
|||||||
|
|
||||||
&:disabled,
|
&:disabled,
|
||||||
&.primary:disabled {
|
&.primary:disabled {
|
||||||
background-color: var(--base-color-dark);
|
background-color: var(--base-color-dark) !important;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
58
resources/js/components/SMImage.vue
Normal file
58
resources/js/components/SMImage.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="image">
|
||||||
|
<SMLoading v-if="imgLoaded == false && imgError == false" />
|
||||||
|
<img
|
||||||
|
v-if="imgError == false"
|
||||||
|
:src="src"
|
||||||
|
@load="imgLoaded = true"
|
||||||
|
@error="imgError = true" />
|
||||||
|
<div v-if="imgError == true" class="image-error">
|
||||||
|
<ion-icon name="alert-circle-outline"></ion-icon>
|
||||||
|
<p>Error loading image</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import SMLoading from "./SMLoading.vue";
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgLoaded = ref(false);
|
||||||
|
const imgError = ref(false);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.image {
|
||||||
|
display: flex;
|
||||||
|
flex-basis: 300px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-error {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
ion-icon {
|
||||||
|
font-size: 300%;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="loading-container">
|
<div :class="['loading-background', { overlay: props.overlay }]">
|
||||||
<SMLoadingIcon v-bind="{ large: props.large }" />
|
<div :class="{ 'loading-box': props.overlay }">
|
||||||
|
<SMLoadingIcon v-bind="{ large: props.large }" />
|
||||||
|
<p v-if="props.text" class="loading-text">{{ props.text }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -13,14 +16,54 @@ const props = defineProps({
|
|||||||
default: false,
|
default: false,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.loading-container {
|
.loading-background {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
&.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10000;
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
-webkit-backdrop-filter: blur(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-box {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 48px 48px 16px 48px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: var(--base-shadow);
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -17,17 +17,11 @@
|
|||||||
:data-title="header['text']"
|
:data-title="header['text']"
|
||||||
:key="`item-row-${index}-${header['value']}`">
|
:key="`item-row-${index}-${header['value']}`">
|
||||||
<template v-if="slots[`item-${header['value']}`]">
|
<template v-if="slots[`item-${header['value']}`]">
|
||||||
<slot
|
<slot :name="`item-${header['value']}`" v-bind="item">
|
||||||
:name="`item-${header['value']}`"
|
|
||||||
v-bind="item as any">
|
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{
|
{{ getItemValue(item, header["value"]) }}
|
||||||
header["value"]
|
|
||||||
.split(".")
|
|
||||||
.reduce((item, key) => item[key], item)
|
|
||||||
}}
|
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -57,6 +51,22 @@ const slots = useSlots();
|
|||||||
const handleRowClick = (item) => {
|
const handleRowClick = (item) => {
|
||||||
emits("rowClick", item);
|
emits("rowClick", item);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getItemValue = (data: unknown, key: string): string => {
|
||||||
|
if (typeof data === "object" && data !== null) {
|
||||||
|
return key.split(".").reduce((item, key) => item[key], data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasClassLong = (text: unknown): boolean => {
|
||||||
|
if (typeof text == "string") {
|
||||||
|
return text.length >= 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -81,6 +91,10 @@ const handleRowClick = (item) => {
|
|||||||
td {
|
td {
|
||||||
font-size: 85%;
|
font-size: 85%;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
||||||
|
&.long {
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody {
|
tbody {
|
||||||
@@ -127,16 +141,19 @@ const handleRowClick = (item) => {
|
|||||||
border: none;
|
border: none;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 8px 12px 8px 50%;
|
padding: 8px 12px 8px 40%;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 8px 12px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 12px;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 45%;
|
width: 35%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.success .sm-toast-inner {
|
&.success .toast-inner {
|
||||||
border-left-color: var(--success-color);
|
border-left-color: var(--success-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.danger .sm-toast-inner {
|
&.danger .toast-inner {
|
||||||
border-left-color: var(--danger-color);
|
border-left-color: var(--danger-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<SMFormCard>
|
<SMFormCard>
|
||||||
<h1>{{ props.title }}</h1>
|
<h3>{{ props.title }}</h3>
|
||||||
<p v-html="computedSanitizedText"></p>
|
<p v-html="computedSanitizedText"></p>
|
||||||
<SMFormFooter>
|
<SMFormFooter>
|
||||||
<template #left>
|
<template #left>
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ export const api = {
|
|||||||
options.headers["Authorization"] = `Bearer ${userStore.token}`;
|
options.headers["Authorization"] = `Bearer ${userStore.token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.method = options.method.toUpperCase() || "GET";
|
||||||
|
|
||||||
if (options.body && typeof options.body === "object") {
|
if (options.body && typeof options.body === "object") {
|
||||||
if (options.body instanceof FormData) {
|
if (options.body instanceof FormData) {
|
||||||
if (
|
if (
|
||||||
@@ -82,6 +84,11 @@ export const api = {
|
|||||||
// remove the "Content-Type" key from the headers object
|
// remove the "Content-Type" key from the headers object
|
||||||
delete options.headers["Content-Type"];
|
delete options.headers["Content-Type"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.method != "POST") {
|
||||||
|
options.body.append("_method", options.method);
|
||||||
|
options.method = "POST";
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
options.body instanceof Blob ||
|
options.body instanceof Blob ||
|
||||||
options.body instanceof ArrayBuffer
|
options.body instanceof ArrayBuffer
|
||||||
@@ -94,7 +101,9 @@ export const api = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(options.method.toUpperCase() || "GET") == "POST" &&
|
(options.method == "POST" ||
|
||||||
|
options.method == "PUT" ||
|
||||||
|
options.method == "PATCH") &&
|
||||||
options.progress
|
options.progress
|
||||||
) {
|
) {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ type FormControlSetValidation = (
|
|||||||
type FormControlIsValid = () => boolean;
|
type FormControlIsValid = () => boolean;
|
||||||
|
|
||||||
export interface FormControlObject {
|
export interface FormControlObject {
|
||||||
value: string;
|
value: unknown;
|
||||||
validate: () => Promise<ValidationResult>;
|
validate: () => Promise<ValidationResult>;
|
||||||
validation: FormControlValidation;
|
validation: FormControlValidation;
|
||||||
clearValidations: FormControlClearValidations;
|
clearValidations: FormControlClearValidations;
|
||||||
|
|||||||
@@ -3,19 +3,23 @@ import { extractFileNameFromUrl } from "./url";
|
|||||||
/**
|
/**
|
||||||
* Tests if an object or string is empty.
|
* Tests if an object or string is empty.
|
||||||
*
|
*
|
||||||
* @param {object|string} objOrString The object or string.
|
* @param {unknown} value The object or string.
|
||||||
* @returns {boolean} If the object or string is empty.
|
* @returns {boolean} If the object or string is empty.
|
||||||
*/
|
*/
|
||||||
export const isEmpty = (objOrString: unknown): boolean => {
|
export const isEmpty = (value: unknown): boolean => {
|
||||||
if (objOrString == null) {
|
if (typeof value === "string") {
|
||||||
return true;
|
return value.trim().length === 0;
|
||||||
} else if (typeof objOrString === "string") {
|
|
||||||
return objOrString.length == 0;
|
|
||||||
} else if (
|
} else if (
|
||||||
typeof objOrString == "object" &&
|
value instanceof File ||
|
||||||
Object.keys(objOrString).length === 0
|
value instanceof Blob ||
|
||||||
|
value instanceof Map ||
|
||||||
|
value instanceof Set
|
||||||
) {
|
) {
|
||||||
return true;
|
return value.size === 0;
|
||||||
|
} else if (value instanceof FormData) {
|
||||||
|
return [...value.entries()].length === 0;
|
||||||
|
} else if (typeof value === "object") {
|
||||||
|
return !value || Object.keys(value).length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { bytesReadable } from "../helpers/types";
|
import { bytesReadable } from "../helpers/types";
|
||||||
import { SMDate } from "./datetime";
|
import { SMDate } from "./datetime";
|
||||||
|
import { isEmpty } from "../helpers/utils";
|
||||||
|
|
||||||
export interface ValidationObject {
|
export interface ValidationObject {
|
||||||
validate: (value: any) => Promise<ValidationResult>;
|
validate: (value: unknown) => Promise<ValidationResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidationResult {
|
export interface ValidationResult {
|
||||||
@@ -744,9 +745,9 @@ export function Required(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
validate: function (value: string): Promise<ValidationResult> {
|
validate: function (value: unknown): Promise<ValidationResult> {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
valid: value.length > 0,
|
valid: !isEmpty(value),
|
||||||
invalidMessages: [
|
invalidMessages: [
|
||||||
typeof this.invalidMessage === "string"
|
typeof this.invalidMessage === "string"
|
||||||
? this.invalidMessage
|
? this.invalidMessage
|
||||||
@@ -831,8 +832,11 @@ export function FileSize(
|
|||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
validate: function (value: File): Promise<ValidationResult> {
|
validate: function (value: File): Promise<ValidationResult> {
|
||||||
|
const isValid =
|
||||||
|
value instanceof File ? value.size < options.size : true;
|
||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
valid: value.size < options.size,
|
valid: isValid,
|
||||||
invalidMessages: [
|
invalidMessages: [
|
||||||
typeof this.invalidMessage === "string"
|
typeof this.invalidMessage === "string"
|
||||||
? this.invalidMessage
|
? this.invalidMessage
|
||||||
|
|||||||
@@ -40,7 +40,10 @@
|
|||||||
<div
|
<div
|
||||||
class="thumbnail"
|
class="thumbnail"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundImage: `url('${event.hero.url}')`,
|
backgroundImage: `url('${mediaGetVariantUrl(
|
||||||
|
event.hero,
|
||||||
|
'medium'
|
||||||
|
)}')`,
|
||||||
}">
|
}">
|
||||||
<div :class="['banner', event['bannerType']]">
|
<div :class="['banner', event['bannerType']]">
|
||||||
{{ event["banner"] }}
|
{{ event["banner"] }}
|
||||||
@@ -87,6 +90,7 @@ import SMToolbar from "../components/SMToolbar.vue";
|
|||||||
import { api } from "../helpers/api";
|
import { api } from "../helpers/api";
|
||||||
import { Event, EventCollection } from "../helpers/api.types";
|
import { Event, EventCollection } from "../helpers/api.types";
|
||||||
import { SMDate } from "../helpers/datetime";
|
import { SMDate } from "../helpers/datetime";
|
||||||
|
import { mediaGetVariantUrl } from "../helpers/media";
|
||||||
import SMMastHead from "../components/SMMastHead.vue";
|
import SMMastHead from "../components/SMMastHead.vue";
|
||||||
import SMContainer from "../components/SMContainer.vue";
|
import SMContainer from "../components/SMContainer.vue";
|
||||||
import SMNoItems from "../components/SMNoItems.vue";
|
import SMNoItems from "../components/SMNoItems.vue";
|
||||||
|
|||||||
@@ -7,6 +7,13 @@
|
|||||||
<SMContainer class="flex-grow-1">
|
<SMContainer class="flex-grow-1">
|
||||||
<SMLoading v-if="pageLoading" large />
|
<SMLoading v-if="pageLoading" large />
|
||||||
<SMForm v-else :model-value="form" @submit="handleSubmit">
|
<SMForm v-else :model-value="form" @submit="handleSubmit">
|
||||||
|
<SMRow>
|
||||||
|
<SMColumn class="media-container">
|
||||||
|
<!-- <div class="media-container"> -->
|
||||||
|
<SMImage :src="imageUrl" />
|
||||||
|
<!-- </div> -->
|
||||||
|
</SMColumn>
|
||||||
|
</SMRow>
|
||||||
<SMRow>
|
<SMRow>
|
||||||
<SMColumn>
|
<SMColumn>
|
||||||
<SMInput control="file" type="file" />
|
<SMInput control="file" type="file" />
|
||||||
@@ -61,12 +68,15 @@
|
|||||||
<SMInput type="textarea" control="description" />
|
<SMInput type="textarea" control="description" />
|
||||||
</SMColumn>
|
</SMColumn>
|
||||||
</SMRow>
|
</SMRow>
|
||||||
<SMRow class="px-2 justify-content-space-between">
|
<SMRow
|
||||||
|
class="px-2 flex-row-reverse justify-content-space-between">
|
||||||
|
<SMButton type="submit" label="Save" :form="form" />
|
||||||
<SMButton
|
<SMButton
|
||||||
|
:form="form"
|
||||||
|
v-if="route.params.id"
|
||||||
type="danger"
|
type="danger"
|
||||||
label="Delete"
|
label="Delete"
|
||||||
@click="handleDelete" />
|
@click="handleDelete" />
|
||||||
<SMButton type="submit" label="Save" />
|
|
||||||
</SMRow>
|
</SMRow>
|
||||||
</SMForm>
|
</SMForm>
|
||||||
</SMContainer>
|
</SMContainer>
|
||||||
@@ -74,13 +84,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref } from "vue";
|
import { computed, reactive, ref, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { api } from "../../helpers/api";
|
import { api } from "../../helpers/api";
|
||||||
import { Form, FormControl } from "../../helpers/form";
|
import { Form, FormControl } from "../../helpers/form";
|
||||||
import { bytesReadable } from "../../helpers/types";
|
import { bytesReadable } from "../../helpers/types";
|
||||||
import { And, FileSize, Required } from "../../helpers/validate";
|
import { And, FileSize, Required } from "../../helpers/validate";
|
||||||
import { Media, MediaResponse } from "../../helpers/api.types";
|
import { MediaResponse } from "../../helpers/api.types";
|
||||||
import { openDialog } from "../../components/SMDialog";
|
import { openDialog } from "../../components/SMDialog";
|
||||||
import DialogConfirm from "../../components/dialogs/SMDialogConfirm.vue";
|
import DialogConfirm from "../../components/dialogs/SMDialogConfirm.vue";
|
||||||
import SMButton from "../../components/SMButton.vue";
|
import SMButton from "../../components/SMButton.vue";
|
||||||
@@ -89,6 +99,9 @@ import SMInput from "../../components/SMInput.vue";
|
|||||||
import SMMastHead from "../../components/SMMastHead.vue";
|
import SMMastHead from "../../components/SMMastHead.vue";
|
||||||
import SMLoading from "../../components/SMLoading.vue";
|
import SMLoading from "../../components/SMLoading.vue";
|
||||||
import { toTitleCase } from "../../helpers/string";
|
import { toTitleCase } from "../../helpers/string";
|
||||||
|
import { useToastStore } from "../../store/ToastStore";
|
||||||
|
import SMColumn from "../../components/SMColumn.vue";
|
||||||
|
import SMImage from "../../components/SMImage.vue";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -116,6 +129,8 @@ const fileData = reactive({
|
|||||||
user: {},
|
user: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const imageUrl = ref("");
|
||||||
|
|
||||||
const handleLoad = async () => {
|
const handleLoad = async () => {
|
||||||
if (route.params.id) {
|
if (route.params.id) {
|
||||||
try {
|
try {
|
||||||
@@ -142,6 +157,8 @@ const handleLoad = async () => {
|
|||||||
: toTitleCase(data.medium.status);
|
: toTitleCase(data.medium.status);
|
||||||
|
|
||||||
fileData.dimensions = data.medium.dimensions;
|
fileData.dimensions = data.medium.dimensions;
|
||||||
|
|
||||||
|
imageUrl.value = fileData.url;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
pageError.value = err.status;
|
pageError.value = err.status;
|
||||||
}
|
}
|
||||||
@@ -152,51 +169,74 @@ const handleLoad = async () => {
|
|||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
let res = null;
|
form.loading(true);
|
||||||
// let data = {
|
let submitData = new FormData();
|
||||||
// title: formData.title.value,
|
|
||||||
// slug: formData.slug.value,
|
|
||||||
// user_id: formData.user_id.value,
|
|
||||||
// content: formData.content.value
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(route.params.id) {
|
// add file if there is one
|
||||||
// res = await axios.put(`posts/${route.params.id}`, data);
|
if (form.controls.file.value instanceof File) {
|
||||||
// } else {
|
submitData.append("file", form.controls.file.value);
|
||||||
// res = await axios.post(`posts`, data);
|
|
||||||
// }
|
|
||||||
|
|
||||||
let submitFormData = new FormData();
|
|
||||||
if (form.file.value instanceof File) {
|
|
||||||
submitFormData.append("file", form.file.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
submitFormData.append("permission", form.permission.value);
|
submitData.append("title", form.controls.title.value as string);
|
||||||
|
submitData.append(
|
||||||
|
"permission",
|
||||||
|
form.controls.permission.value as string
|
||||||
|
);
|
||||||
|
submitData.append(
|
||||||
|
"description",
|
||||||
|
form.controls.description.value as string
|
||||||
|
);
|
||||||
|
|
||||||
await api.post({
|
if (route.params.id) {
|
||||||
url: "/media",
|
await api.put({
|
||||||
body: submitFormData,
|
url: "/media/{id}",
|
||||||
headers: {
|
params: {
|
||||||
"Content-Type": "multipart/form-data",
|
id: route.params.id,
|
||||||
},
|
},
|
||||||
progress: (progressEvent) =>
|
body: submitData,
|
||||||
(formLoadingMessage.value = `Uploading Files ${Math.floor(
|
headers: {
|
||||||
(progressEvent.loaded / progressEvent.total) * 100
|
"Content-Type": "multipart/form-data",
|
||||||
)}%`),
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await api.post({
|
||||||
|
url: "/media",
|
||||||
|
body: submitData,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
// progress: (progressEvent) =>
|
||||||
|
// (formLoadingMessage.value = `Uploading Files ${Math.floor(
|
||||||
|
// (progressEvent.loaded / progressEvent.total) * 100
|
||||||
|
// )}%`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: route.params.id ? "Media Updated" : "Media Created",
|
||||||
|
content: route.params.id
|
||||||
|
? "The media item has been updated."
|
||||||
|
: "The media item been created.",
|
||||||
|
type: "success",
|
||||||
});
|
});
|
||||||
|
|
||||||
form.message("Your details have been updated", "success");
|
router.push({ name: "dashboard-media-list" });
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
form.apiErrors(err);
|
console.log(error);
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Server error",
|
||||||
|
content: "An error occurred saving the media.",
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
form.loading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
form.loading(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (item: Media) => {
|
const handleDelete = async () => {
|
||||||
let result = await openDialog(DialogConfirm, {
|
let result = await openDialog(DialogConfirm, {
|
||||||
title: "Delete File?",
|
title: "Delete File?",
|
||||||
text: `Are you sure you want to delete the file <strong>${item.title}</strong>?`,
|
text: `Are you sure you want to delete the file <strong>${form.controls.title.value}</strong>?`,
|
||||||
cancel: {
|
cancel: {
|
||||||
type: "secondary",
|
type: "secondary",
|
||||||
label: "Cancel",
|
label: "Cancel",
|
||||||
@@ -209,7 +249,7 @@ const handleDelete = async (item: Media) => {
|
|||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
try {
|
try {
|
||||||
await api.delete(`media/${item.id}`);
|
await api.delete(`media/${route.params.id}`);
|
||||||
router.push({ name: "media" });
|
router.push({ name: "media" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
pageError.value = error.status;
|
pageError.value = error.status;
|
||||||
@@ -223,3 +263,14 @@ const computedFileSize = computed(() => {
|
|||||||
|
|
||||||
handleLoad();
|
handleLoad();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.page-dashboard-media-edit {
|
||||||
|
.media-container {
|
||||||
|
max-height: 300px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -39,6 +39,11 @@
|
|||||||
<template #item-size="item">
|
<template #item-size="item">
|
||||||
{{ bytesReadable(item.size) }}
|
{{ bytesReadable(item.size) }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #item-title="item"
|
||||||
|
>{{ item.title }}<br /><span class="small"
|
||||||
|
>({{ item.name }})</span
|
||||||
|
></template
|
||||||
|
>
|
||||||
<template #item-actions="item">
|
<template #item-actions="item">
|
||||||
<SMButton
|
<SMButton
|
||||||
label="Edit"
|
label="Edit"
|
||||||
@@ -86,7 +91,7 @@ const itemsPerPage = 25;
|
|||||||
const itemsPage = ref(parseInt((route.query.page as string) || "1"));
|
const itemsPage = ref(parseInt((route.query.page as string) || "1"));
|
||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
{ text: "Name", value: "title", sortable: true },
|
{ text: "Title (Name)", value: "title", sortable: true },
|
||||||
{ text: "Size", value: "size", sortable: true },
|
{ text: "Size", value: "size", sortable: true },
|
||||||
{ text: "Uploaded By", value: "user.display_name", sortable: true },
|
{ text: "Uploaded By", value: "user.display_name", sortable: true },
|
||||||
{ text: "Actions", value: "actions" },
|
{ text: "Actions", value: "actions" },
|
||||||
@@ -257,7 +262,8 @@ handleLoad();
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.page-dashboard-media-list {
|
.page-dashboard-media-list {
|
||||||
.table tr {
|
.table tr {
|
||||||
td:first-of-type {
|
td:first-of-type,
|
||||||
|
td:nth-of-type(2) {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,6 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
router.push({ name: "dashboard-post-list" });
|
router.push({ name: "dashboard-post-list" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
form.apiErrors(error);
|
form.apiErrors(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user