route element file uploads
Add short and long PDF paths to elements, keep pdfPath as a short PDF compatibility alias, and route generic file uploads by fileType.
This commit is contained in:
parent
0d5783ba9c
commit
9bbabc7fa6
28 changed files with 686 additions and 214 deletions
|
|
@ -14,13 +14,23 @@ interface ElementForm {
|
|||
|
||||
const route = useRoute()
|
||||
const elementsStore = useElementsStore()
|
||||
const { element, isLoading, error, isSaving, isUploadingIconImage, isUploadingPdf, saveError } =
|
||||
storeToRefs(elementsStore)
|
||||
const {
|
||||
element,
|
||||
isLoading,
|
||||
error,
|
||||
isSaving,
|
||||
isUploadingIconImage,
|
||||
isUploadingShortPdf,
|
||||
isUploadingLongPdf,
|
||||
saveError,
|
||||
} = storeToRefs(elementsStore)
|
||||
const savedMessage = ref<string | null>(null)
|
||||
const iconImageStatus = ref<string | null>(null)
|
||||
const pdfStatus = ref<string | null>(null)
|
||||
const shortPdfStatus = ref<string | null>(null)
|
||||
const longPdfStatus = ref<string | null>(null)
|
||||
const iconImageInput = ref<HTMLInputElement | null>(null)
|
||||
const pdfInput = ref<HTMLInputElement | null>(null)
|
||||
const shortPdfInput = ref<HTMLInputElement | null>(null)
|
||||
const longPdfInput = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const form = reactive<ElementForm>({
|
||||
title: '',
|
||||
|
|
@ -56,7 +66,8 @@ watch(
|
|||
|
||||
savedMessage.value = null
|
||||
iconImageStatus.value = null
|
||||
pdfStatus.value = null
|
||||
shortPdfStatus.value = null
|
||||
longPdfStatus.value = null
|
||||
void elementsStore.fetchElement(currentElementId)
|
||||
},
|
||||
{ immediate: true },
|
||||
|
|
@ -95,8 +106,12 @@ function chooseIconImage(): void {
|
|||
iconImageInput.value?.click()
|
||||
}
|
||||
|
||||
function choosePdf(): void {
|
||||
pdfInput.value?.click()
|
||||
function chooseShortPdf(): void {
|
||||
shortPdfInput.value?.click()
|
||||
}
|
||||
|
||||
function chooseLongPdf(): void {
|
||||
longPdfInput.value?.click()
|
||||
}
|
||||
|
||||
async function handleIconImageChange(changeEvent: Event): Promise<void> {
|
||||
|
|
@ -107,7 +122,10 @@ async function handleIconImageChange(changeEvent: Event): Promise<void> {
|
|||
|
||||
savedMessage.value = null
|
||||
iconImageStatus.value = null
|
||||
const uploaded = await elementsStore.uploadElementIconImage(elementId.value, selectedFile)
|
||||
const uploaded = await elementsStore.uploadElementIconImage(
|
||||
elementId.value,
|
||||
selectedFile,
|
||||
)
|
||||
resetInput(changeEvent)
|
||||
|
||||
if (uploaded) {
|
||||
|
|
@ -115,19 +133,41 @@ async function handleIconImageChange(changeEvent: Event): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
async function handlePdfChange(changeEvent: Event): Promise<void> {
|
||||
async function handleShortPdfChange(changeEvent: Event): Promise<void> {
|
||||
const selectedFile = selectedInputFile(changeEvent)
|
||||
if (selectedFile === null || elementId.value === '') {
|
||||
return
|
||||
}
|
||||
|
||||
savedMessage.value = null
|
||||
pdfStatus.value = null
|
||||
const uploaded = await elementsStore.uploadElementPdf(elementId.value, selectedFile)
|
||||
shortPdfStatus.value = null
|
||||
const uploaded = await elementsStore.uploadElementShortPdf(
|
||||
elementId.value,
|
||||
selectedFile,
|
||||
)
|
||||
resetInput(changeEvent)
|
||||
|
||||
if (uploaded) {
|
||||
pdfStatus.value = 'PDF updated'
|
||||
shortPdfStatus.value = 'Short PDF updated'
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLongPdfChange(changeEvent: Event): Promise<void> {
|
||||
const selectedFile = selectedInputFile(changeEvent)
|
||||
if (selectedFile === null || elementId.value === '') {
|
||||
return
|
||||
}
|
||||
|
||||
savedMessage.value = null
|
||||
longPdfStatus.value = null
|
||||
const uploaded = await elementsStore.uploadElementLongPdf(
|
||||
elementId.value,
|
||||
selectedFile,
|
||||
)
|
||||
resetInput(changeEvent)
|
||||
|
||||
if (uploaded) {
|
||||
longPdfStatus.value = 'Long PDF updated'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,16 +184,29 @@ async function handleRemoveIconImage(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleRemovePdf(): Promise<void> {
|
||||
async function handleRemoveShortPdf(): Promise<void> {
|
||||
if (elementId.value === '') {
|
||||
return
|
||||
}
|
||||
|
||||
savedMessage.value = null
|
||||
pdfStatus.value = null
|
||||
const removed = await elementsStore.clearElementPdf(elementId.value)
|
||||
shortPdfStatus.value = null
|
||||
const removed = await elementsStore.clearElementShortPdf(elementId.value)
|
||||
if (removed) {
|
||||
pdfStatus.value = 'PDF removed'
|
||||
shortPdfStatus.value = 'Short PDF removed'
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRemoveLongPdf(): Promise<void> {
|
||||
if (elementId.value === '') {
|
||||
return
|
||||
}
|
||||
|
||||
savedMessage.value = null
|
||||
longPdfStatus.value = null
|
||||
const removed = await elementsStore.clearElementLongPdf(elementId.value)
|
||||
if (removed) {
|
||||
longPdfStatus.value = 'Long PDF removed'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +239,9 @@ function resetInput(changeEvent: Event): void {
|
|||
<h1 class="admin-element-page__heading">Edit Element</h1>
|
||||
</header>
|
||||
|
||||
<p v-if="isLoading" class="admin-element-page__status">Loading element...</p>
|
||||
<p v-if="isLoading" class="admin-element-page__status">
|
||||
Loading element...
|
||||
</p>
|
||||
<p
|
||||
v-else-if="error"
|
||||
class="admin-element-page__status admin-element-page__status--error"
|
||||
|
|
@ -194,7 +249,11 @@ function resetInput(changeEvent: Event): void {
|
|||
>
|
||||
{{ error }}
|
||||
</p>
|
||||
<form v-else-if="element" class="admin-element-page__form" @submit.prevent="handleSubmit">
|
||||
<form
|
||||
v-else-if="element"
|
||||
class="admin-element-page__form"
|
||||
@submit.prevent="handleSubmit"
|
||||
>
|
||||
<label class="admin-element-page__field">
|
||||
<span class="admin-element-page__label">Title</span>
|
||||
<input
|
||||
|
|
@ -255,7 +314,10 @@ function resetInput(changeEvent: Event): void {
|
|||
</div>
|
||||
<p
|
||||
v-if="iconImageStatus !== null"
|
||||
class="admin-element-page__status admin-element-page__status--inline"
|
||||
:class="[
|
||||
'admin-element-page__status',
|
||||
'admin-element-page__status--inline',
|
||||
]"
|
||||
data-cy="admin-element-icon-image-status"
|
||||
aria-live="polite"
|
||||
>
|
||||
|
|
@ -274,53 +336,110 @@ function resetInput(changeEvent: Event): void {
|
|||
</label>
|
||||
|
||||
<section class="admin-element-page__media-field">
|
||||
<span class="admin-element-page__label">PDF</span>
|
||||
<span class="admin-element-page__label">Short PDF</span>
|
||||
<a
|
||||
v-if="element.pdfPath !== null"
|
||||
:href="element.pdfPath"
|
||||
v-if="element.shortPdfPath !== null"
|
||||
:href="element.shortPdfPath"
|
||||
class="admin-element-page__pdf-link"
|
||||
data-cy="admin-element-current-pdf"
|
||||
data-cy="admin-element-current-short-pdf"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View current PDF
|
||||
View current short PDF
|
||||
</a>
|
||||
<p v-else class="admin-element-page__empty-media">No PDF</p>
|
||||
<p v-else class="admin-element-page__empty-media">No short PDF</p>
|
||||
<div class="admin-element-page__media-actions">
|
||||
<input
|
||||
ref="pdfInput"
|
||||
ref="shortPdfInput"
|
||||
class="admin-element-page__file-input"
|
||||
data-cy="admin-element-pdf-input"
|
||||
data-cy="admin-element-short-pdf-input"
|
||||
type="file"
|
||||
accept="application/pdf"
|
||||
:disabled="isUploadingPdf"
|
||||
@change="handlePdfChange"
|
||||
:disabled="isUploadingShortPdf"
|
||||
@change="handleShortPdfChange"
|
||||
/>
|
||||
<button
|
||||
class="admin-element-page__secondary-button"
|
||||
type="button"
|
||||
:disabled="isUploadingPdf"
|
||||
@click="choosePdf"
|
||||
:disabled="isUploadingShortPdf"
|
||||
@click="chooseShortPdf"
|
||||
>
|
||||
{{ isUploadingPdf ? 'Uploading...' : 'Choose PDF' }}
|
||||
{{ isUploadingShortPdf ? 'Uploading...' : 'Choose short PDF' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="element.pdfPath !== null"
|
||||
v-if="element.shortPdfPath !== null"
|
||||
class="admin-element-page__secondary-button"
|
||||
type="button"
|
||||
:disabled="isSaving || isUploadingPdf"
|
||||
@click="handleRemovePdf"
|
||||
:disabled="isSaving || isUploadingShortPdf"
|
||||
@click="handleRemoveShortPdf"
|
||||
>
|
||||
Remove PDF
|
||||
Remove short PDF
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
v-if="pdfStatus !== null"
|
||||
class="admin-element-page__status admin-element-page__status--inline"
|
||||
data-cy="admin-element-pdf-status"
|
||||
v-if="shortPdfStatus !== null"
|
||||
:class="[
|
||||
'admin-element-page__status',
|
||||
'admin-element-page__status--inline',
|
||||
]"
|
||||
data-cy="admin-element-short-pdf-status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{ pdfStatus }}
|
||||
{{ shortPdfStatus }}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="admin-element-page__media-field">
|
||||
<span class="admin-element-page__label">Long PDF</span>
|
||||
<a
|
||||
v-if="element.longPdfPath !== null"
|
||||
:href="element.longPdfPath"
|
||||
class="admin-element-page__pdf-link"
|
||||
data-cy="admin-element-current-long-pdf"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View current long PDF
|
||||
</a>
|
||||
<p v-else class="admin-element-page__empty-media">No long PDF</p>
|
||||
<div class="admin-element-page__media-actions">
|
||||
<input
|
||||
ref="longPdfInput"
|
||||
class="admin-element-page__file-input"
|
||||
data-cy="admin-element-long-pdf-input"
|
||||
type="file"
|
||||
accept="application/pdf"
|
||||
:disabled="isUploadingLongPdf"
|
||||
@change="handleLongPdfChange"
|
||||
/>
|
||||
<button
|
||||
class="admin-element-page__secondary-button"
|
||||
type="button"
|
||||
:disabled="isUploadingLongPdf"
|
||||
@click="chooseLongPdf"
|
||||
>
|
||||
{{ isUploadingLongPdf ? 'Uploading...' : 'Choose long PDF' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="element.longPdfPath !== null"
|
||||
class="admin-element-page__secondary-button"
|
||||
type="button"
|
||||
:disabled="isSaving || isUploadingLongPdf"
|
||||
@click="handleRemoveLongPdf"
|
||||
>
|
||||
Remove long PDF
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
v-if="longPdfStatus !== null"
|
||||
:class="[
|
||||
'admin-element-page__status',
|
||||
'admin-element-page__status--inline',
|
||||
]"
|
||||
data-cy="admin-element-long-pdf-status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{ longPdfStatus }}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -345,7 +464,10 @@ function resetInput(changeEvent: Event): void {
|
|||
</button>
|
||||
<p
|
||||
v-if="statusMessage !== null"
|
||||
:class="['admin-element-page__status', 'admin-element-page__status--inline']"
|
||||
:class="[
|
||||
'admin-element-page__status',
|
||||
'admin-element-page__status--inline',
|
||||
]"
|
||||
data-cy="admin-element-status"
|
||||
aria-live="polite"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -242,15 +242,29 @@ function isShortYoutubeHost(hostname: string): boolean {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="element.pdfPath !== null" class="element-page__actions">
|
||||
<div
|
||||
v-if="element.shortPdfPath !== null || element.longPdfPath !== null"
|
||||
class="element-page__actions"
|
||||
>
|
||||
<a
|
||||
:href="element.pdfPath"
|
||||
v-if="element.shortPdfPath !== null"
|
||||
:href="element.shortPdfPath"
|
||||
class="element-page__pdf-link"
|
||||
data-cy="element-pdf-link"
|
||||
data-cy="element-short-pdf-link"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View PDF
|
||||
View Short PDF
|
||||
</a>
|
||||
<a
|
||||
v-if="element.longPdfPath !== null"
|
||||
:href="element.longPdfPath"
|
||||
class="element-page__pdf-link"
|
||||
data-cy="element-long-pdf-link"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View Long PDF
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -360,7 +374,9 @@ function isShortYoutubeHost(hostname: string): boolean {
|
|||
|
||||
.element-page__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue