diff --git a/web/pages/search-messages/index.vue b/web/pages/search-messages/index.vue
index 67062625..f5eda862 100644
--- a/web/pages/search-messages/index.vue
+++ b/web/pages/search-messages/index.vue
@@ -95,8 +95,11 @@
class="py-5"
@click="fetchMessages(true)"
>
- {{ mdiMagnify }}
- Search Messages
+ {{
+ mdiMagnify
+ }}
+ SEARCH
+ Search Messages
@@ -120,8 +123,11 @@
v-bind="attrs"
v-on="on"
>
- {{ mdiDelete }}
- Delete messages
+ {{
+ mdiDelete
+ }}
+ DELETE
+ Delete messages
@@ -139,7 +145,6 @@
:loading="loading"
@click="deleteMessages"
>
- {{ mdiDelete }}
Yes Delete Messages
@@ -147,6 +152,46 @@
+
+
+
+ {{ mdiRefresh }}
+ Resend Messages
+
+
+
+
+ Are you sure you want to resend the
+ {{ selectedMessages.length }} selected messages?
+
+
+ The selected messages will be queued for sending again using
+ the original sender, recipient, and content.
+
+
+
+ Yes Resend Messages
+
+
+ Close
+
+
+
- {{ mdiExport }}
- Export to CSV
+ {{
+ mdiExport
+ }}
+ EXPORT
+ Export to CSV
@@ -280,11 +328,13 @@ import {
mdiCallReceived,
mdiCallMade,
mdiExport,
+ mdiRefresh,
mdiProgressCheck,
mdiAlert,
} from '@mdi/js'
import { AxiosError } from 'axios'
import { DataOptions } from 'vuetify'
+import axios from '~/plugins/axios'
import { ErrorMessages, getErrorMessages } from '~/plugins/errors'
import capitalize from '~/plugins/capitalize'
import {
@@ -317,6 +367,7 @@ export default Vue.extend({
mdiMagnify,
mdiArrowLeft,
mdiExport,
+ mdiRefresh,
mdiAlert,
mdiCheck,
mdiCheckAll,
@@ -328,6 +379,7 @@ export default Vue.extend({
initialLoadComplete: false,
errorTitle: '',
showDeleteDialog: false,
+ showResendDialog: false,
selectedMessages: [] as EntitiesMessage[],
errorMessages: new ErrorMessages(),
formOwners: [],
@@ -360,6 +412,16 @@ export default Vue.extend({
}
},
computed: {
+ canResendSelected(): boolean {
+ return (
+ this.selectedMessages.length > 0 &&
+ this.selectedMessages.every(
+ (message: EntitiesMessage) =>
+ message.type === 'mobile-terminated' &&
+ (message.status === 'expired' || message.status === 'failed'),
+ )
+ )
+ },
phoneNumberSelectItems() {
return this.$store.getters.getPhones.map((phone: EntitiesPhone) => {
return {
@@ -495,6 +557,49 @@ export default Vue.extend({
})
},
+ resendMessages() {
+ this.loading = true
+ Promise.allSettled(
+ this.selectedMessages.map((message) =>
+ axios.post('/v1/messages/send', {
+ from: message.owner,
+ to: message.contact,
+ content: message.content,
+ sim: message.sim,
+ request_id: message.request_id,
+ }),
+ ),
+ )
+ .then((results) => {
+ const failed = results.filter((r) => r.status === 'rejected')
+ if (failed.length === 0) {
+ this.$store.dispatch('addNotification', {
+ message: 'The selected messages have been queued for resending',
+ type: 'success',
+ })
+ this.selectedMessages = []
+ } else if (failed.length === results.length) {
+ this.$store.dispatch('addNotification', {
+ message: 'Error while resending the selected messages',
+ type: 'error',
+ })
+ } else {
+ this.$store.dispatch('addNotification', {
+ message: `${results.length - failed.length} messages resent, ${
+ failed.length
+ } failed`,
+ type: 'warning',
+ })
+ this.selectedMessages = []
+ }
+ })
+ .finally(() => {
+ this.loading = false
+ this.showResendDialog = false
+ this.fetchMessages()
+ })
+ },
+
fetchMessages(reset = false) {
this.loading = true
this.errorMessages = new ErrorMessages()
diff --git a/web/store/index.ts b/web/store/index.ts
index 9077ccf6..4e9e9482 100644
--- a/web/store/index.ts
+++ b/web/store/index.ts
@@ -294,6 +294,7 @@ export type SendMessageRequest = {
to: string
content: string
sim: SIM
+ request_id?: string
}
export const actions = {