feat: add composables for migration and update portal settings (#9299)
* feat: setup vuelitdate for vue 2.7 * feat: add all composables * fix: portal settings layout * feat: remove styles * feat: use setup API for ListAllCategories * chore: format ListAllCategories * refactor: add useAlert * feat: add track composable * feat: update map getters * fix: import * feat: update edit portal locales page [wip] * feat: migrate locales page * feat: remove alert message ref * chore: format EditPortalLocales * refactor: use composiiton api for PortalCustomization * refactor: remove color * feat: update PortalSettingsCustomizationForm to use setup syntax * refactor: no need to import defineEmits * refactor: format component * fix: update logic * feat: migrate PortalSettingsBasicForm * refactor: format PortalSettingsBasicForm * refactor: migrate EditPortalCustomization to Vue 2.7 * feat: migrate EditPortalBasic to vue 2.7 * chore: revert changes to EditPortal * fix: portal layout * fix: width * feat: use setup syntax * fix: double border * feat: return track method * refactor: track usage --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex-grow-0 flex-shrink-0 w-full h-full max-w-full bg-white border border-transparent border-solid dark:bg-slate-900 dark:border-transparent md:max-w-2xl"
|
||||
>
|
||||
<h3
|
||||
v-if="$slots.title || title"
|
||||
class="text-lg text-black-900 dark:text-slate-200"
|
||||
>
|
||||
<slot name="title">{{ title }}</slot>
|
||||
</h3>
|
||||
<div
|
||||
class="mx-0 my-4 border-b border-solid border-slate-25 dark:border-slate-800"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<slot name="footer-left" />
|
||||
</div>
|
||||
<div>
|
||||
<slot name="footer-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,256 +1,241 @@
|
||||
<template>
|
||||
<div
|
||||
class="pt-3 bg-white dark:bg-slate-900 h-full border border-solid border-transparent px-6 pb-6 dark:border-transparent w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
|
||||
>
|
||||
<div class="w-full">
|
||||
<h3 class="text-lg text-black-900 dark:text-slate-200">
|
||||
{{
|
||||
$t(
|
||||
'HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.BASIC_SETTINGS_PAGE.TITLE'
|
||||
)
|
||||
}}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="my-4 mx-0 border-b border-solid border-slate-25 dark:border-slate-800"
|
||||
>
|
||||
<div class="w-[65%] flex-shrink-0 flex-grow-0 max-w-[65%]">
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center flex-row">
|
||||
<woot-avatar-uploader
|
||||
ref="imageUpload"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.LOGO.LABEL')"
|
||||
:src="logoUrl"
|
||||
@change="onFileChange"
|
||||
/>
|
||||
<div v-if="showDeleteButton" class="avatar-delete-btn">
|
||||
<woot-button
|
||||
type="button"
|
||||
color-scheme="alert"
|
||||
variant="hollow"
|
||||
size="small"
|
||||
@click="deleteAvatar"
|
||||
>
|
||||
{{ $t('PROFILE_SETTINGS.DELETE_AVATAR') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="mt-1 mb-0 text-xs text-slate-600 dark:text-slate-400 not-italic"
|
||||
>
|
||||
{{ $t('HELP_CENTER.PORTAL.ADD.LOGO.HELP_TEXT') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model.trim="name"
|
||||
:class="{ error: $v.name.$error }"
|
||||
:error="nameError"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.NAME.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.NAME.PLACEHOLDER')"
|
||||
:help-text="$t('HELP_CENTER.PORTAL.ADD.NAME.HELP_TEXT')"
|
||||
@blur="$v.name.$touch"
|
||||
@input="onNameChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model.trim="slug"
|
||||
:class="{ error: $v.slug.$error }"
|
||||
:error="slugError"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.SLUG.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.SLUG.PLACEHOLDER')"
|
||||
:help-text="domainHelpText"
|
||||
@blur="$v.slug.$touch"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model.trim="domain"
|
||||
:class="{ error: $v.domain.$error }"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.PLACEHOLDER')"
|
||||
:help-text="domainExampleHelpText"
|
||||
:error="domainError"
|
||||
@blur="$v.domain.$touch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<woot-button
|
||||
:is-loading="isSubmitting"
|
||||
:is-disabled="$v.$invalid"
|
||||
@click="onSubmitClick"
|
||||
>
|
||||
{{ submitButtonText }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { required, minLength } from '@vuelidate/validators';
|
||||
|
||||
<script>
|
||||
import { required, minLength } from 'vuelidate/lib/validators';
|
||||
import { isDomain } from 'shared/helpers/Validators';
|
||||
import { defineComponent, reactive, computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import { convertToCategorySlug } from 'dashboard/helper/commons.js';
|
||||
import { buildPortalURL } from 'dashboard/helper/portalHelper';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { hasValidAvatarUrl } from 'dashboard/helper/URLHelper';
|
||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||
import { uploadFile } from 'dashboard/helper/uploadHelper';
|
||||
import { isDomain } from 'shared/helpers/Validators';
|
||||
import SettingsLayout from './Layout/SettingsLayout.vue';
|
||||
|
||||
const { EXAMPLE_URL } = wootConstants;
|
||||
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
|
||||
|
||||
export default {
|
||||
mixins: [alertMixin],
|
||||
props: {
|
||||
portal: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
isSubmitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
submitButtonText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
slug: '',
|
||||
domain: '',
|
||||
alertMessage: '',
|
||||
const { t } = useI18n();
|
||||
|
||||
// Logouploader keys
|
||||
avatarBlobId: '',
|
||||
logoUrl: '',
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
name: {
|
||||
required,
|
||||
minLength: minLength(2),
|
||||
},
|
||||
slug: {
|
||||
required,
|
||||
},
|
||||
domain: {
|
||||
isDomain,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
nameError() {
|
||||
if (this.$v.name.$error) {
|
||||
return this.$t('HELP_CENTER.CATEGORY.ADD.NAME.ERROR');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
slugError() {
|
||||
if (this.$v.slug.$error) {
|
||||
return this.$t('HELP_CENTER.CATEGORY.ADD.SLUG.ERROR');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
domainError() {
|
||||
if (this.$v.domain.$error) {
|
||||
return this.$t('HELP_CENTER.PORTAL.ADD.DOMAIN.ERROR');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
domainHelpText() {
|
||||
return buildPortalURL(this.slug);
|
||||
},
|
||||
domainExampleHelpText() {
|
||||
return this.$t('HELP_CENTER.PORTAL.ADD.DOMAIN.HELP_TEXT', {
|
||||
exampleURL: EXAMPLE_URL,
|
||||
});
|
||||
},
|
||||
showDeleteButton() {
|
||||
return hasValidAvatarUrl(this.logoUrl);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const portal = this.portal || {};
|
||||
this.name = portal.name || '';
|
||||
this.slug = portal.slug || '';
|
||||
this.domain = portal.custom_domain || '';
|
||||
this.alertMessage = '';
|
||||
if (portal.logo) {
|
||||
const {
|
||||
logo: { file_url: logoURL, blob_id: blobId },
|
||||
} = portal;
|
||||
this.logoUrl = logoURL;
|
||||
this.avatarBlobId = blobId;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onNameChange() {
|
||||
this.slug = convertToCategorySlug(this.name);
|
||||
},
|
||||
onSubmitClick() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
defineComponent({
|
||||
name: 'PortalSettingsBasicForm',
|
||||
});
|
||||
|
||||
const portal = {
|
||||
name: this.name,
|
||||
slug: this.slug,
|
||||
custom_domain: this.domain,
|
||||
blob_id: this.avatarBlobId || null,
|
||||
};
|
||||
this.$emit('submit', portal);
|
||||
},
|
||||
async deleteAvatar() {
|
||||
this.logoUrl = '';
|
||||
this.avatarBlobId = '';
|
||||
this.$emit('delete-logo');
|
||||
},
|
||||
onFileChange({ file }) {
|
||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||
this.uploadLogoToStorage(file);
|
||||
} else {
|
||||
this.showAlert(
|
||||
this.$t(
|
||||
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.IMAGE_UPLOAD_SIZE_ERROR',
|
||||
{
|
||||
size: MAXIMUM_FILE_UPLOAD_SIZE,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
const props = defineProps({
|
||||
portal: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
isSubmitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
submitButtonText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
this.$refs.imageUpload.value = '';
|
||||
},
|
||||
async uploadLogoToStorage(file) {
|
||||
try {
|
||||
const { fileUrl, blobId } = await uploadFile(file);
|
||||
if (fileUrl) {
|
||||
this.logoUrl = fileUrl;
|
||||
this.avatarBlobId = blobId;
|
||||
}
|
||||
} catch (error) {
|
||||
this.showAlert(
|
||||
this.$t('HELP_CENTER.PORTAL.ADD.LOGO.IMAGE_DELETE_ERROR')
|
||||
);
|
||||
}
|
||||
},
|
||||
const state = reactive({
|
||||
name: '',
|
||||
slug: '',
|
||||
domain: '',
|
||||
logoUrl: '',
|
||||
avatarBlobId: '',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
name: {
|
||||
required,
|
||||
minLength: minLength(2),
|
||||
},
|
||||
slug: {
|
||||
required,
|
||||
},
|
||||
domain: {
|
||||
isDomain,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
input {
|
||||
@apply mb-1;
|
||||
|
||||
const v$ = useVuelidate(rules, state);
|
||||
|
||||
const nameError = computed(() => {
|
||||
if (v$.value.name.$error) {
|
||||
return t('HELP_CENTER.CATEGORY.ADD.NAME.ERROR');
|
||||
}
|
||||
.help-text {
|
||||
@apply mb-0;
|
||||
return '';
|
||||
});
|
||||
|
||||
const slugError = computed(() => {
|
||||
if (v$.value.slug.$error) {
|
||||
return t('HELP_CENTER.CATEGORY.ADD.SLUG.ERROR');
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
const domainError = computed(() => {
|
||||
if (v$.value.domain.$error) {
|
||||
return t('HELP_CENTER.PORTAL.ADD.DOMAIN.ERROR');
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
const domainHelpText = computed(() => {
|
||||
return buildPortalURL(state.slug);
|
||||
});
|
||||
|
||||
const domainExampleHelpText = computed(() => {
|
||||
return t('HELP_CENTER.PORTAL.ADD.DOMAIN.HELP_TEXT', {
|
||||
exampleURL: EXAMPLE_URL,
|
||||
});
|
||||
});
|
||||
|
||||
const showDeleteButton = computed(() => {
|
||||
return hasValidAvatarUrl(state.logoUrl);
|
||||
});
|
||||
|
||||
const emit = defineEmits(['submit', 'delete-logo']);
|
||||
|
||||
onMounted(() => {
|
||||
const portal = props.portal || {};
|
||||
state.name = portal.name || '';
|
||||
state.slug = portal.slug || '';
|
||||
state.domain = portal.custom_domain || '';
|
||||
|
||||
if (portal.logo) {
|
||||
const {
|
||||
logo: { file_url: logoURL, blob_id: blobId },
|
||||
} = portal;
|
||||
state.logoUrl = logoURL;
|
||||
state.avatarBlobId = blobId;
|
||||
}
|
||||
});
|
||||
|
||||
function onNameChange() {
|
||||
state.slug = convertToCategorySlug(state.name);
|
||||
}
|
||||
|
||||
function onSubmitClick() {
|
||||
v$.value.$touch();
|
||||
if (v$.value.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const portal = {
|
||||
name: state.name,
|
||||
slug: state.slug,
|
||||
custom_domain: state.domain,
|
||||
blob_id: state.avatarBlobId || null,
|
||||
};
|
||||
emit('submit', portal);
|
||||
}
|
||||
async function deleteAvatar() {
|
||||
state.logoUrl = '';
|
||||
state.avatarBlobId = '';
|
||||
emit('delete-logo');
|
||||
}
|
||||
|
||||
async function uploadLogoToStorage(file) {
|
||||
try {
|
||||
const { fileUrl, blobId } = await uploadFile(file);
|
||||
if (fileUrl) {
|
||||
state.logoUrl = fileUrl;
|
||||
state.avatarBlobId = blobId;
|
||||
}
|
||||
} catch (error) {
|
||||
useAlert(t('HELP_CENTER.PORTAL.ADD.LOGO.IMAGE_UPLOAD_ERROR'));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
function onFileChange({ file }) {
|
||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||
uploadLogoToStorage(file);
|
||||
} else {
|
||||
const errorKey =
|
||||
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.IMAGE_UPLOAD_SIZE_ERROR';
|
||||
useAlert(t(errorKey, { size: MAXIMUM_FILE_UPLOAD_SIZE }));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SettingsLayout
|
||||
:title="
|
||||
$t('HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.BASIC_SETTINGS_PAGE.TITLE')
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
<div class="flex flex-row items-center">
|
||||
<woot-avatar-uploader
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.LOGO.LABEL')"
|
||||
:src="state.logoUrl"
|
||||
@change="onFileChange"
|
||||
/>
|
||||
<div v-if="showDeleteButton" class="avatar-delete-btn">
|
||||
<woot-button
|
||||
type="button"
|
||||
color-scheme="alert"
|
||||
variant="hollow"
|
||||
size="small"
|
||||
@click="deleteAvatar"
|
||||
>
|
||||
{{ $t('PROFILE_SETTINGS.DELETE_AVATAR') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="mt-1 mb-0 text-xs not-italic text-slate-600 dark:text-slate-400"
|
||||
>
|
||||
{{ $t('HELP_CENTER.PORTAL.ADD.LOGO.HELP_TEXT') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model="state.name"
|
||||
:class="{ error: v$.name.$error }"
|
||||
:error="nameError"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.NAME.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.NAME.PLACEHOLDER')"
|
||||
:help-text="$t('HELP_CENTER.PORTAL.ADD.NAME.HELP_TEXT')"
|
||||
@blur="v$.name.$touch"
|
||||
@input="onNameChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model="state.slug"
|
||||
:class="{ error: v$.slug.$error }"
|
||||
:error="slugError"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.SLUG.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.SLUG.PLACEHOLDER')"
|
||||
:help-text="domainHelpText"
|
||||
@blur="v$.slug.$touch"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model="state.domain"
|
||||
:class="{ error: v$.domain.$error }"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.PLACEHOLDER')"
|
||||
:help-text="domainExampleHelpText"
|
||||
:error="domainError"
|
||||
@blur="v$.domain.$touch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer-right>
|
||||
<woot-button
|
||||
:is-loading="isSubmitting"
|
||||
:is-disabled="v$.$invalid"
|
||||
@click="onSubmitClick"
|
||||
>
|
||||
{{ submitButtonText }}
|
||||
</woot-button>
|
||||
</template>
|
||||
</SettingsLayout>
|
||||
</template>
|
||||
|
||||
@@ -1,68 +1,140 @@
|
||||
<script setup>
|
||||
import { getRandomColor } from 'dashboard/helper/labelColor';
|
||||
import SettingsLayout from './Layout/SettingsLayout.vue';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
const { EXAMPLE_URL } = wootConstants;
|
||||
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { url } from '@vuelidate/validators';
|
||||
|
||||
import { defineComponent, reactive, computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
|
||||
defineComponent({
|
||||
name: 'PortalSettingsCustomizationForm',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
portal: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
isSubmitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['submit']);
|
||||
|
||||
const state = reactive({
|
||||
color: getRandomColor(),
|
||||
pageTitle: '',
|
||||
headerText: '',
|
||||
homePageLink: '',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
homePageLink: { url },
|
||||
};
|
||||
|
||||
const homepageExampleHelpText = computed(() => {
|
||||
return t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.HELP_TEXT', {
|
||||
exampleURL: EXAMPLE_URL,
|
||||
});
|
||||
});
|
||||
|
||||
const v$ = useVuelidate(rules, state);
|
||||
|
||||
function updateDataFromStore() {
|
||||
const { portal } = props;
|
||||
if (portal) {
|
||||
state.color = portal.color || getRandomColor();
|
||||
state.pageTitle = portal.page_title || '';
|
||||
state.headerText = portal.header_text || '';
|
||||
state.homePageLink = portal.homepage_link || '';
|
||||
}
|
||||
}
|
||||
|
||||
function onSubmitClick() {
|
||||
v$.value.$touch();
|
||||
if (v$.value.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const portal = {
|
||||
id: props.portal.id,
|
||||
slug: props.portal.slug,
|
||||
color: state.color,
|
||||
page_title: state.pageTitle,
|
||||
header_text: state.headerText,
|
||||
homepage_link: state.homePageLink,
|
||||
};
|
||||
|
||||
emit('submit', portal);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateDataFromStore();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="pt-3 bg-white dark:bg-slate-900 h-full border border-solid border-transparent px-6 pb-6 dark:border-transparent w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
|
||||
<SettingsLayout
|
||||
:title="
|
||||
$t('HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.CUSTOMIZATION_PAGE.TITLE')
|
||||
"
|
||||
>
|
||||
<div class="w-full">
|
||||
<h3 class="text-lg text-black-900 dark:text-slate-200">
|
||||
{{
|
||||
$t('HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.CUSTOMIZATION_PAGE.TITLE')
|
||||
}}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="my-4 mx-0 border-b border-solid border-slate-25 dark:border-slate-800"
|
||||
>
|
||||
<div class="w-[65%] flex-shrink-0 flex-grow-0 max-w-[65%]">
|
||||
<div class="mb-4">
|
||||
<label>
|
||||
{{ $t('HELP_CENTER.PORTAL.ADD.THEME_COLOR.LABEL') }}
|
||||
</label>
|
||||
<woot-color-picker v-model="color" />
|
||||
<p
|
||||
class="mt-1 mb-0 text-xs text-slate-600 dark:text-slate-400 not-italic"
|
||||
>
|
||||
{{ $t('HELP_CENTER.PORTAL.ADD.THEME_COLOR.HELP_TEXT') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model.trim="pageTitle"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.PLACEHOLDER')"
|
||||
:help-text="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.HELP_TEXT')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model.trim="headerText"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.PLACEHOLDER')"
|
||||
:help-text="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.HELP_TEXT')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model.trim="homePageLink"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.LABEL')"
|
||||
:placeholder="
|
||||
$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.PLACEHOLDER')
|
||||
"
|
||||
:help-text="homepageExampleHelpText"
|
||||
:error="
|
||||
$v.homePageLink.$error
|
||||
? $t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.ERROR')
|
||||
: ''
|
||||
"
|
||||
:class="{ error: $v.homePageLink.$error }"
|
||||
@blur="$v.homePageLink.$touch"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-grow-0 flex-shrink-0">
|
||||
<div class="mb-4">
|
||||
<label>
|
||||
{{ $t('HELP_CENTER.PORTAL.ADD.THEME_COLOR.LABEL') }}
|
||||
</label>
|
||||
<woot-color-picker v-model="state.color" />
|
||||
<p
|
||||
class="mt-1 mb-0 text-xs not-italic text-slate-600 dark:text-slate-400"
|
||||
>
|
||||
{{ $t('HELP_CENTER.PORTAL.ADD.THEME_COLOR.HELP_TEXT') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model="state.pageTitle"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.PLACEHOLDER')"
|
||||
:help-text="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.HELP_TEXT')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model="state.headerText"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.PLACEHOLDER')"
|
||||
:help-text="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.HELP_TEXT')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<woot-input
|
||||
v-model="state.homePageLink"
|
||||
:label="$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.LABEL')"
|
||||
:placeholder="$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.PLACEHOLDER')"
|
||||
:help-text="homepageExampleHelpText"
|
||||
:error="
|
||||
v$.homePageLink.$error
|
||||
? $t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.ERROR')
|
||||
: ''
|
||||
"
|
||||
:class="{ error: v$.homePageLink.$error }"
|
||||
@blur="v$.homePageLink.$touch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<template #footer-right>
|
||||
<woot-button
|
||||
:is-loading="isSubmitting"
|
||||
:is-disabled="$v.$invalid"
|
||||
:is-disabled="v$.$invalid"
|
||||
@click="onSubmitClick"
|
||||
>
|
||||
{{
|
||||
@@ -71,94 +143,12 @@
|
||||
)
|
||||
}}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SettingsLayout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { url } from 'vuelidate/lib/validators';
|
||||
import { getRandomColor } from 'dashboard/helper/labelColor';
|
||||
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
|
||||
const { EXAMPLE_URL } = wootConstants;
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
mixins: [alertMixin],
|
||||
props: {
|
||||
portal: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
isSubmitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
color: '#000',
|
||||
pageTitle: '',
|
||||
headerText: '',
|
||||
homePageLink: '',
|
||||
alertMessage: '',
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
homePageLink: {
|
||||
url,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
homepageExampleHelpText() {
|
||||
return this.$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.HELP_TEXT', {
|
||||
exampleURL: EXAMPLE_URL,
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.color = getRandomColor();
|
||||
this.updateDataFromStore();
|
||||
},
|
||||
methods: {
|
||||
updateDataFromStore() {
|
||||
const { portal } = this;
|
||||
if (portal) {
|
||||
this.color = portal.color || getRandomColor();
|
||||
this.pageTitle = portal.page_title || '';
|
||||
this.headerText = portal.header_text || '';
|
||||
this.homePageLink = portal.homepage_link || '';
|
||||
this.alertMessage = '';
|
||||
}
|
||||
},
|
||||
onSubmitClick() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
const portal = {
|
||||
id: this.portal.id,
|
||||
slug: this.portal.slug,
|
||||
color: this.color,
|
||||
page_title: this.pageTitle,
|
||||
header_text: this.headerText,
|
||||
homepage_link: this.homePageLink,
|
||||
};
|
||||
this.$emit('submit', portal);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
input {
|
||||
@apply mb-1;
|
||||
}
|
||||
.help-text {
|
||||
@apply mb-0;
|
||||
}
|
||||
.colorpicker--selected {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,115 @@
|
||||
<script setup>
|
||||
import { useRoute } from 'dashboard/composables/route';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useAlert, useTrack } from 'dashboard/composables';
|
||||
import { PORTALS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
|
||||
import { defineComponent, ref, computed } from 'vue';
|
||||
|
||||
import CategoryListItem from './CategoryListItem.vue';
|
||||
import AddCategory from './AddCategory.vue';
|
||||
import EditCategory from './EditCategory.vue';
|
||||
|
||||
defineComponent({
|
||||
name: 'ListAllCategories',
|
||||
});
|
||||
|
||||
const selectedCategory = ref({});
|
||||
const currentLocaleCode = ref('en');
|
||||
const showEditCategoryModal = ref(false);
|
||||
const showAddCategoryModal = ref(false);
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const track = useTrack();
|
||||
const { t } = useI18n();
|
||||
const currentPortalSlug = computed(() => {
|
||||
return route.params.portalSlug;
|
||||
});
|
||||
|
||||
const categoriesByLocaleCode = computed(() => {
|
||||
return getters['categories/categoriesByLocaleCode'].value(
|
||||
currentLocaleCode.value
|
||||
);
|
||||
});
|
||||
|
||||
const currentPortal = computed(() => {
|
||||
const slug = currentPortalSlug.value;
|
||||
if (slug) return getters['portals/portalBySlug'].value(slug);
|
||||
|
||||
return getters['portals/allPortals'].value[0];
|
||||
});
|
||||
const currentPortalName = computed(() => {
|
||||
return currentPortal.value ? currentPortal.value.name : '';
|
||||
});
|
||||
const allLocales = computed(() => {
|
||||
return currentPortal.value ? currentPortal.value.config.allowed_locales : [];
|
||||
});
|
||||
|
||||
const allowedLocaleCodes = computed(() => {
|
||||
return allLocales.value.map(locale => locale.code);
|
||||
});
|
||||
|
||||
function openAddCategoryModal() {
|
||||
showAddCategoryModal.value = true;
|
||||
}
|
||||
function openEditCategoryModal(category) {
|
||||
selectedCategory.value = category;
|
||||
showEditCategoryModal.value = true;
|
||||
}
|
||||
function closeAddCategoryModal() {
|
||||
showAddCategoryModal.value = false;
|
||||
}
|
||||
function closeEditCategoryModal() {
|
||||
showEditCategoryModal.value = false;
|
||||
}
|
||||
async function fetchCategoriesByPortalSlugAndLocale(localeCode) {
|
||||
await store.dispatch('categories/index', {
|
||||
portalSlug: currentPortalSlug.value,
|
||||
locale: localeCode,
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteCategory(category) {
|
||||
let alertMessage = '';
|
||||
try {
|
||||
await store.dispatch('categories/delete', {
|
||||
portalSlug: currentPortalSlug.value,
|
||||
categoryId: category.id,
|
||||
});
|
||||
alertMessage = t('HELP_CENTER.CATEGORY.DELETE.API.SUCCESS_MESSAGE');
|
||||
track(PORTALS_EVENTS.DELETE_CATEGORY, {
|
||||
hasArticles: category?.meta?.articles_count !== 0,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorMessage = error?.message;
|
||||
alertMessage =
|
||||
errorMessage || t('HELP_CENTER.CATEGORY.DELETE.API.ERROR_MESSAGE');
|
||||
} finally {
|
||||
useAlert(alertMessage);
|
||||
}
|
||||
}
|
||||
function changeCurrentCategory(event) {
|
||||
const localeCode = event.target.value;
|
||||
currentLocaleCode.value = localeCode;
|
||||
fetchCategoriesByPortalSlugAndLocale(localeCode);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full pl-4">
|
||||
<header class="flex justify-between items-center mb-4">
|
||||
<div class="w-full max-w-5xl">
|
||||
<header class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center w-full gap-3">
|
||||
<label
|
||||
class="font-normal mb-0 text-base text-slate-800 dark:text-slate-100"
|
||||
class="mb-0 text-base font-normal text-slate-800 dark:text-slate-100"
|
||||
>
|
||||
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TITLE') }}
|
||||
</label>
|
||||
<select
|
||||
:value="currentLocaleCode"
|
||||
class="w-[15%] select-locale"
|
||||
class="w-[15%] h-8 mb-0 py-0.5"
|
||||
@change="changeCurrentCategory"
|
||||
>
|
||||
<option
|
||||
@@ -21,7 +121,7 @@
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-none items-center">
|
||||
<div class="items-center flex-none">
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
@@ -58,119 +158,3 @@
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import portalMixin from '../../mixins/portalMixin';
|
||||
|
||||
import CategoryListItem from './CategoryListItem.vue';
|
||||
import AddCategory from './AddCategory.vue';
|
||||
import EditCategory from './EditCategory.vue';
|
||||
import { PORTALS_EVENTS } from '../../../../../helper/AnalyticsHelper/events';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CategoryListItem,
|
||||
AddCategory,
|
||||
EditCategory,
|
||||
},
|
||||
mixins: [alertMixin, portalMixin],
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: {},
|
||||
selectedLocaleCode: '',
|
||||
currentLocaleCode: 'en',
|
||||
showEditCategoryModal: false,
|
||||
showAddCategoryModal: false,
|
||||
alertMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
portals: 'portals/allPortals',
|
||||
meta: 'portals/getMeta',
|
||||
isFetching: 'portals/isFetchingPortals',
|
||||
}),
|
||||
currentPortalSlug() {
|
||||
return this.$route.params.portalSlug;
|
||||
},
|
||||
categoriesByLocaleCode() {
|
||||
return this.$store.getters['categories/categoriesByLocaleCode'](
|
||||
this.currentLocaleCode
|
||||
);
|
||||
},
|
||||
currentPortal() {
|
||||
const slug = this.currentPortalSlug;
|
||||
if (slug) return this.$store.getters['portals/portalBySlug'](slug);
|
||||
|
||||
return this.$store.getters['portals/allPortals'][0];
|
||||
},
|
||||
currentPortalName() {
|
||||
return this.currentPortal ? this.currentPortal.name : '';
|
||||
},
|
||||
currentPortalLocale() {
|
||||
return this.currentPortal ? this.currentPortal?.meta?.default_locale : '';
|
||||
},
|
||||
allLocales() {
|
||||
return this.currentPortal
|
||||
? this.currentPortal.config.allowed_locales
|
||||
: [];
|
||||
},
|
||||
allowedLocaleCodes() {
|
||||
return this.allLocales.map(locale => locale.code);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openAddCategoryModal() {
|
||||
this.showAddCategoryModal = true;
|
||||
},
|
||||
openEditCategoryModal(category) {
|
||||
this.selectedCategory = category;
|
||||
this.showEditCategoryModal = true;
|
||||
},
|
||||
closeAddCategoryModal() {
|
||||
this.showAddCategoryModal = false;
|
||||
},
|
||||
closeEditCategoryModal() {
|
||||
this.showEditCategoryModal = false;
|
||||
},
|
||||
async fetchCategoriesByPortalSlugAndLocale(localeCode) {
|
||||
await this.$store.dispatch('categories/index', {
|
||||
portalSlug: this.currentPortalSlug,
|
||||
locale: localeCode,
|
||||
});
|
||||
},
|
||||
async deleteCategory(category) {
|
||||
try {
|
||||
await this.$store.dispatch('categories/delete', {
|
||||
portalSlug: this.currentPortalSlug,
|
||||
categoryId: category.id,
|
||||
});
|
||||
this.alertMessage = this.$t(
|
||||
'HELP_CENTER.CATEGORY.DELETE.API.SUCCESS_MESSAGE'
|
||||
);
|
||||
this.$track(PORTALS_EVENTS.DELETE_CATEGORY, {
|
||||
hasArticles: category?.meta?.articles_count !== 0,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorMessage = error?.message;
|
||||
this.alertMessage =
|
||||
errorMessage ||
|
||||
this.$t('HELP_CENTER.CATEGORY.DELETE.API.ERROR_MESSAGE');
|
||||
} finally {
|
||||
this.showAlert(this.alertMessage);
|
||||
}
|
||||
},
|
||||
changeCurrentCategory(event) {
|
||||
const localeCode = event.target.value;
|
||||
this.currentLocaleCode = localeCode;
|
||||
this.fetchCategoriesByPortalSlugAndLocale(localeCode);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.select-locale {
|
||||
@apply h-8 mb-0 py-0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,3 +1,75 @@
|
||||
<script setup>
|
||||
import PortalSettingsBasicForm from 'dashboard/routes/dashboard/helpcenter/components/PortalSettingsBasicForm.vue';
|
||||
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { useRoute, useRouter } from 'dashboard/composables/route';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { defineComponent, computed, ref, onMounted } from 'vue';
|
||||
|
||||
defineComponent({ name: 'EditPortalBasic' });
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const uiFlags = getters['portals/uiFlagsIn'];
|
||||
|
||||
const lastPortalSlug = ref(null);
|
||||
|
||||
const currentPortalSlug = computed(() => {
|
||||
return route.params.portalSlug;
|
||||
});
|
||||
|
||||
const currentPortal = computed(() => {
|
||||
const slug = route.params.portalSlug;
|
||||
return getters['portals/portalBySlug'].value(slug);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
lastPortalSlug.value = currentPortalSlug.value;
|
||||
});
|
||||
|
||||
async function updatePortalSettings(portalObj) {
|
||||
let alertMessage = '';
|
||||
|
||||
try {
|
||||
const portalSlug = lastPortalSlug.value;
|
||||
await store.dispatch('portals/update', { ...portalObj, portalSlug });
|
||||
|
||||
alertMessage = t('HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE_FOR_UPDATE');
|
||||
|
||||
if (lastPortalSlug.value !== portalObj.slug) {
|
||||
await store.dispatch('portals/index');
|
||||
router.replace({
|
||||
name: route.name,
|
||||
params: { portalSlug: portalObj.slug },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
alertMessage =
|
||||
error?.message ||
|
||||
t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE_FOR_UPDATE');
|
||||
} finally {
|
||||
useAlert(alertMessage);
|
||||
}
|
||||
}
|
||||
async function deleteLogo() {
|
||||
try {
|
||||
const portalSlug = lastPortalSlug.value;
|
||||
await store.dispatch('portals/deleteLogo', {
|
||||
portalSlug,
|
||||
});
|
||||
} catch (error) {
|
||||
useAlert(
|
||||
error?.message || t('HELP_CENTER.PORTAL.ADD.LOGO.IMAGE_DELETE_ERROR')
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<portal-settings-basic-form
|
||||
v-if="currentPortal"
|
||||
@@ -10,80 +82,3 @@
|
||||
@delete-logo="deleteLogo"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
|
||||
import PortalSettingsBasicForm from 'dashboard/routes/dashboard/helpcenter/components/PortalSettingsBasicForm.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PortalSettingsBasicForm,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
data() {
|
||||
return {
|
||||
lastPortalSlug: undefined,
|
||||
alertMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
uiFlags: 'portals/uiFlagsIn',
|
||||
}),
|
||||
currentPortalSlug() {
|
||||
return this.$route.params.portalSlug;
|
||||
},
|
||||
currentPortal() {
|
||||
return this.$store.getters['portals/portalBySlug'](
|
||||
this.currentPortalSlug
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.lastPortalSlug = this.currentPortalSlug;
|
||||
},
|
||||
methods: {
|
||||
async updatePortalSettings(portalObj) {
|
||||
try {
|
||||
const portalSlug = this.lastPortalSlug;
|
||||
await this.$store.dispatch('portals/update', {
|
||||
...portalObj,
|
||||
portalSlug,
|
||||
});
|
||||
this.alertMessage = this.$t(
|
||||
'HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE_FOR_UPDATE'
|
||||
);
|
||||
|
||||
if (this.lastPortalSlug !== portalObj.slug) {
|
||||
await this.$store.dispatch('portals/index');
|
||||
this.$router.replace({
|
||||
name: this.$route.name,
|
||||
params: { portalSlug: portalObj.slug },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.alertMessage =
|
||||
error?.message ||
|
||||
this.$t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE_FOR_UPDATE');
|
||||
} finally {
|
||||
this.showAlert(this.alertMessage);
|
||||
}
|
||||
},
|
||||
async deleteLogo() {
|
||||
try {
|
||||
const portalSlug = this.lastPortalSlug;
|
||||
await this.$store.dispatch('portals/deleteLogo', {
|
||||
portalSlug,
|
||||
});
|
||||
} catch (error) {
|
||||
this.alertMessage =
|
||||
error?.message ||
|
||||
this.$t('HELP_CENTER.PORTAL.ADD.LOGO.IMAGE_DELETE_ERROR');
|
||||
this.showAlert(this.alertMessage);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
<script setup>
|
||||
import PortalSettingsCustomizationForm from 'dashboard/routes/dashboard/helpcenter/components/PortalSettingsCustomizationForm.vue';
|
||||
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { useRoute } from 'dashboard/composables/route';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
defineComponent({
|
||||
name: 'EditPortalCustomization',
|
||||
});
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const uiFlags = getters['portals/uiFlagsIn'];
|
||||
|
||||
const currentPortal = computed(() => {
|
||||
const slug = route.params.portalSlug;
|
||||
return getters['portals/portalBySlug'].value(slug);
|
||||
});
|
||||
|
||||
async function updatePortalSettings(portalObj) {
|
||||
const portalSlug = route.params.portalSlug;
|
||||
let alertMessage = '';
|
||||
try {
|
||||
await store.dispatch('portals/update', {
|
||||
...portalObj,
|
||||
portalSlug,
|
||||
});
|
||||
|
||||
alertMessage = t('HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE_FOR_UPDATE');
|
||||
} catch (error) {
|
||||
alertMessage =
|
||||
error?.message ||
|
||||
t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE_FOR_UPDATE');
|
||||
} finally {
|
||||
useAlert(alertMessage);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<portal-settings-customization-form
|
||||
v-if="currentPortal"
|
||||
@@ -9,51 +54,3 @@
|
||||
@submit="updatePortalSettings"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import PortalSettingsCustomizationForm from 'dashboard/routes/dashboard/helpcenter/components/PortalSettingsCustomizationForm.vue';
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PortalSettingsCustomizationForm,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
data() {
|
||||
return {
|
||||
alertMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
uiFlags: 'portals/uiFlagsIn',
|
||||
}),
|
||||
currentPortal() {
|
||||
const slug = this.$route.params.portalSlug;
|
||||
return this.$store.getters['portals/portalBySlug'](slug);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async updatePortalSettings(portalObj) {
|
||||
const portalSlug = this.$route.params.portalSlug;
|
||||
try {
|
||||
await this.$store.dispatch('portals/update', {
|
||||
...portalObj,
|
||||
portalSlug,
|
||||
});
|
||||
this.alertMessage = this.$t(
|
||||
'HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE_FOR_UPDATE'
|
||||
);
|
||||
} catch (error) {
|
||||
this.alertMessage =
|
||||
error?.message ||
|
||||
this.$t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE_FOR_UPDATE');
|
||||
} finally {
|
||||
this.showAlert(this.alertMessage);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,117 @@
|
||||
<script setup>
|
||||
import LocaleItemTable from 'dashboard/routes/dashboard/helpcenter/components/PortalListItemTable.vue';
|
||||
import AddLocale from 'dashboard/routes/dashboard/helpcenter/components/AddLocale.vue';
|
||||
import { PORTALS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
|
||||
import { useAlert, useTrack } from 'dashboard/composables';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { useRoute } from 'dashboard/composables/route';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { defineComponent, ref, onBeforeMount, computed } from 'vue';
|
||||
|
||||
defineComponent({
|
||||
name: 'EditPortalLocales',
|
||||
});
|
||||
|
||||
const isAddLocaleModalOpen = ref(false);
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const track = useTrack();
|
||||
const { t } = useI18n();
|
||||
|
||||
const currentPortalSlug = computed(() => {
|
||||
return route.params.portalSlug;
|
||||
});
|
||||
const currentPortal = computed(() => {
|
||||
const slug = currentPortalSlug.value;
|
||||
if (slug) return getters['portals/portalBySlug'].value(slug);
|
||||
|
||||
return getters['portals/allPortals'].value[0];
|
||||
});
|
||||
const locales = computed(() => {
|
||||
return currentPortal.value?.config.allowed_locales;
|
||||
});
|
||||
const allowedLocales = computed(() => {
|
||||
return Object.keys(locales.value).map(key => {
|
||||
return this.locales.value[key].code;
|
||||
});
|
||||
});
|
||||
|
||||
async function fetchPortals() {
|
||||
await store.dispatch('portals/index');
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
fetchPortals();
|
||||
});
|
||||
|
||||
async function updatePortalLocales({
|
||||
newAllowedLocales,
|
||||
defaultLocale,
|
||||
messageKey,
|
||||
}) {
|
||||
let alertMessage = '';
|
||||
try {
|
||||
await store.dispatch('portals/update', {
|
||||
portalSlug: currentPortalSlug.value,
|
||||
config: {
|
||||
default_locale: defaultLocale,
|
||||
allowed_locales: newAllowedLocales,
|
||||
},
|
||||
});
|
||||
alertMessage = t(`HELP_CENTER.PORTAL.${messageKey}.API.SUCCESS_MESSAGE`);
|
||||
} catch (error) {
|
||||
alertMessage =
|
||||
error?.message || t(`HELP_CENTER.PORTAL.${messageKey}.API.ERROR_MESSAGE`);
|
||||
} finally {
|
||||
useAlert(alertMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function changeDefaultLocale({ localeCode }) {
|
||||
updatePortalLocales({
|
||||
allowedLocales: allowedLocales.value,
|
||||
defaultLocale: localeCode,
|
||||
messageKey: 'CHANGE_DEFAULT_LOCALE',
|
||||
});
|
||||
|
||||
track(PORTALS_EVENTS.SET_DEFAULT_LOCALE, {
|
||||
newLocale: localeCode,
|
||||
from: route.name,
|
||||
});
|
||||
}
|
||||
function deletePortalLocale({ localeCode }) {
|
||||
const updatedLocales = allowedLocales.value.filter(
|
||||
code => code !== localeCode
|
||||
);
|
||||
|
||||
const defaultLocale = currentPortal.value?.meta.default_locale;
|
||||
|
||||
updatePortalLocales({
|
||||
allowedLocales: updatedLocales,
|
||||
defaultLocale,
|
||||
messageKey: 'DELETE_LOCALE',
|
||||
});
|
||||
|
||||
track(PORTALS_EVENTS.DELETE_LOCALE, {
|
||||
deletedLocale: localeCode,
|
||||
from: route.name,
|
||||
});
|
||||
}
|
||||
|
||||
function closeAddLocaleModal() {
|
||||
isAddLocaleModalOpen.value = false;
|
||||
}
|
||||
function addLocale() {
|
||||
isAddLocaleModalOpen.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="portal-locales">
|
||||
<div class="button-container">
|
||||
<div class="w-full h-full max-w-5xl space-y-4 bg-white dark:bg-slate-900">
|
||||
<div class="flex justify-end">
|
||||
<woot-button
|
||||
variant="smooth"
|
||||
size="small"
|
||||
@@ -11,19 +122,18 @@
|
||||
{{ $t('HELP_CENTER.PORTAL.PORTAL_SETTINGS.LIST_ITEM.HEADER.ADD') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
<div class="locale-container">
|
||||
<locale-item-table
|
||||
:locales="locales"
|
||||
:selected-locale-code="currentPortal.meta.default_locale"
|
||||
@change-default-locale="changeDefaultLocale"
|
||||
@delete="deletePortalLocale"
|
||||
/>
|
||||
</div>
|
||||
<LocaleItemTable
|
||||
v-if="currentPortal"
|
||||
:locales="locales"
|
||||
:selected-locale-code="currentPortal.meta.default_locale"
|
||||
@change-default-locale="changeDefaultLocale"
|
||||
@delete="deletePortalLocale"
|
||||
/>
|
||||
<woot-modal
|
||||
:show.sync="isAddLocaleModalOpen"
|
||||
:on-close="closeAddLocaleModal"
|
||||
>
|
||||
<add-locale
|
||||
<AddLocale
|
||||
:show="isAddLocaleModalOpen"
|
||||
:portal="currentPortal"
|
||||
@cancel="closeAddLocaleModal"
|
||||
@@ -31,126 +141,3 @@
|
||||
</woot-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import LocaleItemTable from 'dashboard/routes/dashboard/helpcenter/components/PortalListItemTable.vue';
|
||||
import AddLocale from 'dashboard/routes/dashboard/helpcenter/components/AddLocale.vue';
|
||||
import { PORTALS_EVENTS } from '../../../../../helper/AnalyticsHelper/events';
|
||||
export default {
|
||||
components: {
|
||||
LocaleItemTable,
|
||||
AddLocale,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
data() {
|
||||
return {
|
||||
isAddLocaleModalOpen: false,
|
||||
lastPortalSlug: undefined,
|
||||
alertMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
uiFlags: 'portals/uiFlagsIn',
|
||||
}),
|
||||
currentPortalSlug() {
|
||||
return this.$route.params.portalSlug;
|
||||
},
|
||||
currentPortal() {
|
||||
return this.$store.getters['portals/portalBySlug'](
|
||||
this.currentPortalSlug
|
||||
);
|
||||
},
|
||||
locales() {
|
||||
return this.currentPortal.config.allowed_locales;
|
||||
},
|
||||
allowedLocales() {
|
||||
return Object.keys(this.locales).map(key => {
|
||||
return this.locales[key].code;
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.lastPortalSlug = this.currentPortalSlug;
|
||||
},
|
||||
methods: {
|
||||
changeDefaultLocale({ localeCode }) {
|
||||
this.updatePortalLocales({
|
||||
allowedLocales: this.allowedLocales,
|
||||
defaultLocale: localeCode,
|
||||
successMessage: this.$t(
|
||||
'HELP_CENTER.PORTAL.CHANGE_DEFAULT_LOCALE.API.SUCCESS_MESSAGE'
|
||||
),
|
||||
errorMessage: this.$t(
|
||||
'HELP_CENTER.PORTAL.CHANGE_DEFAULT_LOCALE.API.ERROR_MESSAGE'
|
||||
),
|
||||
});
|
||||
this.$track(PORTALS_EVENTS.SET_DEFAULT_LOCALE, {
|
||||
newLocale: localeCode,
|
||||
from: this.$route.name,
|
||||
});
|
||||
},
|
||||
deletePortalLocale({ localeCode }) {
|
||||
const updatedLocales = this.allowedLocales.filter(
|
||||
code => code !== localeCode
|
||||
);
|
||||
const defaultLocale = this.currentPortal.meta.default_locale;
|
||||
this.updatePortalLocales({
|
||||
allowedLocales: updatedLocales,
|
||||
defaultLocale,
|
||||
successMessage: this.$t(
|
||||
'HELP_CENTER.PORTAL.DELETE_LOCALE.API.SUCCESS_MESSAGE'
|
||||
),
|
||||
errorMessage: this.$t(
|
||||
'HELP_CENTER.PORTAL.DELETE_LOCALE.API.ERROR_MESSAGE'
|
||||
),
|
||||
});
|
||||
this.$track(PORTALS_EVENTS.DELETE_LOCALE, {
|
||||
deletedLocale: localeCode,
|
||||
from: this.$route.name,
|
||||
});
|
||||
},
|
||||
async updatePortalLocales({
|
||||
allowedLocales,
|
||||
defaultLocale,
|
||||
successMessage,
|
||||
errorMessage,
|
||||
}) {
|
||||
try {
|
||||
await this.$store.dispatch('portals/update', {
|
||||
portalSlug: this.currentPortal.slug,
|
||||
config: {
|
||||
default_locale: defaultLocale,
|
||||
allowed_locales: allowedLocales,
|
||||
},
|
||||
});
|
||||
this.alertMessage = successMessage;
|
||||
} catch (error) {
|
||||
this.alertMessage = error?.message || errorMessage;
|
||||
} finally {
|
||||
this.showAlert(this.alertMessage);
|
||||
}
|
||||
},
|
||||
closeAddLocaleModal() {
|
||||
this.isAddLocaleModalOpen = false;
|
||||
this.selectedPortal = {};
|
||||
},
|
||||
addLocale() {
|
||||
this.isAddLocaleModalOpen = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.portal-locales {
|
||||
@apply w-full bg-white dark:bg-slate-900 h-full py-0 pr-0 pl-4;
|
||||
.button-container {
|
||||
@apply flex justify-end;
|
||||
}
|
||||
.locale-container {
|
||||
@apply mt-4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex-1">
|
||||
<section class="flex-1">
|
||||
<settings-header
|
||||
button-route="new"
|
||||
:header-title="portalHeaderText"
|
||||
@@ -10,16 +10,20 @@
|
||||
:show-new-button="false"
|
||||
/>
|
||||
<div
|
||||
class="flex flex-row overflow-auto py-4 pl-4 rtl:pl-0 rtl:pr-4 h-full bg-slate-50 dark:bg-slate-800"
|
||||
class="grid grid-cols-[20rem_1fr] w-full h-full overflow-auto rtl:pl-0 rtl:pr-4 bg-slate-50 dark:bg-slate-800 p-5"
|
||||
>
|
||||
<woot-wizard
|
||||
class="hidden md:block w-1/4"
|
||||
class="hidden md:block"
|
||||
:global-config="globalConfig"
|
||||
:items="items"
|
||||
/>
|
||||
<router-view />
|
||||
<div
|
||||
class="w-full p-5 bg-white border border-transparent border-solid rounded-md shadow-sm dark:bg-slate-900 dark:border-transparent"
|
||||
>
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,3 +1,63 @@
|
||||
<script setup>
|
||||
import PortalSettingsCustomizationForm from 'dashboard/routes/dashboard/helpcenter/components/PortalSettingsCustomizationForm.vue';
|
||||
import { PORTALS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
|
||||
import { useAlert, useTrack } from 'dashboard/composables';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { useRoute, useRouter } from 'dashboard/composables/route';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { defineComponent, onMounted, computed } from 'vue';
|
||||
|
||||
defineComponent({
|
||||
name: 'PortalCustomization',
|
||||
});
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const track = useTrack();
|
||||
const { t } = useI18n();
|
||||
|
||||
const uiFlags = getters['portals/uiFlagsIn'];
|
||||
|
||||
const currentPortal = computed(() => {
|
||||
const slug = route.params.portalSlug;
|
||||
if (slug) return getters['portals/portalBySlug'].value(slug);
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
store.dispatch('portals/index');
|
||||
});
|
||||
|
||||
async function updatePortalSettings(portalObj) {
|
||||
const portalSlug = route.params.portalSlug;
|
||||
let alertMessage = '';
|
||||
try {
|
||||
await store.dispatch('portals/update', {
|
||||
portalSlug,
|
||||
...portalObj,
|
||||
});
|
||||
alertMessage = t('HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE_FOR_UPDATE');
|
||||
|
||||
track(PORTALS_EVENTS.ONBOARD_CUSTOMIZATION, {
|
||||
hasHomePageLink: Boolean(portalObj.homepage_link),
|
||||
hasPageTitle: Boolean(portalObj.page_title),
|
||||
hasHeaderText: Boolean(portalObj.headerText),
|
||||
});
|
||||
} catch (error) {
|
||||
alertMessage =
|
||||
error?.message ||
|
||||
t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE_FOR_UPDATE');
|
||||
} finally {
|
||||
useAlert(alertMessage);
|
||||
router.push({ name: 'portal_finish' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<portal-settings-customization-form
|
||||
v-if="currentPortal"
|
||||
@@ -9,75 +69,3 @@
|
||||
@submit="updatePortalSettings"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import PortalSettingsCustomizationForm from 'dashboard/routes/dashboard/helpcenter/components/PortalSettingsCustomizationForm.vue';
|
||||
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import { getRandomColor } from 'dashboard/helper/labelColor';
|
||||
import { PORTALS_EVENTS } from '../../../../../helper/AnalyticsHelper/events';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PortalSettingsCustomizationForm,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
data() {
|
||||
return {
|
||||
color: '#000',
|
||||
pageTitle: '',
|
||||
headerText: '',
|
||||
homePageLink: '',
|
||||
alertMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
uiFlags: 'portals/uiFlagsIn',
|
||||
portals: 'portals/allPortals',
|
||||
}),
|
||||
currentPortal() {
|
||||
const slug = this.$route.params.portalSlug;
|
||||
return this.$store.getters['portals/portalBySlug'](slug);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchPortals();
|
||||
this.color = getRandomColor();
|
||||
},
|
||||
methods: {
|
||||
fetchPortals() {
|
||||
this.$store.dispatch('portals/index');
|
||||
},
|
||||
async updatePortalSettings(portalObj) {
|
||||
const portalSlug = this.$route.params.portalSlug;
|
||||
try {
|
||||
await this.$store.dispatch('portals/update', {
|
||||
portalSlug,
|
||||
...portalObj,
|
||||
});
|
||||
this.alertMessage = this.$t(
|
||||
'HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE_FOR_UPDATE'
|
||||
);
|
||||
|
||||
this.$track(PORTALS_EVENTS.ONBOARD_CUSTOMIZATION, {
|
||||
hasHomePageLink: Boolean(portalObj.homepage_link),
|
||||
hasPageTitle: Boolean(portalObj.page_title),
|
||||
hasHeaderText: Boolean(portalObj.headerText),
|
||||
});
|
||||
} catch (error) {
|
||||
this.alertMessage =
|
||||
error?.message ||
|
||||
this.$t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE_FOR_UPDATE');
|
||||
} finally {
|
||||
this.showAlert(this.alertMessage);
|
||||
this.$router.push({
|
||||
name: 'portal_finish',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="pt-3 bg-white dark:bg-slate-900 h-full border border-solid border-transparent px-6 pb-6 dark:border-transparent w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
|
||||
class="flex-grow-0 flex-shrink-0 w-full h-full max-w-full px-6 pt-3 pb-6 bg-white border border-transparent border-solid dark:bg-slate-900 dark:border-transparent"
|
||||
>
|
||||
<empty-state
|
||||
:title="$t('HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.FINISH_PAGE.TITLE')"
|
||||
@@ -10,7 +10,7 @@
|
||||
>
|
||||
<div class="w-full text-center">
|
||||
<router-link
|
||||
class="button success nice rounded"
|
||||
class="rounded button success nice"
|
||||
:to="{
|
||||
name: 'list_all_portals',
|
||||
}"
|
||||
@@ -22,18 +22,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
|
||||
export default {
|
||||
components: {
|
||||
EmptyState,
|
||||
},
|
||||
methods: {
|
||||
changeRoute() {
|
||||
this.$router.push({
|
||||
name: 'list_all_portals',
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
import { defineComponent } from 'vue';
|
||||
defineComponent({
|
||||
name: 'PortalSettingsFinish',
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user