Skip to content

Commit

Permalink
feat: enhance file download functionality and refactor UI (#1319)
Browse files Browse the repository at this point in the history
- Add `forceFileDownloadFromURl` utility function for better file download handling.
- Replace `base-link` with `base-button` in `Downloads.vue` for initiating file downloads.
- Introduce `DownloadWorksiteCsv` component to manage async file downloads with a modal UI.
- Update `Work.vue` to integrate `DownloadWorksiteCsv` for processing and downloading CSV files dynamically.
- Refactor and improve user feedback during file download processes.
  • Loading branch information
tabiodun authored Jan 8, 2025
1 parent 7856c12 commit feb981c
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 18 deletions.
78 changes: 78 additions & 0 deletions src/components/downloads/DownloadWorksiteCsv.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script setup lang="ts">
import axios from 'axios';
import { forceFileDownloadFromURl } from '@/utils/downloads';
import Spinner from '@/components/Spinner.vue';
const props = defineProps<{
downloadId: string;
wait?: number;
}>();
const { t } = useI18n();
// Reactive variables for UI feedback
const waitingForFile = ref(true);
const errorMessage = ref<string | null>(null);
const message = ref<string | null>(null);
// Function to download the file
const downloadFile = async (fileId: string) => {
try {
const url = `/files/${fileId}`;
const { data } = await axios.get(url);
const csvUrl = data.csv_url;
forceFileDownloadFromURl(csvUrl, data.filename_original);
waitingForFile.value = false;
message.value = t('~~ Download complete!');
} catch (error) {
console.error('Error downloading file:', error);
errorMessage.value = 'Failed to download the file.';
waitingForFile.value = false;
}
};
onMounted(() => {
const maxAttempts = props.wait || 20; // 20 seconds by default
let attempts = 0;
const interval = setInterval(async () => {
attempts += 1;
try {
const { data } = await axios.get(`/user_downloads/${props.downloadId}`);
if (data.file) {
clearInterval(interval);
await downloadFile(data.file);
} else if (attempts >= maxAttempts) {
clearInterval(interval);
message.value = t('info.processing_download_d');
waitingForFile.value = false;
}
} catch (error) {
console.error('Error checking file status:', error);
clearInterval(interval);
errorMessage.value = 'An error occurred while checking the file status.';
waitingForFile.value = false;
}
}, 1000);
});
</script>

<template>
<div>
<div v-if="waitingForFile">
<p>{{ $t('~~Waiting for the file to be ready for download...') }}</p>
<spinner />
</div>
<div v-else-if="message">
<p v-html="message"></p>
</div>
<div v-else-if="errorMessage">
<p>{{ errorMessage }}</p>
</div>
<!-- Optionally, you can add more UI elements here -->
</div>
</template>

<style scoped>
/* Add your styles here */
</style>
34 changes: 21 additions & 13 deletions src/pages/Downloads.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@
>
<template #file="{ item }">
<div v-if="item.file">
<base-link
:href="item.file.csv_url"
:data-testid="`testDownloads${item.file}Div`"
text-variant="bodysm"
class="px-2"
:download="item.file.filename_original"
>{{ item.file.filename_original }}</base-link
<base-button
:action="() => downloadFile(item.file)"
variant="solid"
class="ml-3"
size="small"
>
</div>
<div v-else>
{{ item.status }}
{{ $t('actions.download') }}
</base-button>
</div>
</template>
</AjaxTable>
Expand All @@ -34,28 +31,39 @@ import moment from 'moment/moment';
import { makeTableColumns } from '@/utils/table';
import AjaxTable from '@/components/AjaxTable.vue';
import BaseText from '@/components/BaseText.vue';
import BaseButton from '@/components/BaseButton.vue';
import axios from 'axios';
import { forceFileDownloadFromURl } from '@/utils/downloads';
export default defineComponent({
name: 'Downloads',
components: { BaseText, AjaxTable },
components: { BaseButton, BaseText, AjaxTable },
setup() {
const tableUrl = `${import.meta.env.VITE_APP_API_BASE_URL}/user_downloads`;
const columns = makeTableColumns([
['created_at', '1fr', 'Created At'],
['file', '1fr', 'Status'],
['status', '1fr', 'Status'],
['file', '1fr', ''],
]);
for (const column of columns) {
// overwrite default column title from `Name` to `Organization`
if (column.key === 'created_at') {
column.transformer = (field) => {
return moment(field).format('ddd MMMM Do YYYY');
return moment(field).format('ddd MMMM Do YYYY h:mm:ss a');
};
}
}
const downloadFile = async (file: any) => {
const { data } = await axios.get(`/files/${file}`);
const url = data.csv_url;
return forceFileDownloadFromURl(url, data.filename_original);
};
return {
tableUrl,
columns,
downloadFile,
};
},
});
Expand Down
14 changes: 9 additions & 5 deletions src/pages/Work.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1037,9 +1037,9 @@ import BaseButton from '@/components/BaseButton.vue';
import AjaxTable from '@/components/AjaxTable.vue';
import { momentFromNow } from '@/filters';
import User from '@/models/User';
import { string } from 'zod';
import _ from 'lodash';
import Spinner from '@/components/Spinner.vue';
import DownloadWorksiteCsv from '@/components/downloads/DownloadWorksiteCsv.vue';
export default defineComponent({
name: 'Work',
Expand Down Expand Up @@ -1892,14 +1892,18 @@ export default defineComponent({
}/worksites_download/download_csv`,
{
params,
headers: { Accept: 'text/csv' },
responseType: 'blob',
},
);
if (response.status === 202) {
await confirm({
await component({
title: t('info.processing_download'),
content: t('info.processing_download_d'),
component: DownloadWorksiteCsv,
classes: 'w-full overflow-auto p-3',
modalClasses: 'bg-white max-w-4xl shadow',
props: {
downloadId: response.data.download_id,
wait: Number(20),
},
});
} else if (response.status === 400) {
const result = await confirm({
Expand Down
13 changes: 13 additions & 0 deletions src/utils/downloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,16 @@ export function downloadCSVFile(csvContent: string, fileName: string) {
link.click();
link.remove();
}

export function forceFileDownloadFromURl(url: string, fileName = 'unknown') {
const link = document.createElement('a');
link.href = url;
const name = fileName ?? 'unknown';
link.setAttribute('download', name);
document.body.append(link);
link.href = url;
// link.target = '_blank';
link.click();
link.remove();
window.URL.revokeObjectURL(url);
}

0 comments on commit feb981c

Please sign in to comment.