Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Host/Strata - UI: Validate upload docs #435

Merged
merged 2 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions strr-base-web/app/locales/en-CA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,12 @@ export default {
},
docUpload: {
fileSize: {
title: 'Error Uploading Document',
description: 'File size too large. Please only upload files less than 10mb.'
title: 'File Too Large',
description: 'The selected file exceeds the maximum allowed size of 10 MB. Please choose a smaller file.'
},
fileType: {
title: 'Invalid File Type',
description: 'Only PDF files are supported. Please upload a PDF document.'
},
generic: {
title: 'Error Uploading Document',
Expand Down
4 changes: 2 additions & 2 deletions strr-host-pm-web/app/components/document/list/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ defineEmits<{
? 'i-heroicons-arrow-path-20-solid'
: 'i-mdi-check-circle'
"
class="size-6"
class="size-6 shrink-0"
:class="{
'animate-spin': doc.loading,
'text-green-500': !doc.loading
}"
/>
<div class="flex flex-col">
<span class="text-sm font-bold">{{ $t(`form.pr.docType.${doc.type}`) }}</span>
<span>{{ doc.name }}</span>
<span class="line-clamp-1">{{ doc.name }}</span>
</div>
</div>
</div>
Expand Down
62 changes: 0 additions & 62 deletions strr-host-pm-web/app/components/document/upload/Button.vue
thorwolpert marked this conversation as resolved.
Show resolved Hide resolved

This file was deleted.

43 changes: 34 additions & 9 deletions strr-host-pm-web/app/components/document/upload/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ const props = defineProps({
size: { type: String, default: 'lg' },
helpId: { type: String, default: undefined },
ariaLabel: { type: String, default: undefined },
accept: { type: String, default: undefined },
error: { type: Boolean, default: false }
accept: { type: String, default: undefined }, // e.g., 'application/pdf, image/png'
error: { type: Boolean, default: false },
maxFileSize: { type: Number, default: 10 * 1024 * 1024 } // in bytes - default 10mb
})

const docStore = useDocumentStore()

const emit = defineEmits<{
change: [any]
change: [File]
cancel: [void]
error: ['fileSize' | 'fileType']
reset: [void]
}>()

const { open, onChange, onCancel, reset } = useFileDialog({
Expand All @@ -31,9 +34,34 @@ onCancel(() => {

onChange((files) => {
const file = files?.[0]
if (file) {
emit('change', file)

if (!file) {
emit('reset') // cleanup side effects (selected doc type)
reset() // cleanup useFileDialog
return
}

// validate file type
if (props.accept) {
const acceptedTypes = props.accept.split(',').map(type => type.trim())
if (!acceptedTypes.includes(file.type)) {
emit('error', 'fileType')
emit('reset')
reset()
return
}
}

// validate file size
if (file.size > props.maxFileSize) {
emit('error', 'fileSize')
emit('reset')
reset()
return
}

emit('change', file)
emit('reset')
reset()
})
</script>
Expand All @@ -56,14 +84,11 @@ onChange((files) => {
:aria-invalid="isInvalid"
value-attribute="value"
:aria-describedby="helpId"
:ui-menu="{
label: true ? 'text-gray-900' : !!error? 'text-red-600': 'text-gray-700'
}"
class="w-full"
@change="open()"
>
<template #label>
<span>{{ label }}</span>
<span :class="!!error ? 'text-red-600' : 'text-gray-700'">{{ label }}</span>
</template>
</USelectMenu>
</div>
Expand Down
9 changes: 7 additions & 2 deletions strr-host-pm-web/app/components/form/AddDocuments/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const config = useRuntimeConfig().public
const reqStore = usePropertyReqStore()
const docStore = useDocumentStore()
const propStore = useHostPropertyStore()
const strrModal = useStrrModals()

const props = defineProps<{ isComplete: boolean }>()

Expand Down Expand Up @@ -101,7 +102,7 @@ onMounted(async () => {
:title="$t('label.fileUpload')"
:error="isComplete && hasFormErrors(docFormRef, ['documentUpload'])"
>
<div class="space-y-5">
<div class="max-w-bcGovInput space-y-5">
<span aria-hidden="true">{{ $t('text.uploadReqDocs') }}</span>
<UFormGroup
name="documentUpload"
Expand All @@ -114,9 +115,13 @@ onMounted(async () => {
:error="isComplete && hasFormErrors(docFormRef, ['documentUpload'])"
:is-required="docStore.requiredDocs.length > 0"
:help-id="docUploadHelpId"
accept=".pdf"
accept="application/pdf"
@change="docStore.addStoredDocument"
@cancel="docStore.selectedDocType = undefined"
@error="e => strrModal.openErrorModal(
$t(`error.docUpload.${e}.title`), $t(`error.docUpload.${e}.description`), false
)"
@reset="docStore.selectedDocType = undefined"
/>

<template #help>
Expand Down
6 changes: 0 additions & 6 deletions strr-host-pm-web/app/stores/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,6 @@ export const useDocumentStore = defineStore('host/document', () => {
]

async function addStoredDocument (doc: File): Promise<void> {
const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10mb
if (doc.size > MAX_FILE_SIZE) {
strrModal.openErrorModal(t('error.docUpload.fileSize.title'), t('error.docUpload.fileSize.description'), false)
return
}

const uiDoc: UiDocument = {
file: doc,
apiDoc: {} as ApiDocument,
Expand Down
2 changes: 1 addition & 1 deletion strr-host-pm-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "strr-host-pm-web",
"private": true,
"type": "module",
"version": "0.0.27",
"version": "0.0.28",
"scripts": {
"build-check": "nuxt build",
"build": "nuxt generate",
Expand Down
2 changes: 1 addition & 1 deletion strr-strata-web/app/components/document/list/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ defineEmits<{
/>
<div class="flex flex-col">
<span class="text-sm font-bold">{{ $t(`docType.${doc.type}`) }}</span>
<span>{{ doc.name }}</span>
<span class="line-clamp-1">{{ doc.name }}</span>
</div>
</div>
</div>
Expand Down
63 changes: 57 additions & 6 deletions strr-strata-web/app/components/document/upload/Button.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,71 @@
<script setup lang="ts">
defineProps({
const props = defineProps({
id: { type: String, required: true },
isDisabled: { type: Boolean, default: false },
isRequired: { type: Boolean, default: false },
isInvalid: { type: Boolean, default: false },
label: { type: String, default: '' },
helpId: { type: String, default: undefined },
ariaLabel: { type: String, default: undefined },
accept: { type: String, default: undefined },
accept: { type: String, default: undefined }, // e.g., 'application/pdf, image/png'
multiple: { type: Boolean, default: false },
directory: { type: Boolean, default: false }
directory: { type: Boolean, default: false },
maxFileSize: { type: Number, default: 10 * 1024 * 1024 } // in bytes - default 10mb
})

defineEmits<{
change: [files: FileList | null]
const emit = defineEmits<{
change: [files: File[]]
cancel: [void]
error: [Array<{ file: File, reason: 'fileType' | 'fileSize' }>]
reset: [void]
}>()

const inputRef = useTemplateRef('inputRef')

function onChange (e: Event) {
const files = (e.target as HTMLInputElement).files

if (!files || files.length === 0) {
emit('reset')
return
}

const validFiles: File[] = []
const invalidFiles: Array<{ file: File, reason: 'fileType' | 'fileSize' }> = []

const acceptedTypes: string[] = props.accept ? props.accept.split(',').map(type => type.trim()) : []

for (const file of files) {
// validate file type
if (acceptedTypes.length && !acceptedTypes.includes(file.type)) {
invalidFiles.push({ file, reason: 'fileType' })
continue
}

// validate file size
if (file.size > props.maxFileSize) {
invalidFiles.push({ file, reason: 'fileSize' })
continue
}

validFiles.push(file)
}

if (invalidFiles.length > 0) {
emit('error', invalidFiles)
}

if (validFiles.length > 0) {
emit('change', validFiles)
}

// TODO: assign valid files to file input or maybe use aria-describedby ?
// reset to remove invalid files, this currently also removes valid files as well so will need some improvement
if (inputRef.value) {
inputRef.value.value = ''
}
emit('reset')
}
</script>
<template>
<div
Expand Down Expand Up @@ -44,6 +94,7 @@ defineEmits<{
</label>
<input
id="file-input"
ref="inputRef"
type="file"
class="absolute size-full cursor-pointer opacity-0 ring-0 focus:outline-none focus:ring-0"
:accept
Expand All @@ -55,7 +106,7 @@ defineEmits<{
:aria-describedby="helpId"
:disabled="isDisabled"
@cancel="$emit('cancel')"
@change="(e) => $emit('change', (e.target as HTMLInputElement).files)"
@change="onChange"
>
</div>
</div>
Expand Down
70 changes: 0 additions & 70 deletions strr-strata-web/app/components/document/upload/Select.vue
kialj876 marked this conversation as resolved.
Show resolved Hide resolved

This file was deleted.

Loading
Loading