new media job support
This commit is contained in:
@@ -104,12 +104,25 @@ class MediaWorkerJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if file already exists
|
// Check if file already exists
|
||||||
if (Storage::disk($storage)->exists($data['name']) === true) {
|
$exists = Storage::disk($storage)->exists($data['name']);
|
||||||
if (array_key_exists('replace', $data) === false || isTrue($data['replace']) === false) {
|
if ($exists === true) {
|
||||||
|
if (array_key_exists('noreplace', $data) === true && isTrue($data['noreplace']) === true) {
|
||||||
$this->throwMediaJobFailure('file already exists on server');
|
$this->throwMediaJobFailure('file already exists on server');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($exists === true) {
|
||||||
|
$pathInfo = pathinfo($data['name']);
|
||||||
|
$basename = $pathInfo['filename'];
|
||||||
|
$extension = $pathInfo['extension'];
|
||||||
|
$index = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$index++;
|
||||||
|
$data['name'] = $basename . '-' . $index . '.' . $extension;
|
||||||
|
} while (Storage::disk($storage)->exists($data['name']) === true);
|
||||||
|
}
|
||||||
|
|
||||||
if ($media === null) {
|
if ($media === null) {
|
||||||
$newMedia = true;
|
$newMedia = true;
|
||||||
$media = new Media([
|
$media = new Media([
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
defineComponent,
|
defineComponent,
|
||||||
shallowReactive,
|
shallowReactive,
|
||||||
VNodeProps,
|
VNodeProps,
|
||||||
|
watch,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
|
|
||||||
export interface DialogInstance {
|
export interface DialogInstance {
|
||||||
@@ -52,10 +53,10 @@ export function closeDialog(data?: unknown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastDialog = dialogRefs.pop();
|
const lastDialog = dialogRefs.pop();
|
||||||
if (data === undefined && lastDialog.comp) {
|
if (data === undefined && lastDialog.comp && lastDialog.comp.returnValue) {
|
||||||
data = lastDialog.comp.returnValue();
|
data = lastDialog.comp.returnValue();
|
||||||
}
|
}
|
||||||
if (lastDialog) {
|
if (lastDialog && data !== undefined) {
|
||||||
lastDialog.resolve(data);
|
lastDialog.resolve(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,9 @@ export function openDialog<C extends Component>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
const autofocusElement = document.querySelector("[autofocus]");
|
const autofocusElement = document.querySelector(
|
||||||
|
"[autofocus]",
|
||||||
|
) as HTMLInputElement;
|
||||||
if (autofocusElement) {
|
if (autofocusElement) {
|
||||||
autofocusElement.focus();
|
autofocusElement.focus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,17 @@
|
|||||||
fill="currentColor" />
|
fill="currentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
<img
|
<img
|
||||||
class="max-w-48 max-h-48 w-full h-full"
|
:class="[
|
||||||
|
'max-w-48',
|
||||||
|
'max-h-48',
|
||||||
|
'p-2',
|
||||||
|
'w-full',
|
||||||
|
'h-full',
|
||||||
|
{
|
||||||
|
'border-red-6': feedbackInvalid,
|
||||||
|
'border-2': feedbackInvalid,
|
||||||
|
},
|
||||||
|
]"
|
||||||
@load="handleImageLoaded"
|
@load="handleImageLoaded"
|
||||||
@error="handleImageError"
|
@error="handleImageError"
|
||||||
:style="{ display: image == '' ? 'none' : 'block' }"
|
:style="{ display: image == '' ? 'none' : 'block' }"
|
||||||
@@ -29,6 +39,11 @@
|
|||||||
fill="currentColor" />
|
fill="currentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
<p
|
||||||
|
v-if="feedbackInvalid"
|
||||||
|
class="px-2 -mt-2 pb-2 text-xs text-red-6">
|
||||||
|
{{ feedbackInvalid }}
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="font-medium px-6 py-1.5 rounded-md hover:shadow-md transition text-sm bg-sky-600 hover:bg-sky-500 text-white cursor-pointer"
|
class="font-medium px-6 py-1.5 rounded-md hover:shadow-md transition text-sm bg-sky-600 hover:bg-sky-500 text-white cursor-pointer"
|
||||||
@@ -55,7 +70,6 @@ import { toTitleCase } from "../helpers/string";
|
|||||||
import { mediaGetThumbnail } from "../helpers/media";
|
import { mediaGetThumbnail } from "../helpers/media";
|
||||||
import { openDialog } from "./SMDialog";
|
import { openDialog } from "./SMDialog";
|
||||||
import SMDialogMedia from "./dialogs/SMDialogMedia.vue";
|
import SMDialogMedia from "./dialogs/SMDialogMedia.vue";
|
||||||
// import SMDialogUpload from "./dialogs/SMDialogUpload.vue";
|
|
||||||
import { Media } from "../helpers/api.types";
|
import { Media } from "../helpers/api.types";
|
||||||
import SMLoading from "./SMLoading.vue";
|
import SMLoading from "./SMLoading.vue";
|
||||||
|
|
||||||
|
|||||||
@@ -143,16 +143,10 @@
|
|||||||
{ 'mb-6': showMediaName(item) },
|
{ 'mb-6': showMediaName(item) },
|
||||||
]"
|
]"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundImage:
|
backgroundImage: `url('${mediaGetThumbnail(
|
||||||
item.status === 'OK'
|
item,
|
||||||
? `url('${mediaGetThumbnail(
|
)}')`,
|
||||||
item,
|
backgroundColor: 'initial',
|
||||||
)}')`
|
|
||||||
: 'initial',
|
|
||||||
backgroundColor:
|
|
||||||
item.status === 'OK'
|
|
||||||
? 'initial'
|
|
||||||
: 'rgba(220,220,220,1)',
|
|
||||||
}">
|
}">
|
||||||
<div
|
<div
|
||||||
v-if="showMediaName(item)"
|
v-if="showMediaName(item)"
|
||||||
@@ -160,38 +154,11 @@
|
|||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
</div>
|
</div>
|
||||||
<SMLoading
|
<SMLoading
|
||||||
v-if="
|
v-if="false"
|
||||||
item.status !== 'OK' &&
|
|
||||||
item.status.startsWith(
|
|
||||||
'Error',
|
|
||||||
) === false
|
|
||||||
"
|
|
||||||
small
|
small
|
||||||
class="bg-white bg-op-90 w-full h-full"
|
class="bg-white bg-op-90 w-full h-full"
|
||||||
>{{
|
>NONE</SMLoading
|
||||||
item.status.split(":")
|
|
||||||
.length > 1
|
|
||||||
? item.status
|
|
||||||
.split(":")[1]
|
|
||||||
.trim()
|
|
||||||
: item.status
|
|
||||||
}}</SMLoading
|
|
||||||
>
|
>
|
||||||
<div
|
|
||||||
v-if="
|
|
||||||
item.status.startsWith(
|
|
||||||
'Error',
|
|
||||||
) === true
|
|
||||||
">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-10 w-10"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
d="M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"
|
|
||||||
fill="rgba(220,38,38,1)" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -430,16 +397,9 @@
|
|||||||
backgroundImage: `url('${mediaGetThumbnail(
|
backgroundImage: `url('${mediaGetThumbnail(
|
||||||
item,
|
item,
|
||||||
)}')`,
|
)}')`,
|
||||||
backgroundColor:
|
|
||||||
item.status === 'OK'
|
|
||||||
? 'initial'
|
|
||||||
: 'rgba(220,220,220,1)',
|
|
||||||
}">
|
}">
|
||||||
<SMLoading
|
<SMLoading
|
||||||
v-if="
|
v-if="false"
|
||||||
item.status !== 'OK' &&
|
|
||||||
item.status.startsWith('Error') === false
|
|
||||||
"
|
|
||||||
small
|
small
|
||||||
class="bg-white bg-op-90 w-full h-full" />
|
class="bg-white bg-op-90 w-full h-full" />
|
||||||
<div
|
<div
|
||||||
@@ -996,11 +956,7 @@ const updateFiles = async () => {
|
|||||||
let remaining = false;
|
let remaining = false;
|
||||||
|
|
||||||
mediaItems.value.forEach((item, index) => {
|
mediaItems.value.forEach((item, index) => {
|
||||||
if (
|
if (isUUID(item.id)) {
|
||||||
isUUID(item.id) &&
|
|
||||||
item.status != "OK" &&
|
|
||||||
item.status.startsWith("Error") == false
|
|
||||||
) {
|
|
||||||
remaining = true;
|
remaining = true;
|
||||||
|
|
||||||
api.get({
|
api.get({
|
||||||
@@ -1056,9 +1012,9 @@ const updateFiles = async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mediaItems.value = mediaItems.value.filter(
|
// mediaItems.value = mediaItems.value.filter(
|
||||||
(item) => item.status.startsWith("Error") === false,
|
// (item) => item.status.startsWith("Error") === false,
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (remaining) {
|
if (remaining) {
|
||||||
updateFilesNonce.value = setTimeout(() => {
|
updateFilesNonce.value = setTimeout(() => {
|
||||||
@@ -1106,7 +1062,6 @@ const handleLoad = async () => {
|
|||||||
let params = {
|
let params = {
|
||||||
page: page.value,
|
page: page.value,
|
||||||
limit: perPage.value,
|
limit: perPage.value,
|
||||||
status: "!Error",
|
|
||||||
filter: "",
|
filter: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1202,8 +1157,8 @@ watch(mediaItems, () => {
|
|||||||
const computedSelectDisabled = computed(() => {
|
const computedSelectDisabled = computed(() => {
|
||||||
if (selectedTab.value == "tab-browser") {
|
if (selectedTab.value == "tab-browser") {
|
||||||
return (
|
return (
|
||||||
selected.value.length == 0 ||
|
selected.value.length == 0 // ||
|
||||||
selected.value.findIndex((item) => item.status !== "OK") > -1
|
// selected.value.findIndex((item) => item.status !== "OK") > -1
|
||||||
);
|
);
|
||||||
} else if (selectedTab.value == "tab-url") {
|
} else if (selectedTab.value == "tab-url") {
|
||||||
return (
|
return (
|
||||||
|
|||||||
45
resources/js/components/dialogs/SMDialogProgress.vue
Normal file
45
resources/js/components/dialogs/SMDialogProgress.vue
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="fixed top-0 left-0 w-full h-full bg-black bg-op-20 backdrop-blur"></div>
|
||||||
|
<div
|
||||||
|
class="fixed top-0 left-0 right-0 bottom-0 flex-justify-center flex-items-center flex">
|
||||||
|
<div
|
||||||
|
class="flex flex-col m-4 border-1 bg-white rounded-xl text-gray-5 px-4 md:px-12 py-4 md:py-8 max-w-200 w-full overflow-hidden">
|
||||||
|
<h2 class="mb-2">{{ props.title }}</h2>
|
||||||
|
<div
|
||||||
|
v-for="(row, index) in props.rows"
|
||||||
|
class="flex flex-col text-xs my-4"
|
||||||
|
:key="index">
|
||||||
|
<div class="w-full bg-gray-3 h-3 mb-2 rounded-2">
|
||||||
|
<div
|
||||||
|
class="bg-sky-600 h-3 rounded-2"
|
||||||
|
:style="{
|
||||||
|
width: `${props.progress[index]}%`,
|
||||||
|
}"></div>
|
||||||
|
</div>
|
||||||
|
<p class="m-0"></p>
|
||||||
|
{{ row }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -1,373 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="fixed top-0 left-0 w-full h-full bg-black bg-op-20 backdrop-blur"></div>
|
|
||||||
<div
|
|
||||||
class="fixed top-0 left-0 right-0 bottom-0 flex-justify-center flex-items-center flex">
|
|
||||||
<div
|
|
||||||
class="flex flex-col m-4 border-1 bg-white rounded-xl text-gray-5 px-4 md:px-12 py-4 md:py-8 max-w-200 w-full overflow-hidden"
|
|
||||||
v-if="uploadFileCount > 0">
|
|
||||||
<h2 class="mb-2">Upload Media</h2>
|
|
||||||
<div class="flex flex-col text-xs pb-2 my-4">
|
|
||||||
<div class="w-full bg-gray-3 h-3 mb-2 rounded-2">
|
|
||||||
<div
|
|
||||||
class="bg-sky-600 h-3 rounded-2"
|
|
||||||
:style="{
|
|
||||||
width: `${progressUploadPercent}%`,
|
|
||||||
}"></div>
|
|
||||||
</div>
|
|
||||||
<p class="m-0">
|
|
||||||
{{ progressUploadStatus }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col text-xs my-2">
|
|
||||||
<div class="w-full bg-gray-3 h-3 mb-2 rounded-2">
|
|
||||||
<div
|
|
||||||
class="bg-sky-600 h-3 rounded-2"
|
|
||||||
:style="{
|
|
||||||
width: `${
|
|
||||||
(100 / uploadFileCount) * mediaItems.length
|
|
||||||
}%`,
|
|
||||||
}"></div>
|
|
||||||
</div>
|
|
||||||
<p class="m-0">
|
|
||||||
{{ progressFileStatus }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
id="file"
|
|
||||||
ref="refUploadInput"
|
|
||||||
type="file"
|
|
||||||
style="display: none"
|
|
||||||
:accept="computedAccepts"
|
|
||||||
@change="handleChangeSelectFile" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, onMounted, onUnmounted, ref, Ref } from "vue";
|
|
||||||
import { closeDialog } from "../SMDialog";
|
|
||||||
import { api } from "../../helpers/api";
|
|
||||||
import { ApiInfo, Media, MediaResponse } from "../../helpers/api.types";
|
|
||||||
import { convertFileNameToTitle } from "../../helpers/utils";
|
|
||||||
import { isUUID } from "../../helpers/uuid";
|
|
||||||
import { useToastStore } from "../../store/ToastStore";
|
|
||||||
import { bytesReadable } from "../../helpers/types";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
mime: {
|
|
||||||
type: String,
|
|
||||||
default: "image/*",
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
accepts: {
|
|
||||||
type: String,
|
|
||||||
default: "image/*",
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
multiple: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the File Upload Input element.
|
|
||||||
*/
|
|
||||||
const refUploadInput = ref<HTMLInputElement | null>(null);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Max upload size
|
|
||||||
*/
|
|
||||||
const max_upload_size = ref("");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of current media items.
|
|
||||||
*/
|
|
||||||
const mediaItems: Ref<Media[]> = ref([]);
|
|
||||||
const processingItems: Ref<Media[]> = ref([]);
|
|
||||||
|
|
||||||
const uploadFileCount = ref(0);
|
|
||||||
const uploadFileNum = ref(0);
|
|
||||||
const uploadFileName = ref("");
|
|
||||||
const uploadFileProgress = ref(0);
|
|
||||||
|
|
||||||
const processFileName = ref("");
|
|
||||||
const processFileStatus = ref("");
|
|
||||||
|
|
||||||
const progressUploadPercent = computed(() => {
|
|
||||||
if (uploadFileCount.value <= uploadFileNum.value) {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
(100 / uploadFileCount.value) * uploadFileNum.value +
|
|
||||||
(100 / uploadFileCount.value / 100) * uploadFileProgress.value
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const progressUploadStatus = computed(() => {
|
|
||||||
if (uploadFileCount.value <= uploadFileNum.value) {
|
|
||||||
return `Uploaded ${uploadFileName.value}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `Uploading ${uploadFileName.value} - ${uploadFileProgress.value}%`;
|
|
||||||
});
|
|
||||||
|
|
||||||
const progressFileStatus = computed(() => {
|
|
||||||
if (processFileName.value.length > 0) {
|
|
||||||
return (
|
|
||||||
processFileName.value +
|
|
||||||
" - " +
|
|
||||||
(processFileStatus.value.split(":").length > 1
|
|
||||||
? processFileStatus.value.split(":")[1].trim()
|
|
||||||
: processFileStatus.value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Waiting for upload to complete";
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the file types accepted.
|
|
||||||
*/
|
|
||||||
const computedAccepts = computed(() => {
|
|
||||||
if (props.accepts.length > 0) {
|
|
||||||
return props.accepts;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.mime.endsWith("/")) {
|
|
||||||
return `${props.mime}*`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return props.mime;
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleChangeSelectFile = async () => {
|
|
||||||
if (refUploadInput.value != null && refUploadInput.value.files != null) {
|
|
||||||
const fileList = Array.from(refUploadInput.value.files);
|
|
||||||
uploadFileCount.value = fileList.length;
|
|
||||||
|
|
||||||
for (
|
|
||||||
uploadFileNum.value = 0;
|
|
||||||
uploadFileNum.value < uploadFileCount.value;
|
|
||||||
uploadFileNum.value++
|
|
||||||
) {
|
|
||||||
const file = fileList[uploadFileNum.value];
|
|
||||||
|
|
||||||
const chunkSize = 50 * 1024 * 1024;
|
|
||||||
let chunk = 0;
|
|
||||||
let chunkCount = 1;
|
|
||||||
let resultMedia = null;
|
|
||||||
|
|
||||||
if (file.size > chunkSize) {
|
|
||||||
chunkCount = Math.ceil(file.size / chunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadFileName.value = file.name;
|
|
||||||
uploadFileProgress.value = 0;
|
|
||||||
|
|
||||||
while (chunk < chunkCount) {
|
|
||||||
let submitFormData = new FormData();
|
|
||||||
if (chunkCount == 1) {
|
|
||||||
submitFormData.append("file", file);
|
|
||||||
} else {
|
|
||||||
const offset = chunk * chunkSize;
|
|
||||||
const fileChunk = file.slice(offset, offset + chunkSize);
|
|
||||||
if (resultMedia !== null) {
|
|
||||||
submitFormData.append("id", resultMedia.id);
|
|
||||||
}
|
|
||||||
submitFormData.append("file", fileChunk);
|
|
||||||
submitFormData.append("chunk", (chunk + 1).toString());
|
|
||||||
submitFormData.append("chunk_count", chunkCount.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
submitFormData.append("name", file.name);
|
|
||||||
submitFormData.append(
|
|
||||||
"title",
|
|
||||||
convertFileNameToTitle(file.name),
|
|
||||||
);
|
|
||||||
submitFormData.append("description", "");
|
|
||||||
|
|
||||||
try {
|
|
||||||
let result = await api.post({
|
|
||||||
url: "/media",
|
|
||||||
body: submitFormData,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
progress: (progressEvent) => {
|
|
||||||
uploadFileProgress.value = Math.floor(
|
|
||||||
((chunk * chunkSize + progressEvent.loaded) /
|
|
||||||
file.size) *
|
|
||||||
100,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (result.data) {
|
|
||||||
resultMedia = (result.data as MediaResponse).medium;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
let errorString = "A server error occurred";
|
|
||||||
|
|
||||||
if (error.status == 413) {
|
|
||||||
errorString = `The file is larger than ${max_upload_size.value}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Upload failed",
|
|
||||||
type: "danger",
|
|
||||||
content: errorString,
|
|
||||||
});
|
|
||||||
|
|
||||||
resultMedia = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultMedia != null) {
|
|
||||||
processingItems.value.push(resultMedia);
|
|
||||||
}
|
|
||||||
processFiles();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
closeDialog(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const processFilesNonce = ref(null);
|
|
||||||
|
|
||||||
const processFiles = async () => {
|
|
||||||
if (processFilesNonce.value == null) {
|
|
||||||
let remaining = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < processingItems.value.length; i++) {
|
|
||||||
let item = processingItems.value[i];
|
|
||||||
let breakLoop = true;
|
|
||||||
|
|
||||||
if (
|
|
||||||
isUUID(item.id) &&
|
|
||||||
item.status != "OK" &&
|
|
||||||
item.status.startsWith("Error") == false
|
|
||||||
) {
|
|
||||||
remaining = true;
|
|
||||||
|
|
||||||
api.get({
|
|
||||||
url: "/media/{id}",
|
|
||||||
params: {
|
|
||||||
id: item.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((updateResult) => {
|
|
||||||
if (updateResult.data) {
|
|
||||||
let removeItem = false;
|
|
||||||
|
|
||||||
const updateData =
|
|
||||||
updateResult.data as MediaResponse;
|
|
||||||
if (updateData.medium.status == "OK") {
|
|
||||||
mediaItems.value.push(updateData.medium);
|
|
||||||
removeItem = true;
|
|
||||||
} else if (
|
|
||||||
updateData.medium.status.startsWith("Error") ===
|
|
||||||
true
|
|
||||||
) {
|
|
||||||
removeItem = true;
|
|
||||||
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Upload failed",
|
|
||||||
type: "danger",
|
|
||||||
content: updateData.medium.status,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
processFileName.value = updateData.medium.name;
|
|
||||||
processFileStatus.value =
|
|
||||||
updateData.medium.status;
|
|
||||||
breakLoop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removeItem) {
|
|
||||||
processingItems.value =
|
|
||||||
processingItems.value.filter(
|
|
||||||
(mediaItem) =>
|
|
||||||
mediaItem.id !==
|
|
||||||
updateData.medium.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw "error";
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
/* error retreiving data */
|
|
||||||
processingItems.value = processingItems.value.filter(
|
|
||||||
(mediaItem) => mediaItem.id !== item.id,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!breakLoop) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remaining) {
|
|
||||||
processFilesNonce.value = setTimeout(() => {
|
|
||||||
processFilesNonce.value = null;
|
|
||||||
processFiles();
|
|
||||||
}, 500);
|
|
||||||
} else {
|
|
||||||
processFilesNonce.value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processFilesNonce.value == null) {
|
|
||||||
if (mediaItems.value.length == 0) {
|
|
||||||
closeDialog(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.multiple == false) {
|
|
||||||
closeDialog(mediaItems.value[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
closeDialog(mediaItems.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get max upload size
|
|
||||||
api.get({
|
|
||||||
url: "",
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
if (result.data) {
|
|
||||||
const data = result.data as ApiInfo;
|
|
||||||
|
|
||||||
max_upload_size.value = bytesReadable(data.max_upload_size);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
/* empty */
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleFocus = () => {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
if (uploadFileCount.value == 0) {
|
|
||||||
closeDialog(false);
|
|
||||||
}
|
|
||||||
}, 20);
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener("focus", handleFocus);
|
|
||||||
|
|
||||||
if (refUploadInput.value != null) {
|
|
||||||
refUploadInput.value.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.removeEventListener("focus", handleFocus);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -19,7 +19,7 @@ interface ApiCallbackData {
|
|||||||
type ApiProgressCallback = (progress: ApiProgressData) => void;
|
type ApiProgressCallback = (progress: ApiProgressData) => void;
|
||||||
type ApiResultCallback = (data: ApiCallbackData) => void;
|
type ApiResultCallback = (data: ApiCallbackData) => void;
|
||||||
|
|
||||||
interface ApiOptions {
|
export interface ApiOptions {
|
||||||
url: string;
|
url: string;
|
||||||
params?: object;
|
params?: object;
|
||||||
method?: string;
|
method?: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
:title="pageHeading"
|
:title="pageHeading"
|
||||||
:back-link="{ name: 'dashboard-media-list' }"
|
:back-link="{ name: 'dashboard-media-list' }"
|
||||||
back-title="Back to Media" />
|
back-title="Back to Media" />
|
||||||
<SMLoading v-if="form.loading()">{{ progressText }}</SMLoading>
|
<SMLoading v-if="form.loading()" />
|
||||||
<div v-else class="max-w-4xl mx-auto px-4 mt-8">
|
<div v-else class="max-w-4xl mx-auto px-4 mt-8">
|
||||||
<SMForm
|
<SMForm
|
||||||
:model-value="form"
|
:model-value="form"
|
||||||
@@ -77,9 +77,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive, ref, watch } from "vue";
|
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { api } from "../../helpers/api";
|
import { ApiOptions, 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, Required } from "../../helpers/validate";
|
import { And, Required } from "../../helpers/validate";
|
||||||
@@ -88,7 +88,7 @@ import {
|
|||||||
MediaJobResponse,
|
MediaJobResponse,
|
||||||
MediaResponse,
|
MediaResponse,
|
||||||
} from "../../helpers/api.types";
|
} from "../../helpers/api.types";
|
||||||
import { openDialog } from "../../components/SMDialog";
|
import { closeDialog, openDialog } from "../../components/SMDialog";
|
||||||
import DialogConfirm from "../../components/dialogs/SMDialogConfirm.vue";
|
import DialogConfirm from "../../components/dialogs/SMDialogConfirm.vue";
|
||||||
import SMForm from "../../components/SMForm.vue";
|
import SMForm from "../../components/SMForm.vue";
|
||||||
import SMInput from "../../components/SMInput.vue";
|
import SMInput from "../../components/SMInput.vue";
|
||||||
@@ -100,6 +100,7 @@ import SMSelectFile from "../../components/SMSelectFile.vue";
|
|||||||
import { userHasPermission } from "../../helpers/utils";
|
import { userHasPermission } from "../../helpers/utils";
|
||||||
import SMImageGallery from "../../components/SMImageGallery.vue";
|
import SMImageGallery from "../../components/SMImageGallery.vue";
|
||||||
import { toTitleCase } from "../../helpers/string";
|
import { toTitleCase } from "../../helpers/string";
|
||||||
|
import SMDialogProgress from "../../components/dialogs/SMDialogProgress.vue";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -111,13 +112,12 @@ const pageHeading = route.params.id
|
|||||||
? "Edit Multiple Media"
|
? "Edit Multiple Media"
|
||||||
: "Edit Media"
|
: "Edit Media"
|
||||||
: "Upload Media";
|
: "Upload Media";
|
||||||
const progressText = ref("");
|
|
||||||
const galleryItems = ref([]);
|
const galleryItems = ref([]);
|
||||||
|
|
||||||
const form = reactive(
|
const form = reactive(
|
||||||
Form({
|
Form({
|
||||||
file: FormControl("", And([Required()])),
|
file: FormControl("", And([Required()])),
|
||||||
title: FormControl(),
|
title: FormControl("", Required()),
|
||||||
description: FormControl(),
|
description: FormControl(),
|
||||||
permission: FormControl(),
|
permission: FormControl(),
|
||||||
}),
|
}),
|
||||||
@@ -193,204 +193,271 @@ const handleLoad = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (enableFormCallBack) => {
|
const dialogDataSetStatus = (dialogData, status, progress, add) => {
|
||||||
let processing = false;
|
if (add) {
|
||||||
form.loading(true);
|
dialogData.rows.push(status);
|
||||||
|
dialogData.progress.push(progress);
|
||||||
try {
|
} else {
|
||||||
if (editMultiple === false) {
|
const index = dialogData.rows.length - 1;
|
||||||
let submitData = new FormData();
|
if (status.length > 0) {
|
||||||
|
dialogData.rows[index] = status;
|
||||||
// add file if there is one
|
|
||||||
if (form.controls.file.value instanceof File) {
|
|
||||||
submitData.append("file", form.controls.file.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,
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = null;
|
|
||||||
if (route.params.id) {
|
|
||||||
result = await api.put({
|
|
||||||
url: "/media/{id}",
|
|
||||||
params: {
|
|
||||||
id: route.params.id,
|
|
||||||
},
|
|
||||||
body: submitData,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
progress: (progressEvent) =>
|
|
||||||
(progressText.value = `Uploading File: ${Math.floor(
|
|
||||||
(progressEvent.loaded / progressEvent.total) * 100,
|
|
||||||
)}%`),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
result = await api.chunk({
|
|
||||||
url: "/media",
|
|
||||||
body: submitData,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
chunk: "file",
|
|
||||||
progress: (progressEvent) =>
|
|
||||||
(progressText.value = `Uploading File: ${Math.floor(
|
|
||||||
(progressEvent.loaded / progressEvent.total) * 100,
|
|
||||||
)}%`),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const mediaJobId = result.data.media_job.id;
|
|
||||||
const mediaJobUpdate = async () => {
|
|
||||||
api.get({
|
|
||||||
url: "/media/job/{id}",
|
|
||||||
params: {
|
|
||||||
id: mediaJobId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
const data = result.data as MediaJobResponse;
|
|
||||||
|
|
||||||
// queued
|
|
||||||
// complete
|
|
||||||
// waiting
|
|
||||||
// processing - txt - prog
|
|
||||||
|
|
||||||
// invalid - err
|
|
||||||
// failed - err
|
|
||||||
|
|
||||||
if (data.media_job.status != "complete") {
|
|
||||||
if (data.media_job.status == "queued") {
|
|
||||||
progressText.value = "Queued for processing";
|
|
||||||
} else if (data.media_job.status == "processing") {
|
|
||||||
if (data.media_job.progress != -1) {
|
|
||||||
progressText.value = `${toTitleCase(
|
|
||||||
data.media_job.status_text,
|
|
||||||
)} ${data.media_job.progress}%`;
|
|
||||||
} else {
|
|
||||||
progressText.value = `${toTitleCase(
|
|
||||||
data.media_job.status_text,
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
data.media_job.status == "invalid" ||
|
|
||||||
data.media_job.status == "failed"
|
|
||||||
) {
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Error Processing Media",
|
|
||||||
content: toTitleCase(
|
|
||||||
data.media_job.status_text,
|
|
||||||
),
|
|
||||||
type: "danger",
|
|
||||||
});
|
|
||||||
|
|
||||||
progressText.value = "";
|
|
||||||
form.loading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.setTimeout(mediaJobUpdate, 500);
|
|
||||||
} else {
|
|
||||||
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",
|
|
||||||
});
|
|
||||||
|
|
||||||
progressText.value = "";
|
|
||||||
form.loading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.log("error", e);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
processing = true;
|
|
||||||
mediaJobUpdate();
|
|
||||||
} else {
|
|
||||||
let successCount = 0;
|
|
||||||
let errorCount = 0;
|
|
||||||
|
|
||||||
(route.params.id as string).split(",").forEach(async (id) => {
|
|
||||||
try {
|
|
||||||
let data = {
|
|
||||||
title: form.controls.title.value,
|
|
||||||
content: form.controls.content.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
await api.put({
|
|
||||||
url: "/media/{id}",
|
|
||||||
params: {
|
|
||||||
id: id,
|
|
||||||
},
|
|
||||||
body: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
successCount++;
|
|
||||||
} catch (err) {
|
|
||||||
errorCount++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errorCount === 0) {
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Media Updated",
|
|
||||||
content: `The selected media have been updated.`,
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
} else if (successCount === 0) {
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Error Updating Media",
|
|
||||||
content: "An unexpected server error occurred.",
|
|
||||||
type: "danger",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Some Media Updated",
|
|
||||||
content: `Only ${successCount} media items where updated. ${errorCount} could not because of an unexpected error.`,
|
|
||||||
type: "warning",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (progress > -1) {
|
||||||
// const urlParams = new URLSearchParams(window.location.search);
|
dialogData.progress[index] = progress;
|
||||||
// const returnUrl = urlParams.get("return");
|
|
||||||
// if (returnUrl) {
|
|
||||||
// router.push(decodeURIComponent(returnUrl));
|
|
||||||
// } else {
|
|
||||||
// router.push({ name: "dashboard-media-list" });
|
|
||||||
// }
|
|
||||||
} catch (error) {
|
|
||||||
processing = false;
|
|
||||||
|
|
||||||
useToastStore().addToast({
|
|
||||||
title: "Server error",
|
|
||||||
content: "An error occurred saving the media.",
|
|
||||||
type: "danger",
|
|
||||||
});
|
|
||||||
|
|
||||||
enableFormCallBack();
|
|
||||||
} finally {
|
|
||||||
if (processing == false) {
|
|
||||||
progressText.value = "";
|
|
||||||
form.loading(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (enableFormCallBack) => {
|
||||||
|
if (editMultiple === false) {
|
||||||
|
let dialogData = ref({
|
||||||
|
title: "Upload Media",
|
||||||
|
rows: [],
|
||||||
|
progress: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
openDialog(SMDialogProgress, dialogData.value);
|
||||||
|
let submitData = new FormData();
|
||||||
|
|
||||||
|
// add file if there is one
|
||||||
|
if (form.controls.file.value instanceof File) {
|
||||||
|
submitData.append("file", form.controls.file.value);
|
||||||
|
dialogDataSetStatus(
|
||||||
|
dialogData.value,
|
||||||
|
`Uploading File: ${form.controls.file.value.name}`,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
let apiRequest: ApiOptions = {
|
||||||
|
url: "/media",
|
||||||
|
body: submitData,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
progress: (progressEvent) => {
|
||||||
|
dialogDataSetStatus(
|
||||||
|
dialogData.value,
|
||||||
|
"",
|
||||||
|
Math.floor(
|
||||||
|
(progressEvent.loaded / progressEvent.total) * 100,
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (submitData.has("file") == true) {
|
||||||
|
apiRequest.chunk = "file";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.params.id) {
|
||||||
|
apiRequest.url = "/media/{id}";
|
||||||
|
apiRequest.method = "PUT";
|
||||||
|
apiRequest.params = {
|
||||||
|
id: route.params.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
api.chunk(apiRequest)
|
||||||
|
.then((result) => {
|
||||||
|
if (submitData.has("file") == true) {
|
||||||
|
dialogDataSetStatus(
|
||||||
|
dialogData.value,
|
||||||
|
"Upload Complete",
|
||||||
|
100,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogDataSetStatus(dialogData.value, "Processing", 0, true);
|
||||||
|
|
||||||
|
const mediaJobId = (result.data as MediaJobResponse).media_job
|
||||||
|
.id;
|
||||||
|
const mediaJobUpdate = async () => {
|
||||||
|
api.get({
|
||||||
|
url: "/media/job/{id}",
|
||||||
|
params: {
|
||||||
|
id: mediaJobId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
const data = result.data as MediaJobResponse;
|
||||||
|
|
||||||
|
const statusText = toTitleCase(
|
||||||
|
data.media_job.status_text,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.media_job.status != "complete") {
|
||||||
|
if (data.media_job.status == "queued") {
|
||||||
|
dialogDataSetStatus(
|
||||||
|
dialogData.value,
|
||||||
|
"Queued for processing",
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
data.media_job.status == "processing"
|
||||||
|
) {
|
||||||
|
dialogDataSetStatus(
|
||||||
|
dialogData.value,
|
||||||
|
statusText,
|
||||||
|
data.media_job.progress,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
data.media_job.status == "invalid" ||
|
||||||
|
data.media_job.status == "failed"
|
||||||
|
) {
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Error Processing Media",
|
||||||
|
content: statusText,
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
|
||||||
|
form.controls.file.setValidationResult(
|
||||||
|
false,
|
||||||
|
statusText,
|
||||||
|
);
|
||||||
|
|
||||||
|
closeDialog();
|
||||||
|
enableFormCallBack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout(mediaJobUpdate, 500);
|
||||||
|
} else {
|
||||||
|
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",
|
||||||
|
});
|
||||||
|
|
||||||
|
closeDialog();
|
||||||
|
enableFormCallBack();
|
||||||
|
|
||||||
|
// return to dashboard
|
||||||
|
const urlParams = new URLSearchParams(
|
||||||
|
window.location.search,
|
||||||
|
);
|
||||||
|
const returnUrl = urlParams.get("return");
|
||||||
|
if (returnUrl) {
|
||||||
|
router.push(decodeURIComponent(returnUrl));
|
||||||
|
} else {
|
||||||
|
router.push({
|
||||||
|
name: "dashboard-media-list",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Error Uploading Media",
|
||||||
|
content: "A server error occurred.",
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
|
||||||
|
closeDialog();
|
||||||
|
enableFormCallBack();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
mediaJobUpdate();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.status == 413) {
|
||||||
|
form.controls.file.setValidationResult(
|
||||||
|
false,
|
||||||
|
"The file size is too large",
|
||||||
|
);
|
||||||
|
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Error Uploading Media",
|
||||||
|
content: "The file size is too large.",
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Error Uploading Media",
|
||||||
|
content: "A server error occurred.",
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDialog();
|
||||||
|
enableFormCallBack();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
|
||||||
|
(route.params.id as string).split(",").forEach(async (id) => {
|
||||||
|
try {
|
||||||
|
let data = {
|
||||||
|
title: form.controls.title.value,
|
||||||
|
content: form.controls.content.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
await api.put({
|
||||||
|
url: "/media/{id}",
|
||||||
|
params: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
successCount++;
|
||||||
|
} catch (err) {
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorCount === 0) {
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Media Updated",
|
||||||
|
content: `The selected media have been updated.`,
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
} else if (successCount === 0) {
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Error Updating Media",
|
||||||
|
content: "An unexpected server error occurred.",
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
useToastStore().addToast({
|
||||||
|
title: "Some Media Updated",
|
||||||
|
content: `Only ${successCount} media items where updated. ${errorCount} could not because of an unexpected error.`,
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
// const returnUrl = urlParams.get("return");
|
||||||
|
// if (returnUrl) {
|
||||||
|
// router.push(decodeURIComponent(returnUrl));
|
||||||
|
// } else {
|
||||||
|
// router.push({ name: "dashboard-media-list" });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
const handleFailValidation = () => {
|
const handleFailValidation = () => {
|
||||||
useToastStore().addToast({
|
useToastStore().addToast({
|
||||||
title: "Save Error",
|
title: "Save Error",
|
||||||
|
|||||||
Reference in New Issue
Block a user