feat(inboxes): add app store reviews channel

This commit is contained in:
Muhsin
2026-05-21 08:21:35 +04:00
parent 27f2c2b392
commit 572d6d2e40
30 changed files with 833 additions and 9 deletions
@@ -12,6 +12,7 @@ import Telegram from './channels/Telegram.vue';
import Instagram from './channels/Instagram.vue';
import Tiktok from './channels/Tiktok.vue';
import Voice from './channels/Voice.vue';
import AppStore from './channels/AppStore.vue';
const channelViewList = {
facebook: Facebook,
@@ -25,6 +26,7 @@ const channelViewList = {
telegram: Telegram,
instagram: Instagram,
tiktok: Tiktok,
app_store: AppStore,
voice: Voice,
};
@@ -77,6 +77,12 @@ const channelList = computed(() => {
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.INSTAGRAM.DESCRIPTION'),
icon: 'i-woot-instagram',
},
{
key: 'app_store',
title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.APP_STORE.TITLE'),
description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.APP_STORE.DESCRIPTION'),
icon: 'i-ri-app-store-line',
},
];
if (hasTiktokConfigured.value) {
@@ -249,6 +249,13 @@ export default {
];
}
if (this.isAnAppStoreChannel) {
const unsupportedKeys = ['business-hours', 'csat', 'bot-configuration'];
visibleToAllChannelTabs = visibleToAllChannelTabs.filter(
tab => !unsupportedKeys.includes(tab.key)
);
}
return visibleToAllChannelTabs;
},
currentInboxId() {
@@ -282,6 +289,9 @@ export default {
if (this.isAnEmailChannel) {
return `${this.inbox.name} (${this.inbox.email})`;
}
if (this.isAnAppStoreChannel && this.inbox.app_id) {
return `${this.inbox.name} (${this.inbox.app_id})`;
}
return this.inbox.name;
},
canLocktoSingleConversation() {
@@ -293,6 +303,7 @@ export default {
this.isAnInstagramChannel ||
this.isALineChannel ||
this.isATiktokChannel ||
this.isAnAppStoreChannel ||
this.isATelegramChannel
);
},
@@ -829,6 +840,7 @@ export default {
</SettingsFieldSection>
<SettingsFieldSection
v-if="!isAnAppStoreChannel"
:label="$t('INBOX_MGMT.HELP_CENTER.LABEL')"
:help-text="$t('INBOX_MGMT.HELP_CENTER.SUB_TEXT')"
>
@@ -1128,6 +1140,7 @@ export default {
</SettingsAccordion>
<SettingsAccordion
v-if="!isAnAppStoreChannel"
:title="$t('INBOX_MGMT.CHANNEL_PREFERENCES')"
class="mt-6"
>
@@ -0,0 +1,176 @@
<script>
import { mapGetters } from 'vuex';
import { useVuelidate } from '@vuelidate/core';
import { required } from '@vuelidate/validators';
import { useAlert } from 'dashboard/composables';
import router from '../../../../index';
import PageHeader from '../../SettingsSubPageHeader.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
PageHeader,
NextButton,
},
setup() {
return { v$: useVuelidate() };
},
data() {
return {
channelName: '',
appId: '',
issuerId: '',
keyId: '',
privateKey: '',
};
},
computed: {
...mapGetters({
uiFlags: 'inboxes/getUIFlags',
}),
},
validations: {
channelName: { required },
appId: { required },
issuerId: { required },
keyId: { required },
privateKey: { required },
},
methods: {
async createChannel() {
this.v$.$touch();
if (this.v$.$invalid) return;
try {
const appStoreChannel = await this.$store.dispatch(
'inboxes/createChannel',
{
name: this.channelName?.trim(),
channel: {
type: 'app_store',
app_id: this.appId.trim(),
issuer_id: this.issuerId.trim(),
key_id: this.keyId.trim(),
private_key: this.privateKey.trim(),
},
}
);
router.replace({
name: 'settings_inboxes_add_agents',
params: {
page: 'new',
inbox_id: appStoreChannel.id,
},
});
} catch (error) {
useAlert(
error.message || this.$t('INBOX_MGMT.ADD.APP_STORE.API.ERROR_MESSAGE')
);
}
},
},
};
</script>
<template>
<div class="h-full w-full p-6 col-span-6">
<PageHeader
:header-title="$t('INBOX_MGMT.ADD.APP_STORE.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.APP_STORE.DESC')"
/>
<form
class="flex flex-wrap flex-col mx-0"
@submit.prevent="createChannel()"
>
<div class="flex-shrink-0 flex-grow-0">
<label :class="{ error: v$.channelName.$error }">
{{ $t('INBOX_MGMT.ADD.APP_STORE.CHANNEL_NAME.LABEL') }}
<input
v-model="channelName"
type="text"
:placeholder="
$t('INBOX_MGMT.ADD.APP_STORE.CHANNEL_NAME.PLACEHOLDER')
"
@blur="v$.channelName.$touch"
/>
<span v-if="v$.channelName.$error" class="message">
{{ $t('INBOX_MGMT.ADD.APP_STORE.CHANNEL_NAME.ERROR') }}
</span>
</label>
</div>
<div class="flex-shrink-0 flex-grow-0">
<label :class="{ error: v$.appId.$error }">
{{ $t('INBOX_MGMT.ADD.APP_STORE.APP_ID.LABEL') }}
<input
v-model="appId"
type="text"
:placeholder="$t('INBOX_MGMT.ADD.APP_STORE.APP_ID.PLACEHOLDER')"
@blur="v$.appId.$touch"
/>
<span v-if="v$.appId.$error" class="message">
{{ $t('INBOX_MGMT.ADD.APP_STORE.APP_ID.ERROR') }}
</span>
</label>
</div>
<div class="flex-shrink-0 flex-grow-0">
<label :class="{ error: v$.issuerId.$error }">
{{ $t('INBOX_MGMT.ADD.APP_STORE.ISSUER_ID.LABEL') }}
<input
v-model="issuerId"
type="text"
:placeholder="$t('INBOX_MGMT.ADD.APP_STORE.ISSUER_ID.PLACEHOLDER')"
@blur="v$.issuerId.$touch"
/>
<span v-if="v$.issuerId.$error" class="message">
{{ $t('INBOX_MGMT.ADD.APP_STORE.ISSUER_ID.ERROR') }}
</span>
</label>
</div>
<div class="flex-shrink-0 flex-grow-0">
<label :class="{ error: v$.keyId.$error }">
{{ $t('INBOX_MGMT.ADD.APP_STORE.KEY_ID.LABEL') }}
<input
v-model="keyId"
type="text"
:placeholder="$t('INBOX_MGMT.ADD.APP_STORE.KEY_ID.PLACEHOLDER')"
@blur="v$.keyId.$touch"
/>
<span v-if="v$.keyId.$error" class="message">
{{ $t('INBOX_MGMT.ADD.APP_STORE.KEY_ID.ERROR') }}
</span>
</label>
</div>
<div class="flex-shrink-0 flex-grow-0">
<label :class="{ error: v$.privateKey.$error }">
{{ $t('INBOX_MGMT.ADD.APP_STORE.PRIVATE_KEY.LABEL') }}
<textarea
v-model="privateKey"
rows="8"
:placeholder="
$t('INBOX_MGMT.ADD.APP_STORE.PRIVATE_KEY.PLACEHOLDER')
"
@blur="v$.privateKey.$touch"
/>
<span v-if="v$.privateKey.$error" class="message">
{{ $t('INBOX_MGMT.ADD.APP_STORE.PRIVATE_KEY.ERROR') }}
</span>
</label>
</div>
<div class="w-full mt-4">
<NextButton
:is-loading="uiFlags.isCreating"
type="submit"
solid
blue
:label="$t('INBOX_MGMT.ADD.APP_STORE.SUBMIT_BUTTON')"
/>
</div>
</form>
</div>
</template>
@@ -34,6 +34,7 @@ const i18nMap = {
'Channel::Api': 'API',
'Channel::Instagram': 'INSTAGRAM',
'Channel::Tiktok': 'TIKTOK',
'Channel::AppStore': 'APP_STORE',
};
const twilioChannelName = () => {