Skip to content

Commit 4e10f95

Browse files
committed
add export modal to prevent firefox popup blocking behaviour
1 parent 959cad8 commit 4e10f95

File tree

4 files changed

+88
-5
lines changed

4 files changed

+88
-5
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<script setup lang="ts">
2+
import {
3+
ArrowDownTrayIcon,
4+
CheckCircleIcon,
5+
XMarkIcon,
6+
} from '@heroicons/vue/20/solid';
7+
import { Modal, PrimaryButton } from '@/packages/ui/src';
8+
const props = defineProps<{
9+
exportUrl: string | null;
10+
}>();
11+
12+
const showExportModal = defineModel('show', { default: false });
13+
14+
function downloadCurrentExport() {
15+
if (props.exportUrl) {
16+
window.open(props.exportUrl, '_blank')?.focus();
17+
}
18+
}
19+
</script>
20+
21+
<template>
22+
<Modal
23+
closeable
24+
max-width="lg"
25+
@close="showExportModal = false"
26+
:show="showExportModal">
27+
<button
28+
class="text-text-tertiary w-6 mx-auto absolute focus-visible:outline-none focus-visible:ring-2 rounded-full focus-visible:ring-white/80 transition focus-visible:text-text-primary hover:text-text-primary top-2 right-2">
29+
<XMarkIcon @click="showExportModal = false"></XMarkIcon>
30+
</button>
31+
<div class="text-center text-text-primary py-6">
32+
<div
33+
class="flex items-center font-semibold text-lg justify-center space-x-2 pb-2">
34+
<CheckCircleIcon
35+
class="text-text-tertiary w-6"></CheckCircleIcon>
36+
<span> Export Successful! </span>
37+
</div>
38+
<div class="text-center text-sm max-w-64 mx-auto">
39+
<p class="pb-5">
40+
Your export is ready, you can download it with the button
41+
below.
42+
</p>
43+
<PrimaryButton
44+
:icon="ArrowDownTrayIcon"
45+
@click="downloadCurrentExport"
46+
>Download</PrimaryButton
47+
>
48+
</div>
49+
</div>
50+
</Modal>
51+
</template>
52+
53+
<style scoped></style>

resources/js/Pages/Reporting.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,20 @@ async function downloadExport(format: ExportFormat) {
198198
'Export successful',
199199
'Export failed'
200200
);
201+
201202
if (response?.download_url) {
202-
window.open(response.download_url as string, '_blank')?.focus();
203+
showExportModal.value = true;
204+
exportUrl.value = response.download_url as string;
203205
}
204206
}
205207
}
206208
const { getNameForReportingRowEntry, emptyPlaceholder } = useReportingStore();
207209
import { useProjectsStore } from '@/utils/useProjects';
210+
import ReportingExportModal from '@/Components/Common/Reporting/ReportingExportModal.vue';
208211
const projectsStore = useProjectsStore();
209212
const { projects } = storeToRefs(projectsStore);
213+
const showExportModal = ref(false);
214+
const exportUrl = ref<string | null>(null);
210215
211216
const groupedPieChartData = computed(() => {
212217
return (
@@ -274,6 +279,9 @@ const tableData = computed(() => {
274279
title="Reporting"
275280
data-testid="reporting_view"
276281
class="overflow-hidden">
282+
<ReportingExportModal
283+
v-model:show="showExportModal"
284+
:exportUrl="exportUrl"></ReportingExportModal>
277285
<MainContainer
278286
class="py-3 sm:py-5 border-b border-default-background-separator flex justify-between items-center">
279287
<div class="flex items-center space-x-3 sm:space-x-6">

resources/js/Pages/ReportingDetailed.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import { useNotificationsStore } from '@/utils/notification';
6767
import TimeEntryMassActionRow from '@/packages/ui/src/TimeEntry/TimeEntryMassActionRow.vue';
6868
import { isAllowedToPerformPremiumAction } from '@/utils/billing';
6969
import { canCreateProjects } from '@/utils/permissions';
70+
import ReportingExportModal from '@/Components/Common/Reporting/ReportingExportModal.vue';
7071
7172
const startDate = useSessionStorage<string>(
7273
'reporting-start-date',
@@ -167,6 +168,9 @@ const { clients } = storeToRefs(clientStore);
167168
168169
const selectedTimeEntries = ref<TimeEntry[]>([]);
169170
171+
const showExportModal = ref(false);
172+
const exportUrl = ref<string | null>(null);
173+
170174
async function createTag(name: string) {
171175
return await useTagsStore().createTag(name);
172176
}
@@ -234,7 +238,8 @@ async function downloadExport(format: ExportFormat) {
234238
'Export failed'
235239
);
236240
if (response?.download_url) {
237-
window.open(response.download_url as string, '_blank')?.focus();
241+
showExportModal.value = true;
242+
exportUrl.value = response.download_url as string;
238243
}
239244
}
240245
}
@@ -245,6 +250,9 @@ async function downloadExport(format: ExportFormat) {
245250
title="Reporting"
246251
data-testid="reporting_view"
247252
class="overflow-hidden">
253+
<ReportingExportModal
254+
v-model:show="showExportModal"
255+
:exportUrl="exportUrl"></ReportingExportModal>
248256
<MainContainer
249257
class="py-3 sm:py-5 border-b border-default-background-separator flex justify-between items-center">
250258
<div class="flex items-center space-x-3 sm:space-x-6">

resources/js/packages/ui/src/Buttons/PrimaryButton.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
<script setup lang="ts">
22
import type { HtmlButtonType } from '@/types/dom';
33
import LoadingSpinner from '../LoadingSpinner.vue';
4+
import type { Component } from 'vue';
5+
import { twMerge } from 'tailwind-merge';
46
5-
withDefaults(
7+
const props = withDefaults(
68
defineProps<{
79
type: HtmlButtonType;
10+
icon?: Component;
811
loading: boolean;
912
}>(),
1013
{
@@ -19,7 +22,18 @@ withDefaults(
1922
:type="type"
2023
:disabled="loading"
2124
class="inline-flex items-center px-2 sm:px-3 py-1 sm:py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-xs sm:text-sm text-white hover:bg-accent-300/20 active:bg-accent-300/20 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
22-
<LoadingSpinner v-if="loading"></LoadingSpinner>
23-
<slot />
25+
<span
26+
:class="
27+
twMerge('flex items-center ', props.icon ? 'space-x-1.5' : '')
28+
">
29+
<LoadingSpinner v-if="loading"></LoadingSpinner>
30+
<component
31+
v-if="props.icon && !loading"
32+
:is="props.icon"
33+
class="text-text-secondary w-4 -ml-0.5 mr-1"></component>
34+
<span>
35+
<slot />
36+
</span>
37+
</span>
2438
</button>
2539
</template>

0 commit comments

Comments
 (0)