Skip to content

Commit

Permalink
Merge pull request #570 from noi-techpark/improve-send-push-visualiza…
Browse files Browse the repository at this point in the history
…tion

Improve send push visualization
  • Loading branch information
RudiThoeni committed Apr 30, 2024
2 parents 1fc950e + ecb9201 commit 899f6cc
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 233 deletions.
1 change: 1 addition & 0 deletions databrowser/src/config/builder/tourism/pushData.ts
Expand Up @@ -12,5 +12,6 @@ export const pushDataTableCell = (): PropertyConfig => ({
objectMapping: {
id: '_Meta.Id',
type: '_Meta.Type',
publishedOn: 'PublishedOn',
},
});
Expand Up @@ -48,101 +48,26 @@ SPDX-License-Identifier: AGPL-3.0-or-later
</template>

<script setup lang="ts">
import { formatDistanceToNow, format as formatFn } from 'date-fns';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import LoadingIndicator from '../../../../../components/loading/LoadingIndicator.vue';
import IconExclamationMark from '../../../../../components/svg/IconExclamationMark.vue';
import {
DEFAULT_DATE_TIME_FORMAT,
withOdhBaseUrl,
} from '../../../../../config/utils';
import { useApiRead } from '../../../../api/useApi';
import { WithTourismPagination } from '../../../../datasets/pagination/types';
import { OdhPushResponse, PublisherWithPushResult } from './types';
import { useLastPushResponse } from './lastPush';
import { PublisherWithPushResponse } from './types';
import { watch } from 'vue';
const { t } = useI18n();
const props = defineProps<{
id?: string;
pushResults: PublisherWithPushResult[];
id: string;
pushResults: PublisherWithPushResponse[];
}>();
const url = computed(() =>
props.id == null
? undefined
: // Fetch last push info for the given id
withOdhBaseUrl(
`/v1/PushResponse?pagesize=1&pagenumber=1&rawsort=-Date&rawfilter=and(eq(PushObject.Id,'${props.id}'))`
)
);
const { data, error, isLoading, isError } =
useApiRead<WithTourismPagination<OdhPushResponse>>(url);
interface PushResponseData {
state: 'empty' | 'info' | 'ok' | 'error';
id?: string;
date?: string;
dateAgo?: string;
dateFormatted?: string;
message?: string;
}
const pushResponse = computed<PushResponseData>(() => {
// If the push results are available, this means that a push notification has
// been sent. In this case, we show the information from the first push result.
if (props.pushResults != null && props.pushResults.length > 0) {
const pushResult = props.pushResults[0];
return {
state: pushResult.pushResult.success ? 'ok' : 'error',
id: pushResult.pushResult.id,
...buildDateInfo(new Date().toISOString()),
};
}
if (data.value == null) {
return { state: 'empty' };
}
if (data.value.TotalResults === 0) {
return {
state: 'info',
message: 'No data available',
};
}
const odhPushResponse = data.value.Items[0];
// Fetch the last push response
const { pushResponse, isLoading, isError, error, refetch } =
useLastPushResponse(props.id);
return {
state: 'ok',
id: odhPushResponse.Id,
...buildDateInfo(odhPushResponse.Date),
};
});
const buildDateInfo = (dateAsString: string | undefined) => {
if (dateAsString == null) {
return {
date: undefined,
dateAgo: undefined,
dateFormatted: t('components.pushData.lastPushInfo.sentAtUnknown'),
};
}
const date = new Date(dateAsString);
const pushResponseDate = formatFn(date, DEFAULT_DATE_TIME_FORMAT);
const pushResponseDateAgo = formatDistanceToNow(date, {
addSuffix: true,
includeSeconds: true,
});
return {
date: pushResponseDate,
dateAgo: pushResponseDateAgo,
dateFormatted: `${pushResponseDate} (${pushResponseDateAgo})`,
};
};
watch(
() => props.pushResults,
() => refetch()
);
</script>
Expand Up @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
</template>

<script setup lang="ts">
import { reactive, watch } from 'vue';
import { reactive, ref, watch } from 'vue';
import CheckboxCustom from '../../../../../components/checkbox/CheckboxCustom.vue';
import { Publisher } from './types';
Expand All @@ -49,9 +49,30 @@ const toggleAll = (event: boolean) =>
0
);
// Array of selected publishers that is updated when the component properties
// change or when the user selects a publisher
const selectedPublishers = ref<Publisher[]>([]);
// Update the selected publishers when the component properties change
watch(
() => props.publishers,
() =>
(selectedPublishers.value =
props.publishers.length === 1 ? [props.publishers[0]] : [])
);
// Update the selected publishers when the publishers change
const updateSelection = (selected: boolean[]) => {
selectedPublishers.value = props.publishers.filter(
(_, index) => selected[index]
);
emit('selectionChange', selectedPublishers.value);
};
// Update selection when the selected array changes
watch(
() => selected,
() => emit('selectionChange', selected),
{ deep: true }
() => updateSelection(selected),
{ immediate: true, deep: true }
);
</script>
Expand Up @@ -12,21 +12,37 @@ SPDX-License-Identifier: AGPL-3.0-or-later

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
import { usePublisherStore } from '../../../../publisher/publisherStore';
import PushDataPopup from './PushDataPopup.vue';
import { computed } from 'vue';
import { Publisher } from './types';
const props = defineProps<{ id?: string; type?: string }>();
const props = defineProps<{
id?: string;
type?: string;
publishedOn?: string[];
}>();
const { publishers } = storeToRefs(usePublisherStore());
// Compute the publishers with the URL to push data to
const publishersWithUrl = computed(() =>
publishers.value.map<Publisher>((publisher) => ({
id: publisher.id,
name: publisher.name,
url: publisher.buildUrl(props.id, props.type),
}))
);
const publishersWithUrl = computed(() => {
if (props.publishedOn == null || props.publishedOn.length === 0) {
return [];
}
return (
publishers.value
// Use only publishers that are in the publishedOn list
.filter(
(publisher) =>
props.publishedOn?.find((pon) => pon === publisher.id) != null
)
.map<Publisher>((publisher) => ({
id: publisher.id,
name: publisher.name,
url: publisher.buildUrl(props.id, props.type),
}))
);
});
</script>
Expand Up @@ -13,12 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
:class="buttonClasses"
class="flex items-center justify-center gap-2 px-2 py-px"
>
<!-- Dirty workaround to reset component state when popup is re-opened -->
{{ resetComponentState() }}

<span class="line-height-1">
{{ t('components.pushData.sendPushNotifications') }}
</span>
{{ t('components.pushData.sendPushNotifications') }}
<IconStrokedArrowDown
class="h-5 w-5 stroke-current"
:class="{ 'rotate-180': open }"
Expand All @@ -28,130 +23,36 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<template #container>
<PopoverCustomPanel>
<PopoverContentFrame class="max-w-lg">
<div>
<div
class="mb-2 mr-1 text-sm font-bold text-black md:w-auto md:text-base"
>
{{ t('components.pushData.popup.title') }}
</div>
</div>

<div v-if="publishers.length === 0" class="mb-6 flex flex-col gap-2">
<div>
{{ t('components.pushData.popup.noPublishersAvailable') }}
</div>
<div>
{{ t('components.pushData.popup.contactSupport1') }}
<a :href="`mailto:${t('contact.emailSupport')}`">
{{ t('components.pushData.popup.contactSupport2') }}
</a>
</div>
</div>

<div v-else>
<div class="mb-4">
{{ t('components.pushData.popup.selectChannel') }}
</div>

<PublisherSelection
class="mb-5"
:publishers="publishers"
:disabled="isPushed"
@selection-change="updateSelection"
/>

<div class="mb-5">
{{ t('components.pushData.popup.pushSendImmediately') }}
</div>

<ButtonCustom
:variant="Variant.ghost"
:tone="Tone.primary"
:disabled="selectedPublishers.length === 0 || isPushed"
class="mb-4 w-full"
@click="sendPushes"
>
{{
isPushed
? t('components.pushData.popup.buttonAfterSend')
: t('components.pushData.popup.buttonBeforeSend')
}}
</ButtonCustom>

<PushResult :push-results="pushResults" />
</div>
<LastPushInfo :id="id" :push-results="pushResults" />
<PushDataPopupContent :id="id" :publishers="publishers" />
</PopoverContentFrame>
</PopoverCustomPanel>
</template>
</PopoverCustom>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import ButtonCustom from '../../../../../components/button/ButtonCustom.vue';
import { computeButtonClasses } from '../../../../../components/button/styles';
import { Size, Tone, Variant } from '../../../../../components/button/types';
import { Size, Variant } from '../../../../../components/button/types';
import PopoverContentFrame from '../../../../../components/popover/PopoverContentFrame.vue';
import PopoverCustom from '../../../../../components/popover/PopoverCustom.vue';
import PopoverCustomButton from '../../../../../components/popover/PopoverCustomButton.vue';
import PopoverCustomPanel from '../../../../../components/popover/PopoverCustomPanel.vue';
import IconStrokedArrowDown from '../../../../../components/svg/IconStrokedArrowDown.vue';
import { useAuth } from '../../../../auth/store/auth';
import LastPushInfo from './LastPushInfo.vue';
import PublisherSelection from './PublisherSelection.vue';
import PushResult from './PushResult.vue';
import { sendPushNotifications } from './pushNotification';
import { Publisher, PublisherWithPushResult } from './types';
import PushDataPopupContent from './PushDataPopupContent.vue';
import { Publisher } from './types';
const { t } = useI18n();
const props = defineProps<{ id?: string; publishers: Publisher[] }>();
const auth = useAuth();
const disabled = computed(() => !auth.isAuthenticated);
// Array of selected publishers that is updated when the component properties
// change or when the user selects a publisher
const selectedPublishers = ref<Publisher[]>([]);
// Update the selected publishers when the component properties change
watch(
() => props.publishers,
() =>
(selectedPublishers.value =
props.publishers.length === 1 ? [props.publishers[0]] : [])
const disabled = computed(
() => !auth.isAuthenticated || props.publishers.length === 0
);
// Update the selected publishers when the publishers change
const updateSelection = (selected: boolean[]) =>
(selectedPublishers.value = props.publishers.filter(
(_, index) => selected[index]
));
// Keep track whether the push notifications have been sent
// It is not possible to send push notifications when they are already sent, until the popup is closed
const isPushed = ref(false);
// Array of push results that is updated when the push notifications are sent
const pushResults = ref<PublisherWithPushResult[]>([]);
const sendPushes = async () => {
try {
pushResults.value = await sendPushNotifications(selectedPublishers.value);
} catch (err) {
console.error(err);
}
isPushed.value = true;
};
// Reset the component state when the popup is re-opened,
// which enables the user to send push notifications again
const resetComponentState = () => {
isPushed.value = false;
pushResults.value = [];
};
// Compute the button classes
const buttonClasses = computed(() =>
computeButtonClasses({
size: Size.xs,
Expand Down

0 comments on commit 899f6cc

Please sign in to comment.