refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
478
resources/js/components/settings/ActivityTypes.vue
Normal file
478
resources/js/components/settings/ActivityTypes.vue
Normal file
@@ -0,0 +1,478 @@
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="activityTypes" position="bottom right" />
|
||||
|
||||
<h3 class="with-actions">
|
||||
{{ $t('settings.personalization_activity_type_category_title') }}
|
||||
<a v-if="!limited" v-cy-name="'add-activity-type-category-button'" class="btn nt2" :class="[ dirltr ? 'fr' : 'fl' ]" href=""
|
||||
@click.prevent="showCreateCategoryModal"
|
||||
>
|
||||
{{ $t('settings.personalization_activity_type_category_add') }}
|
||||
</a>
|
||||
</h3>
|
||||
<p>{{ $t('settings.personalization_activity_type_category_description') }}</p>
|
||||
|
||||
<div v-if="limited" v-cy-name="'activity-type-premium-message'" class="mt3 mb3 form-information-message br2">
|
||||
<div class="pa3 flex">
|
||||
<div class="mr3">
|
||||
<svg viewBox="0 0 20 20">
|
||||
<g fill-rule="evenodd">
|
||||
<circle cx="10" cy="10" r="9" fill="currentColor" /><path d="M10 0C4.486 0 0 4.486 0 10s4.486 10 10 10 10-4.486 10-10S15.514 0 10 0m0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8m1-5v-3a1 1 0 0 0-1-1H9a1 1 0 1 0 0 2v3a1 1 0 0 0 1 1h1a1 1 0 1 0 0-2m-1-5.9a1.1 1.1 0 1 0 0-2.2 1.1 1.1 0 0 0 0 2.2" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-html="$t('settings.personalisation_paid_upgrade_vue', {url: 'settings/subscriptions' })"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dt dt--fixed w-100 collapse br--top br--bottom">
|
||||
<div class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_activity_type_category_table_name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_activity_type_category_table_actions') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-cy-name="'activity-types'">
|
||||
<ul v-cy-name="'activity-type-categories'" v-cy-items="activityTypeCategories.map(a => a.id)">
|
||||
<li v-for="activityTypeCategory in activityTypeCategories" :key="activityTypeCategory.id" v-cy-name="'activity-types-'+activityTypeCategory.id"
|
||||
v-cy-items="activityTypeCategory.activityTypes ? activityTypeCategory.activityTypes.map(a => a.id) : ''" class="dt dt--fixed w-100 collapse br--top br--bottom mt3"
|
||||
>
|
||||
<!-- ACTIVITY TYPE CATEGORY -->
|
||||
<div class="dt-row hover bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
<strong>{{ activityTypeCategory.name }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<div class="pa2" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<em v-if="!limited" v-cy-name="'activity-type-category-edit-button-'+activityTypeCategory.id"
|
||||
class="fa fa-pencil-square-o pointer pr2" @click="showEditCategory(activityTypeCategory)"
|
||||
></em>
|
||||
<em v-if="!limited" v-cy-name="'activity-type-category-delete-button-'+activityTypeCategory.id"
|
||||
class="fa fa-trash-o pointer" @click="showDeleteCategory(activityTypeCategory)"
|
||||
></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="activityType in activityTypeCategory.activityTypes" :key="activityType.id" class="dt-row hover bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2 pl4">
|
||||
{{ activityType.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2">
|
||||
<em v-if="!limited" v-cy-name="'activity-type-edit-button-'+activityType.id"
|
||||
class="fa fa-pencil-square-o pointer pr2" @click="showEditType(activityType, activityTypeCategory.id)"
|
||||
></em>
|
||||
<em v-if="!limited" v-cy-name="'activity-type-delete-button-'+activityType.id"
|
||||
class="fa fa-trash-o pointer" @click="showDeleteType(activityType)"
|
||||
></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!limited" class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 pl4">
|
||||
<a v-cy-name="'add-activity-type-button-for-category-'+activityTypeCategory.id" class="pointer" href=""
|
||||
@click.prevent="showCreateTypeModal(activityTypeCategory)"
|
||||
>
|
||||
{{ $t('settings.personalization_activity_type_add_button') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Create Activity Type Category -->
|
||||
<sweet-modal ref="createCategoryModal" overlay-theme="dark" :title="$t('settings.personalization_activity_type_category_modal_add')">
|
||||
<form @submit.prevent="storeCategory()">
|
||||
<div class="mb4">
|
||||
<p class="b mb2"></p>
|
||||
<form-input
|
||||
:id="'add-category-name'"
|
||||
v-model="createCategoryForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_activity_type_category_modal_question')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeCategoryModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-cy-name="'add-activity-type-category-save-button'" class="btn btn-primary" href="" @click.prevent="storeCategory()">
|
||||
{{ $t('app.save') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Update Activity Type Category -->
|
||||
<sweet-modal ref="updateCategoryModal" overlay-theme="dark" :title="$t('settings.personalization_activity_type_category_modal_edit')">
|
||||
<form @submit.prevent="updateCategory()">
|
||||
<div class="mb4">
|
||||
<p class="b mb2"></p>
|
||||
<form-input
|
||||
:id="'update-category-name'"
|
||||
v-model="updateCategoryForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_activity_type_category_modal_question')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeUpdateCategoryModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-cy-name="'update-activity-type-category-button'" class="btn btn-primary" href="" @click.prevent="updateCategory()">
|
||||
{{ $t('app.update') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Create Activity Type -->
|
||||
<sweet-modal ref="createTypeModal" overlay-theme="dark" :title="$t('settings.personalization_activity_type_modal_add')">
|
||||
<form @submit.prevent="storeType()">
|
||||
<div class="mb4">
|
||||
<p class="b mb2"></p>
|
||||
<form-input
|
||||
:id="'add-type-name'"
|
||||
v-model="createTypeForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_activity_type_modal_question')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeCreateTypeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-cy-name="'add-type-button'" class="btn btn-primary" href="" @click.prevent="storeType()">
|
||||
{{ $t('app.save') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Update Activity Type -->
|
||||
<sweet-modal ref="updateTypeModal" overlay-theme="dark" :title="$t('settings.personalization_activity_type_modal_edit')">
|
||||
<form @submit.prevent="updateType()">
|
||||
<div class="mb4">
|
||||
<p class="b mb2"></p>
|
||||
<form-input
|
||||
:id="'update-type-name'"
|
||||
v-model="updateTypeForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_activity_type_modal_question')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeUpdateTypeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-cy-name="'update-type-button'" class="btn btn-primary" href="" @click.prevent="updateType()">
|
||||
{{ $t('app.update') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Delete Activiy type category -->
|
||||
<sweet-modal ref="deleteCategoryModal" overlay-theme="dark" :title="$t('settings.personalization_activity_type_category_modal_delete')">
|
||||
<form>
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb4">
|
||||
<p class="mb2">
|
||||
{{ $t('settings.personalization_activity_type_category_modal_delete_desc') }}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteCategoryModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-cy-name="'delete-category-button'" class="btn btn-primary" href="" @click.prevent="destroyCategory()">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Delete Activiy type -->
|
||||
<sweet-modal ref="deleteTypeModal" overlay-theme="dark" :title="$t('settings.personalization_activity_type_modal_delete')">
|
||||
<form>
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb4">
|
||||
<p class="mb2">
|
||||
{{ $t('settings.personalization_activity_type_modal_delete_desc') }}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteTypeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-cy-name="'delete-type-button'" class="btn btn-primary" href="" @click.prevent="destroyType()">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
props: {
|
||||
limited: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
activityTypes: [],
|
||||
activityTypeCategories: [],
|
||||
errorMessage: '',
|
||||
|
||||
updatedCategory: {
|
||||
id: '',
|
||||
name: ''
|
||||
},
|
||||
|
||||
createCategoryForm: {
|
||||
name: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
createTypeForm: {
|
||||
name: '',
|
||||
activity_type_category_id: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
updateCategoryForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
activity_type_category_id: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
updateTypeForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
destroyCategoryForm: {
|
||||
id: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
destroyTypeForm: {
|
||||
id: '',
|
||||
errors: []
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.getActivityTypeCategories();
|
||||
},
|
||||
|
||||
getActivityTypeCategories() {
|
||||
axios.get('settings/personalization/activitytypecategories')
|
||||
.then(response => {
|
||||
this.activityTypeCategories = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
closeCategoryModal() {
|
||||
this.$refs.createCategoryModal.close();
|
||||
},
|
||||
|
||||
closeDeleteCategoryModal() {
|
||||
this.$refs.deleteCategoryModal.close();
|
||||
},
|
||||
|
||||
showCreateCategoryModal() {
|
||||
this.$refs.createCategoryModal.open();
|
||||
},
|
||||
|
||||
storeCategory() {
|
||||
axios.post('settings/personalization/activitytypecategories', this.createCategoryForm)
|
||||
.then(response => {
|
||||
this.$refs.createCategoryModal.close();
|
||||
this.activityTypeCategories.push(response.data.data);
|
||||
this.createCategoryForm.name = '';
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
});
|
||||
},
|
||||
|
||||
showEditCategory(category) {
|
||||
this.updateCategoryForm.id = category.id;
|
||||
this.updateCategoryForm.name = category.name;
|
||||
this.updatedCategory = category;
|
||||
|
||||
this.$refs.updateCategoryModal.open();
|
||||
},
|
||||
|
||||
showDeleteCategory(category) {
|
||||
this.destroyCategoryForm.id = category.id;
|
||||
|
||||
this.$refs.deleteCategoryModal.open();
|
||||
},
|
||||
|
||||
showDeleteType(type) {
|
||||
this.destroyTypeForm.id = type.id;
|
||||
|
||||
this.$refs.deleteTypeModal.open();
|
||||
},
|
||||
|
||||
showEditType(type, categoryId) {
|
||||
this.updateTypeForm.id = type.id;
|
||||
this.updateTypeForm.name = type.name;
|
||||
this.updateTypeForm.activity_type_category_id = categoryId;
|
||||
|
||||
this.$refs.updateTypeModal.open();
|
||||
},
|
||||
|
||||
closeUpdateCategoryModal() {
|
||||
this.$refs.updateCategoryModal.close();
|
||||
},
|
||||
|
||||
closeCreateTypeModal() {
|
||||
this.$refs.createTypeModal.close();
|
||||
},
|
||||
|
||||
closeUpdateTypeModal() {
|
||||
this.$refs.updateTypeModal.close();
|
||||
},
|
||||
|
||||
closeDeleteTypeModal() {
|
||||
this.$refs.deleteTypeModal.close();
|
||||
},
|
||||
|
||||
updateCategory() {
|
||||
axios.put('settings/personalization/activitytypecategories/' + this.updateCategoryForm.id, this.updateCategoryForm)
|
||||
.then(response => {
|
||||
this.$refs.updateCategoryModal.close();
|
||||
this.updatedCategory.name = this.updateCategoryForm.name;
|
||||
this.updateCategoryForm.name = '';
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
});
|
||||
},
|
||||
|
||||
showCreateTypeModal(category) {
|
||||
this.$refs.createTypeModal.open();
|
||||
this.createTypeForm.activity_type_category_id = category.id;
|
||||
},
|
||||
|
||||
storeType() {
|
||||
axios.post('settings/personalization/activitytypes', this.createTypeForm)
|
||||
.then(response => {
|
||||
this.$refs.createTypeModal.close();
|
||||
this.activityTypes.push(response.data);
|
||||
this.createTypeForm.name = '';
|
||||
this.getActivityTypeCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
});
|
||||
},
|
||||
|
||||
destroyCategory() {
|
||||
axios.delete('settings/personalization/activitytypecategories/' + this.destroyCategoryForm.id)
|
||||
.then(response => {
|
||||
this.$refs.deleteCategoryModal.close();
|
||||
this.destroyCategoryForm.id = '';
|
||||
this.getActivityTypeCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorMessage = error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
updateType() {
|
||||
axios.put('settings/personalization/activitytypes/' + this.updateTypeForm.id, this.updateTypeForm)
|
||||
.then(response => {
|
||||
this.$refs.updateTypeModal.close();
|
||||
this.updatedCategory.name = this.updateTypeForm.name;
|
||||
this.updateTypeForm.name = '';
|
||||
this.getActivityTypeCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
});
|
||||
},
|
||||
|
||||
destroyType() {
|
||||
axios.delete('settings/personalization/activitytypes/' + this.destroyTypeForm.id)
|
||||
.then(response => {
|
||||
this.$refs.deleteTypeModal.close();
|
||||
this.destroyTypeForm.id = '';
|
||||
this.getActivityTypeCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorMessage = error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'activityTypes',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
392
resources/js/components/settings/ContactFieldTypes.vue
Normal file
392
resources/js/components/settings/ContactFieldTypes.vue
Normal file
@@ -0,0 +1,392 @@
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="main" position="bottom right" />
|
||||
|
||||
<h3 class="with-actions">
|
||||
{{ $t('settings.personalization_contact_field_type_title') }}
|
||||
<a class="btn nt2" :class="[ dirltr ? 'fr' : 'fl' ]" href="" @click.prevent="add">
|
||||
{{ $t('settings.personalization_contact_field_type_add') }}
|
||||
</a>
|
||||
</h3>
|
||||
<p>{{ $t('settings.personalization_contact_field_type_description') }}</p>
|
||||
|
||||
<div v-if="submitted" class="pa2 ba b--yellow mb3 mt3 br2 bg-washed-yellow">
|
||||
{{ $t('settings.personalization_contact_field_type_add_success') }}
|
||||
</div>
|
||||
|
||||
<div v-if="edited" class="pa2 ba b--yellow mb3 mt3 br2 bg-washed-yellow">
|
||||
{{ $t('settings.personalization_contact_field_type_edit_success') }}
|
||||
</div>
|
||||
|
||||
<div v-if="deleted" class="pa2 ba b--yellow mb3 mt3 br2 bg-washed-yellow">
|
||||
{{ $t('settings.personalization_contact_field_type_delete_success') }}
|
||||
</div>
|
||||
|
||||
<div class="dt dt--fixed w-100 collapse br--top br--bottom">
|
||||
<div class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_protocol') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_actions') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="contactFieldType in contactFieldTypes" :key="contactFieldType.id" class="dt-row hover bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2">
|
||||
<em v-if="contactFieldType.fontawesome_icon" :class="contactFieldType.fontawesome_icon" class="pr2"></em>
|
||||
<em v-else class="pr2 fa fa-address-card-o"></em>
|
||||
{{ contactFieldType.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<code class="f7">
|
||||
{{ contactFieldType.protocol }}
|
||||
</code>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2">
|
||||
<em class="fa fa-pencil-square-o pointer pr2" @click="edit(contactFieldType)"></em>
|
||||
<em v-if="contactFieldType.delible" class="fa fa-trash-o pointer" @click="showDelete(contactFieldType)"></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Contact field type -->
|
||||
<sweet-modal ref="modalCreateContactFieldType" overlay-theme="dark"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_title')"
|
||||
@open="_focusCreateInput"
|
||||
>
|
||||
<!-- Form Errors -->
|
||||
<errors :errors="createForm.errors" />
|
||||
|
||||
<form class="form-horizontal" role="form" @submit.prevent="store">
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'name'"
|
||||
ref="createName"
|
||||
v-model="createForm.name"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_name')"
|
||||
@submit="store"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'protocol'"
|
||||
v-model="createForm.protocol"
|
||||
:placeholder="'mailto:'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_protocol')"
|
||||
@submit="store"
|
||||
/>
|
||||
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_contact_field_type_modal_protocol_help') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'icon'"
|
||||
v-model="createForm.icon"
|
||||
:placeholder="'fa fa-address-book-o'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_icon')"
|
||||
@submit="store"
|
||||
/>
|
||||
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_contact_field_type_modal_icon_help') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeModal">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="store">
|
||||
{{ $t('app.save') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Edit Contact field type -->
|
||||
<sweet-modal ref="modalEditContactFieldType" overlay-theme="dark"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_edit_title')"
|
||||
@open="_focusEditInput"
|
||||
>
|
||||
<!-- Form Errors -->
|
||||
<error :errors="editForm.errors" />
|
||||
|
||||
<form class="form-horizontal" role="form" @submit.prevent="update">
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'name'"
|
||||
ref="editName"
|
||||
v-model="editForm.name"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_name')"
|
||||
@submit="update"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'protocol'"
|
||||
v-model="editForm.protocol"
|
||||
:placeholder="'mailto:'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_protocol')"
|
||||
@submit="update"
|
||||
/>
|
||||
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_contact_field_type_modal_protocol_help') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'icon'"
|
||||
v-model="editForm.icon"
|
||||
:placeholder="'fa fa-address-book-o'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_icon')"
|
||||
@submit="update"
|
||||
/>
|
||||
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_contact_field_type_modal_icon_help') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeModal">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="update">
|
||||
{{ $t('app.edit') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Delete Contact field type -->
|
||||
<sweet-modal ref="modalDeleteContactFieldType" overlay-theme="dark"
|
||||
:title="$t('settings.personalization_contact_field_type_modal_delete_title')"
|
||||
>
|
||||
<p>
|
||||
{{ $t('settings.personalization_contact_field_type_modal_delete_description') }}
|
||||
</p>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeModal">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="trash">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import Errors from '../partials/Error.vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal,
|
||||
Errors,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
contactFieldTypes: [],
|
||||
|
||||
submitted: false,
|
||||
edited: false,
|
||||
deleted: false,
|
||||
|
||||
createForm: {
|
||||
name: '',
|
||||
protocol: '',
|
||||
icon: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
editForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
protocol: '',
|
||||
icon: '',
|
||||
errors: []
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.getContactFieldTypes();
|
||||
},
|
||||
|
||||
getContactFieldTypes() {
|
||||
axios.get('settings/personalization/contactfieldtypes')
|
||||
.then(response => {
|
||||
this.contactFieldTypes = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
add() {
|
||||
this.$refs.modalCreateContactFieldType.open();
|
||||
},
|
||||
|
||||
closeModal() {
|
||||
this.$refs.modalCreateContactFieldType.close();
|
||||
this.$refs.modalEditContactFieldType.close();
|
||||
this.$refs.modalDeleteContactFieldType.close();
|
||||
},
|
||||
|
||||
store() {
|
||||
this.persistClient(
|
||||
'post', 'settings/personalization/contactfieldtypes',
|
||||
this.createForm, this.submitted
|
||||
);
|
||||
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('settings.personalization_contact_field_type_add_success'),
|
||||
text: '',
|
||||
width: '500px',
|
||||
type: 'success'
|
||||
});
|
||||
},
|
||||
|
||||
edit(contactFieldType) {
|
||||
this.editForm.id = contactFieldType.id;
|
||||
this.editForm.name = contactFieldType.name;
|
||||
this.editForm.protocol = contactFieldType.protocol;
|
||||
this.editForm.icon = contactFieldType.fontawesome_icon;
|
||||
|
||||
this.$refs.modalEditContactFieldType.open();
|
||||
},
|
||||
|
||||
update() {
|
||||
this.persistClient(
|
||||
'put', 'settings/personalization/contactfieldtypes/' + this.editForm.id,
|
||||
this.editForm, this.edited
|
||||
);
|
||||
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('settings.personalization_contact_field_type_edit_success'),
|
||||
text: '',
|
||||
width: '500px',
|
||||
type: 'success'
|
||||
});
|
||||
},
|
||||
|
||||
showDelete(contactFieldType) {
|
||||
this.editForm.id = contactFieldType.id;
|
||||
|
||||
this.$refs.modalDeleteContactFieldType.open();
|
||||
},
|
||||
|
||||
trash() {
|
||||
this.persistClient(
|
||||
'delete', 'settings/personalization/contactfieldtypes/' + this.editForm.id,
|
||||
this.editForm, this.deleted
|
||||
);
|
||||
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('settings.personalization_contact_field_type_delete_success'),
|
||||
text: '',
|
||||
width: '500px',
|
||||
type: 'success'
|
||||
});
|
||||
},
|
||||
|
||||
persistClient(method, uri, form, success) {
|
||||
form.errors = {};
|
||||
|
||||
axios[method](uri, form)
|
||||
.then(response => {
|
||||
this.getContactFieldTypes();
|
||||
|
||||
form.id = '';
|
||||
form.name = '';
|
||||
form.protocol = '';
|
||||
form.icon = '';
|
||||
form.errors = [];
|
||||
|
||||
this.closeModal();
|
||||
|
||||
success = true;
|
||||
})
|
||||
.catch(error => {
|
||||
if (typeof error.response.data === 'object') {
|
||||
form.errors = _.flatten(_.toArray(error.response.data));
|
||||
} else {
|
||||
form.errors = [this.$t('app.error_try_again')];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus on modal open.
|
||||
*/
|
||||
_focusCreateInput() {
|
||||
const vm = this;
|
||||
setTimeout(function() {
|
||||
vm.$refs.createName.focus();
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus on modal open.
|
||||
*/
|
||||
_focusEditInput() {
|
||||
const vm = this;
|
||||
setTimeout(function() {
|
||||
vm.$refs.editName.focus();
|
||||
}, 10);
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
128
resources/js/components/settings/DAVResources.vue
Normal file
128
resources/js/components/settings/DAVResources.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<style scoped>
|
||||
.url {
|
||||
width: 650px;
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h2>{{ $t('settings.dav_title') }}</h2>
|
||||
<p>{{ $t('settings.dav_description') }}</p>
|
||||
|
||||
<notifications group="dav" position="top middle" :duration="5000" width="400" />
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="dav_url_base">
|
||||
{{ $t('settings.dav_url_base') }}
|
||||
</label>
|
||||
<br />
|
||||
<input id="dav_url_base" :value="davRoute" class="url form-control" type="text" readonly />
|
||||
<a class="btn btn-primary" :title="$t('settings.dav_copy_help')" href="" @click.prevent="copyIntoClipboard(davRoute)">
|
||||
{{ $t('app.copy') }}
|
||||
</a>
|
||||
<p>
|
||||
{{ $t('settings.dav_connect_help') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('settings.dav_connect_help2') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<h2>{{ $t('settings.dav_title_carddav') }}</h2>
|
||||
<div class="form-group">
|
||||
<label for="dav_url_carddav">
|
||||
{{ $t('settings.dav_url_carddav') }}
|
||||
</label>
|
||||
<br />
|
||||
<input id="dav_url_carddav" :value="cardDavRoute" class="url form-control" type="text" readonly />
|
||||
<a class="btn btn-primary" :title="$t('settings.dav_copy_help')" href="" @click.prevent="copyIntoClipboard(cardDavRoute)">
|
||||
{{ $t('app.copy') }}
|
||||
</a>
|
||||
<br />
|
||||
<a :href="cardDavRoute+'?export'">
|
||||
{{ $t('settings.dav_carddav_export') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<h2>{{ $t('settings.dav_title_caldav') }}</h2>
|
||||
<div class="form-group">
|
||||
<label for="dav_url_caldav_birthdays">
|
||||
{{ $t('settings.dav_url_caldav_birthdays') }}
|
||||
</label>
|
||||
<br />
|
||||
<input id="dav_url_caldav_birthdays" :value="calDavBirthdaysRoute" class="url form-control" type="text" readonly />
|
||||
<a class="btn btn-primary" :title="$t('settings.dav_copy_help')" href="" @click.prevent="copyIntoClipboard(calDavBirthdaysRoute)">
|
||||
{{ $t('app.copy') }}
|
||||
</a>
|
||||
<br />
|
||||
<a :href="calDavBirthdaysRoute+'?export'">
|
||||
{{ $t('settings.dav_caldav_birthdays_export') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="dav_url_caldav_tasks">
|
||||
{{ $t('settings.dav_url_caldav_tasks') }}
|
||||
</label>
|
||||
<br />
|
||||
<input id="dav_url_caldav_tasks" :value="calDavTasksRoute" class="url form-control" type="text" readonly />
|
||||
<a class="btn btn-primary" :title="$t('settings.dav_copy_help')" href="" @click.prevent="copyIntoClipboard(calDavTasksRoute)">
|
||||
{{ $t('app.copy') }}
|
||||
</a>
|
||||
<br />
|
||||
<a :href="calDavTasksRoute+'?export'">
|
||||
{{ $t('settings.dav_caldav_tasks_export') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
davRoute: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
cardDavRoute: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
calDavBirthdaysRoute: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
calDavTasksRoute: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
copyIntoClipboard(text) {
|
||||
this.$copyText(text)
|
||||
.then(response => {
|
||||
this.notify(this.$t('settings.dav_clipboard_copied'), true);
|
||||
});
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'dav',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
443
resources/js/components/settings/Genders.vue
Normal file
443
resources/js/components/settings/Genders.vue
Normal file
@@ -0,0 +1,443 @@
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="main" position="bottom right" />
|
||||
|
||||
<h3 class="mb3">
|
||||
{{ $t('settings.personalization_genders_title') }}
|
||||
<a class="btn nt2" :class="[ dirltr ? 'fr' : 'fl' ]" href="" @click.prevent="showCreateModal">
|
||||
{{ $t('settings.personalization_genders_add') }}
|
||||
</a>
|
||||
</h3>
|
||||
<p>{{ $t('settings.personalization_genders_desc') }}</p>
|
||||
|
||||
<div class="dt dt--fixed w-100 collapse br--top br--bottom">
|
||||
<div class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_genders_table_name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_genders_table_sex') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_genders_table_default') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_actions') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="gender in genders" :key="gender.id"
|
||||
class="dt-row bb b--light-gray"
|
||||
>
|
||||
<div class="dtc">
|
||||
<div class="pa2">
|
||||
{{ gender.name }}
|
||||
<span class="i">
|
||||
{{ $tc('settings.personalization_genders_list_contact_number', gender.numberOfContacts, { count: gender.numberOfContacts }) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<div class="pa2">
|
||||
{{ $t('settings.personalization_genders_' + gender.type.toLowerCase()) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
<div class="pa2">
|
||||
<template v-if="gender.isDefault">
|
||||
{{ $t('settings.personalization_genders_default') }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2">
|
||||
<em class="fa fa-pencil-square-o pointer pr2" @click="showEdit(gender)"></em>
|
||||
<em v-if="genders.length > 1" class="fa fa-trash-o pointer" @click="showDelete(gender)"></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt2" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<a class="pointer" href="" @click.prevent="showDefaultGenderModal">{{ $t('settings.personalization_genders_make_default') }}</a>
|
||||
</div>
|
||||
|
||||
<!-- Create Gender type -->
|
||||
<sweet-modal ref="createModal" overlay-theme="dark" :title="$t('settings.personalization_genders_modal_add')">
|
||||
<form @submit.prevent="store()">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<form-input
|
||||
:id="''"
|
||||
v-model="createForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_modal_name')"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_genders_modal_name_help') }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<form-select
|
||||
:id="''"
|
||||
v-model="createForm.type"
|
||||
:options="genderTypes"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_modal_sex')"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_genders_modal_sex_help') }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<form-toggle
|
||||
:id="''"
|
||||
v-model="createForm.isDefault"
|
||||
:labels="toggleOptions"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_modal_default')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="store()">
|
||||
{{ $t('app.save') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Edit gender type -->
|
||||
<sweet-modal ref="updateModal" overlay-theme="dark" :title="$t('settings.personalization_genders_modal_edit')">
|
||||
<form @submit.prevent="update(updatedGender)">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<form-input
|
||||
:id="''"
|
||||
v-model="updateForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_modal_name')"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_genders_modal_name_help') }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<form-select
|
||||
:id="''"
|
||||
v-model="updateForm.type"
|
||||
:options="genderTypes"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_modal_sex')"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.personalization_genders_modal_sex_help') }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<form-toggle
|
||||
:id="''"
|
||||
v-model="updateForm.isDefault"
|
||||
:labels="toggleOptions"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_modal_default')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeUpdateModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="update(updatedGender)">
|
||||
{{ $t('app.update') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Delete Gender type -->
|
||||
<sweet-modal ref="deleteModal" overlay-theme="dark" :title="$t('settings.personalization_genders_modal_delete')">
|
||||
<form>
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb4">
|
||||
<p class="mb2">
|
||||
{{ $t('settings.personalization_genders_modal_delete_desc', {name: deleteForm.name}) }}
|
||||
</p>
|
||||
<div v-if="deleteForm.numberOfContacts !== 0 || deleteForm.isDefault">
|
||||
<p v-if="deleteForm.numberOfContacts !== 0">
|
||||
{{ $tc('settings.personalization_genders_modal_delete_question', deleteForm.numberOfContacts, {count: deleteForm.numberOfContacts}) }}
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ $tc('settings.personalization_genders_modal_delete_question_default') }}
|
||||
</p>
|
||||
<form-select
|
||||
:id="'deleteNewId'"
|
||||
v-model="deleteForm.newId"
|
||||
:options="genders"
|
||||
:required="true"
|
||||
:title="''"
|
||||
:excluded-id="deleteForm.id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a v-if="deleteForm.numberOfContacts === 0 && ! deleteForm.isDefault"
|
||||
class="btn btn-primary"
|
||||
href=""
|
||||
@click.prevent="trash()"
|
||||
>
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
<a v-else class="btn btn-primary" href="" @click.prevent="trashAndReplace()">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Change default Gender -->
|
||||
<sweet-modal ref="defaultGenderModal" overlay-theme="dark" :title="$t('settings.personalization_genders_modal_default')">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<form-select
|
||||
:id="''"
|
||||
v-model="defaultGenderId"
|
||||
:options="genders"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_genders_select_default')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDefaultGenderModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="updateDefaultGender()">
|
||||
{{ $t('app.save') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
genders: [],
|
||||
genderTypes: [],
|
||||
updatedGender: {
|
||||
id: '',
|
||||
name: '',
|
||||
type: '',
|
||||
isDefault: false,
|
||||
},
|
||||
|
||||
errorMessage: '',
|
||||
|
||||
createForm: {
|
||||
name: '',
|
||||
type: '',
|
||||
isDefault: false,
|
||||
errors: []
|
||||
},
|
||||
|
||||
updateForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
type: '',
|
||||
isDefault: false,
|
||||
errors: []
|
||||
},
|
||||
|
||||
deleteForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
isDefault: false,
|
||||
numberOfContacts: 0,
|
||||
newId: 0
|
||||
},
|
||||
|
||||
defaultGenderId: null,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
},
|
||||
|
||||
toggleOptions() {
|
||||
return {
|
||||
checked: this.$t('app.yes'),
|
||||
unchecked: this.$t('app.no')
|
||||
};
|
||||
},
|
||||
|
||||
defaultGenderType() {
|
||||
const defaultGender = _.findIndex(this.genders, ['isDefault', true]);
|
||||
return this.genders[defaultGender >= 0 ? defaultGender : 0];
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
axios.all([
|
||||
this.getGenders(),
|
||||
this.getGenderTypes()
|
||||
]);
|
||||
},
|
||||
|
||||
setDefaultGenderType() {
|
||||
this.createForm.type = this.defaultGenderType.type;
|
||||
},
|
||||
|
||||
getGenders() {
|
||||
return axios.get('settings/personalization/genders')
|
||||
.then(response => {
|
||||
this.genders = _.toArray(response.data);
|
||||
this.setDefaultGenderType();
|
||||
});
|
||||
},
|
||||
|
||||
getGenderTypes() {
|
||||
return axios.get('settings/personalization/genderTypes')
|
||||
.then(response => {
|
||||
this.genderTypes = _.toArray(response.data);
|
||||
});
|
||||
},
|
||||
|
||||
closeModal() {
|
||||
this.$refs.createModal.close();
|
||||
},
|
||||
|
||||
closeUpdateModal() {
|
||||
this.$refs.updateModal.close();
|
||||
},
|
||||
|
||||
closeDeleteModal() {
|
||||
this.$refs.deleteModal.close();
|
||||
},
|
||||
|
||||
closeDefaultGenderModal() {
|
||||
this.$refs.defaultGenderModal.close();
|
||||
},
|
||||
|
||||
showCreateModal() {
|
||||
this.$refs.createModal.open();
|
||||
},
|
||||
|
||||
showDefaultGenderModal() {
|
||||
this.defaultGenderId = this.defaultGenderType.id;
|
||||
this.$refs.defaultGenderModal.open();
|
||||
},
|
||||
|
||||
store() {
|
||||
axios.post('settings/personalization/genders', this.createForm)
|
||||
.then(response => {
|
||||
this.closeModal();
|
||||
this.getGenders();
|
||||
this.createForm.name = '';
|
||||
this.createForm.type = '';
|
||||
});
|
||||
},
|
||||
|
||||
showEdit(gender) {
|
||||
this.updateForm.id = gender.id.toString();
|
||||
this.updateForm.name = gender.name;
|
||||
this.updateForm.type = gender.type;
|
||||
this.updateForm.isDefault = gender.isDefault;
|
||||
this.updatedGender = gender;
|
||||
|
||||
this.$refs.updateModal.open();
|
||||
},
|
||||
|
||||
update() {
|
||||
axios.put('settings/personalization/genders/' + this.updateForm.id, this.updateForm)
|
||||
.then(response => {
|
||||
this.closeUpdateModal();
|
||||
this.getGenders();
|
||||
this.updatedGender.name = this.updateForm.name;
|
||||
this.updatedGender.type = this.updateForm.type;
|
||||
this.updatedGender.isDefault = this.updateForm.isDefault;
|
||||
this.updateForm.name = '';
|
||||
});
|
||||
},
|
||||
|
||||
showDelete(gender) {
|
||||
this.errorMessage = '';
|
||||
this.deleteForm.name = gender.name;
|
||||
this.deleteForm.id = gender.id.toString();
|
||||
this.deleteForm.isDefault = gender.isDefault;
|
||||
this.deleteForm.numberOfContacts = gender.numberOfContacts;
|
||||
|
||||
this.$refs.deleteModal.open();
|
||||
},
|
||||
|
||||
trash() {
|
||||
axios.delete('settings/personalization/genders/' + this.deleteForm.id)
|
||||
.then(response => {
|
||||
this.closeDeleteModal();
|
||||
this.getGenders();
|
||||
});
|
||||
},
|
||||
|
||||
trashAndReplace() {
|
||||
axios.delete('settings/personalization/genders/' + this.deleteForm.id + '/replaceby/' + this.deleteForm.newId)
|
||||
.then(response => {
|
||||
this.closeDeleteModal();
|
||||
this.getGenders();
|
||||
})
|
||||
.catch(error => {
|
||||
if (typeof error.response.data === 'object') {
|
||||
this.errorMessage = error.response.data.message;
|
||||
} else {
|
||||
this.errorMessage = this.$t('app.error_try_again');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateDefaultGender() {
|
||||
axios.put('settings/personalization/genders/default/' + this.defaultGenderId)
|
||||
.then(response => {
|
||||
this.closeDefaultGenderModal();
|
||||
this.getGenders();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
300
resources/js/components/settings/LifeEventTypes.vue
Normal file
300
resources/js/components/settings/LifeEventTypes.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="lifeEventTypes" position="bottom right" />
|
||||
|
||||
<h3 class="with-actions">
|
||||
{{ $t('settings.personalization_life_event_category_title') }}
|
||||
</h3>
|
||||
<p>{{ $t('settings.personalization_life_event_category_description') }}</p>
|
||||
|
||||
<div v-if="limited" class="mt3 mb3 form-information-message br2">
|
||||
<div class="pa3 flex">
|
||||
<div class="mr3">
|
||||
<svg viewBox="0 0 20 20">
|
||||
<g fill-rule="evenodd">
|
||||
<circle cx="10" cy="10" r="9" fill="currentColor" /><path d="M10 0C4.486 0 0 4.486 0 10s4.486 10 10 10 10-4.486 10-10S15.514 0 10 0m0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8m1-5v-3a1 1 0 0 0-1-1H9a1 1 0 1 0 0 2v3a1 1 0 0 0 1 1h1a1 1 0 1 0 0-2m-1-5.9a1.1 1.1 0 1 0 0-2.2 1.1 1.1 0 0 0 0 2.2" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-html="$t('settings.personalisation_paid_upgrade_vue', {url: 'settings/subscriptions' })"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dt dt--fixed w-100 collapse br--top br--bottom">
|
||||
<div class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_live_event_category_table_name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_live_event_category_table_actions') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="lifeEventCategory in lifeEventCategories" :key="lifeEventCategory.id" class="dt dt--fixed w-100 collapse br--top br--bottom mt3">
|
||||
<!-- LIFE EVENT CATEGORY -->
|
||||
<div class="dt-row hover bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
<strong>{{ $t('people.life_event_category_' + lifeEventCategory.default_life_event_category_key) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc">
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="lifeEventType in lifeEventCategory.lifeEventTypes" :key="lifeEventType.id" class="dt-row hover bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2 pl4">
|
||||
<template v-if="lifeEventType.name">
|
||||
{{ lifeEventType.name }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('people.life_event_sentence_' + lifeEventType.default_life_event_type_key) }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2">
|
||||
<em v-if="!limited" class="fa fa-pencil-square-o pointer pr2" @click="showEditType(lifeEventType, lifeEventCategory.id)"></em>
|
||||
<em v-if="!limited" class="fa fa-trash-o pointer" @click="showDeleteType(lifeEventType)"></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!limited" class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 pl4">
|
||||
<a class="pointer" href=""
|
||||
@click.prevent="showCreateType(lifeEventCategory)"
|
||||
>
|
||||
{{ $t('settings.personalization_life_event_type_add_button') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Create Life Event Type -->
|
||||
<sweet-modal ref="createTypeModal" overlay-theme="dark" :title="$t('settings.personalization_life_event_type_modal_add')">
|
||||
<form @submit.prevent="storeType()">
|
||||
<div class="mb4">
|
||||
<p class="b mb2"></p>
|
||||
<form-input
|
||||
:id="'add-type-name'"
|
||||
v-model="createTypeForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_life_event_type_modal_question')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeCreateTypeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="storeType()">
|
||||
{{ $t('app.save') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Update Life Event Type -->
|
||||
<sweet-modal ref="updateTypeModal" overlay-theme="dark" :title="$t('settings.personalization_life_event_type_modal_edit')">
|
||||
<form @submit.prevent="updateType()">
|
||||
<div class="mb4">
|
||||
<p class="b mb2"></p>
|
||||
<form-input
|
||||
:id="'update-type-name'"
|
||||
v-model="updateTypeForm.name"
|
||||
:input-type="'text'"
|
||||
:required="true"
|
||||
:title="$t('settings.personalization_life_event_type_modal_question')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeUpdateTypeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="updateType()">
|
||||
{{ $t('app.update') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Delete Life Event type -->
|
||||
<sweet-modal ref="deleteTypeModal" overlay-theme="dark" :title="$t('settings.personalization_life_event_type_modal_delete')">
|
||||
<form>
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb4">
|
||||
<p class="mb2">
|
||||
{{ $t('settings.personalization_life_event_type_modal_delete_desc') }}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteTypeModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="destroyType()">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
props: {
|
||||
limited: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
lifeEventCategories: [],
|
||||
errorMessage: '',
|
||||
|
||||
createTypeForm: {
|
||||
name: '',
|
||||
life_event_category_id: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
updateTypeForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
life_event_category_id: '',
|
||||
errors: []
|
||||
},
|
||||
|
||||
destroyTypeForm: {
|
||||
id: '',
|
||||
errors: []
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.getLifeEventCategories();
|
||||
},
|
||||
|
||||
getLifeEventCategories() {
|
||||
axios.get('settings/personalization/lifeeventcategories')
|
||||
.then(response => {
|
||||
this.lifeEventCategories = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
showCreateType(category) {
|
||||
this.$refs.createTypeModal.open();
|
||||
this.createTypeForm.life_event_category_id = category.id;
|
||||
},
|
||||
|
||||
showDeleteType(type) {
|
||||
this.destroyTypeForm.id = type.id;
|
||||
|
||||
this.$refs.deleteTypeModal.open();
|
||||
},
|
||||
|
||||
showEditType(type, categoryId) {
|
||||
this.updateTypeForm.id = type.id;
|
||||
this.updateTypeForm.name = type.name ? type.name : this.$t('people.life_event_sentence_' + type.default_life_event_type_key);
|
||||
this.updateTypeForm.life_event_category_id = categoryId;
|
||||
|
||||
this.$refs.updateTypeModal.open();
|
||||
},
|
||||
|
||||
closeCreateTypeModal() {
|
||||
this.$refs.createTypeModal.close();
|
||||
},
|
||||
|
||||
closeUpdateTypeModal() {
|
||||
this.$refs.updateTypeModal.close();
|
||||
},
|
||||
|
||||
closeDeleteTypeModal() {
|
||||
this.$refs.deleteTypeModal.close();
|
||||
},
|
||||
|
||||
storeType() {
|
||||
axios.post('settings/personalization/lifeeventtypes', this.createTypeForm)
|
||||
.then(response => {
|
||||
this.$refs.createTypeModal.close();
|
||||
this.createTypeForm.name = '';
|
||||
this.getLifeEventCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
});
|
||||
},
|
||||
|
||||
updateType() {
|
||||
axios.put('settings/personalization/lifeeventtypes/' + this.updateTypeForm.id, this.updateTypeForm)
|
||||
.then(response => {
|
||||
this.$refs.updateTypeModal.close();
|
||||
this.updateTypeForm.name = '';
|
||||
this.getLifeEventCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
});
|
||||
},
|
||||
|
||||
destroyType() {
|
||||
axios.delete('settings/personalization/lifeeventtypes/' + this.destroyTypeForm.id)
|
||||
.then(response => {
|
||||
this.$refs.deleteTypeModal.close();
|
||||
this.destroyTypeForm.id = '';
|
||||
this.getLifeEventCategories();
|
||||
|
||||
this.notify(this.$t('app.default_save_success'), true);
|
||||
})
|
||||
.catch(error => {
|
||||
this.errorMessage = error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'lifeEventTypes',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
188
resources/js/components/settings/MfaActivate.vue
Normal file
188
resources/js/components/settings/MfaActivate.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="mfa" position="bottom right" :duration="5000" width="400" />
|
||||
|
||||
<h3>{{ $t('settings.2fa_otp_title') }}</h3>
|
||||
|
||||
<div class="form-group">
|
||||
<a v-if="selectActivated" class="btn btn-warning" href="" @click.prevent="showDisableModal">
|
||||
{{ $t('settings.2fa_disable_title') }}
|
||||
</a>
|
||||
<a v-else class="btn btn-primary" href="" @click.prevent="showEnableModal">
|
||||
{{ $t('settings.2fa_enable_title') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<sweet-modal id="enableModal" ref="enableModal" overlay-theme="dark" :title="$t('settings.2fa_otp_title')">
|
||||
<form @submit.prevent="register()">
|
||||
<p>{{ $t('settings.2fa_enable_description') }}</p>
|
||||
|
||||
<div class="panel-body">
|
||||
{{ $t('settings.2fa_enable_otp') }}
|
||||
<div v-html="image"></div>
|
||||
<p>
|
||||
{{ $t('settings.2fa_enable_otp_help') }}
|
||||
<code id="secretkey">
|
||||
{{ secret }}
|
||||
</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<p>
|
||||
{{ $t('settings.2fa_enable_otp_validate') }}
|
||||
</p>
|
||||
<form-input
|
||||
:id="'one_time_password1'"
|
||||
v-model="one_time_password"
|
||||
:title="$t('auth.2fa_one_time_password')"
|
||||
:input-type="'number'"
|
||||
:width="100"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a id="verify1" class="btn btn-primary" href="" @click.prevent="register()">
|
||||
{{ $t('app.verify') }}
|
||||
</a>
|
||||
<a class="btn" href="" @click.prevent="closeEnableModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<sweet-modal id="disableModal" ref="disableModal" overlay-theme="dark" :title="$t('settings.2fa_otp_title')">
|
||||
<form @submit.prevent="register()">
|
||||
<p>{{ $t('settings.2fa_disable_description') }}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<form-input
|
||||
:id="'one_time_password2'"
|
||||
v-model="one_time_password"
|
||||
:title="$t('auth.2fa_one_time_or_recuperation')"
|
||||
:input-type="'text'"
|
||||
:width="100"
|
||||
:required="true"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a id="verify2" class="btn btn-primary" href="" @click.prevent="unregister()">
|
||||
{{ $t('app.verify') }}
|
||||
</a>
|
||||
<a class="btn" href="" @click.prevent="closeDisableModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
props: {
|
||||
activated: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectActivated: false,
|
||||
errorMessage: '',
|
||||
infoMessage: '',
|
||||
success: false,
|
||||
one_time_password: '',
|
||||
image: '',
|
||||
secret: '',
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
activated: function (val) {
|
||||
this.selectActivated = val;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.selectActivated = this.activated;
|
||||
},
|
||||
|
||||
methods: {
|
||||
register() {
|
||||
axios.post('settings/security/2fa-enable', { one_time_password: this.one_time_password })
|
||||
.then(response => {
|
||||
this.closeEnableModal();
|
||||
this.selectActivated = response.data.success;
|
||||
if (response.data.success) {
|
||||
this.notify(this.$t('settings.2fa_enable_success'), true);
|
||||
} else {
|
||||
this.notify(this.$t('settings.2fa_enable_error'), false);
|
||||
}
|
||||
}).catch(error => {
|
||||
this.closeEnableModal();
|
||||
this.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
unregister() {
|
||||
axios.post('settings/security/2fa-disable', { one_time_password: this.one_time_password })
|
||||
.then(response => {
|
||||
this.closeDisableModal();
|
||||
this.selectActivated = ! response.data.success;
|
||||
if (response.data.success) {
|
||||
this.notify(this.$t('settings.2fa_disable_success'), true);
|
||||
} else {
|
||||
this.notify(this.$t('settings.2fa_disable_error'), false);
|
||||
}
|
||||
}).catch(error => {
|
||||
this.closeDisableModal();
|
||||
this.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
showEnableModal() {
|
||||
this.one_time_password = '';
|
||||
axios.get('settings/security/2fa-enable')
|
||||
.then(response => {
|
||||
this.image = response.data.image;
|
||||
this.secret = response.data.secret;
|
||||
this.$refs.enableModal.open();
|
||||
}).catch(error => {
|
||||
this.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
showDisableModal() {
|
||||
this.one_time_password = '';
|
||||
this.$refs.disableModal.open();
|
||||
},
|
||||
|
||||
closeEnableModal() {
|
||||
this.$refs.enableModal.close();
|
||||
},
|
||||
|
||||
closeDisableModal() {
|
||||
this.$refs.disableModal.close();
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'mfa',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
109
resources/js/components/settings/Modules.vue
Normal file
109
resources/js/components/settings/Modules.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="reminder-rules">
|
||||
<notifications group="main" position="bottom right" />
|
||||
|
||||
<h3 class="mb3">
|
||||
{{ $t('settings.personalization_module_title') }}
|
||||
</h3>
|
||||
<p>
|
||||
{{ $t('settings.personalization_module_desc') }}
|
||||
</p>
|
||||
|
||||
<div v-if="limited" class="mt3 mb3 form-information-message br2">
|
||||
<div class="pa3 flex">
|
||||
<div class="mr3">
|
||||
<svg viewBox="0 0 20 20">
|
||||
<g fill-rule="evenodd">
|
||||
<circle cx="10" cy="10" r="9" fill="currentColor" /><path d="M10 0C4.486 0 0 4.486 0 10s4.486 10 10 10 10-4.486 10-10S15.514 0 10 0m0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8m1-5v-3a1 1 0 0 0-1-1H9a1 1 0 1 0 0 2v3a1 1 0 0 0 1 1h1a1 1 0 1 0 0-2m-1-5.9a1.1 1.1 0 1 0 0-2.2 1.1 1.1 0 0 0 0 2.2" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-html="$t('settings.personalisation_paid_upgrade_vue', {url: 'settings/subscriptions' })"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dt dt--fixed w-100 collapse br--top br--bottom">
|
||||
<div class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_actions') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="module in modules" :key="module.id" class="dt-row hover bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2">
|
||||
{{ module.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2">
|
||||
<form-toggle
|
||||
v-model="module.active"
|
||||
:iclass="'module-'"
|
||||
:disabled="limited"
|
||||
:labels="true"
|
||||
@change="toggle(module, $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
limited: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
modules: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.getModules();
|
||||
},
|
||||
|
||||
methods: {
|
||||
getModules() {
|
||||
axios.get('settings/personalization/modules')
|
||||
.then(response => {
|
||||
this.modules = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
toggle(module) {
|
||||
axios.post('settings/personalization/modules/' + module.id)
|
||||
.then(response => {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('settings.personalization_module_save'),
|
||||
text: '',
|
||||
type: 'success'
|
||||
});
|
||||
this.$set(module, 'active', response.data.data.active);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
139
resources/js/components/settings/RecoveryCodes.vue
Normal file
139
resources/js/components/settings/RecoveryCodes.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<style scoped>
|
||||
.code {
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
.used {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>{{ $t('settings.recovery_title') }}</h3>
|
||||
<div class="form-group">
|
||||
<a class="btn btn-primary" href="" @click.prevent="showRecoveryModal">
|
||||
{{ $t('settings.recovery_show') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<sweet-modal id="recoveryModal" ref="recoveryModal" overlay-theme="dark" :title="$t('settings.recovery_title')">
|
||||
<notifications group="recovery" position="top middle" :duration="5000" width="400" />
|
||||
|
||||
<p>{{ $t('settings.recovery_help_intro') }}</p>
|
||||
<p :class="[ dirltr ? 'ml3' : 'mr3' ]">
|
||||
<span v-for="code in codes" :key="code.id" v-cy-name="'recovery-' + code.id">
|
||||
<pre class="code" :class="[ code.used ? 'used' : '' ]" :title="[ code.used ? usedHelp : '']">{{ code.recovery }}</pre>
|
||||
</span>
|
||||
</p>
|
||||
<p>{{ $t('settings.recovery_help_information') }}</p>
|
||||
<div slot="button">
|
||||
<span :class="[ dirltr ? 'fl' : 'fr' ]">
|
||||
<a class="btn" href="" @click.prevent="generateNewCodes">
|
||||
{{ $t('settings.recovery_generate') }}
|
||||
</a>
|
||||
<br />
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.recovery_generate_help') }}
|
||||
</small>
|
||||
</span>
|
||||
<span :class="[ dirltr ? 'fr' : 'fl' ]">
|
||||
<a class="btn btn-primary" :title="copyHelp" href="" @click.prevent="copyIntoClipboard">
|
||||
{{ $t('app.copy') }}
|
||||
</a>
|
||||
<!--
|
||||
<a @click.prevent="download" class="btn" href="">{{ $t('app.download') }}</a>
|
||||
-->
|
||||
<a class="btn" href="" @click.prevent="closeRecoveryModal">
|
||||
{{ $t('app.close') }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
codes: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
},
|
||||
usedHelp() {
|
||||
return this.$t('settings.recovery_already_used_help');
|
||||
},
|
||||
copyHelp() {
|
||||
return this.$t('settings.recovery_copy_help');
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
showRecoveryModal() {
|
||||
this.codes = [];
|
||||
axios.post('settings/security/recovery-codes')
|
||||
.then(response => {
|
||||
this.codes = response.data;
|
||||
this.$refs.recoveryModal.open();
|
||||
}).catch(error => {
|
||||
this.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
generateNewCodes() {
|
||||
this.codes = [];
|
||||
axios.post('settings/security/generate-recovery-codes')
|
||||
.then(response => {
|
||||
this.codes = response.data;
|
||||
}).catch(error => {
|
||||
this.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
closeRecoveryModal() {
|
||||
this.$refs.recoveryModal.close();
|
||||
},
|
||||
|
||||
copyIntoClipboard() {
|
||||
this.$copyText(this.getDataStream())
|
||||
.then(response => {
|
||||
this.notify(this.$t('settings.recovery_clipboard'), true);
|
||||
});
|
||||
},
|
||||
|
||||
getDataStream() {
|
||||
var text = this.$t('settings.recovery_help_intro')+'\n';
|
||||
var i = 1;
|
||||
this.codes.forEach(code => {
|
||||
if (code.used) {
|
||||
text += i + '. ---------\n';
|
||||
} else {
|
||||
text += i + '. ' + code.recovery + '\n';
|
||||
}
|
||||
i++;
|
||||
});
|
||||
return text;
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'recovery',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
92
resources/js/components/settings/ReminderRules.vue
Normal file
92
resources/js/components/settings/ReminderRules.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="reminder-rules">
|
||||
<notifications group="main" position="bottom right" />
|
||||
|
||||
<h3 class="mb3">
|
||||
{{ $t('settings.personalization_reminder_rule_title') }}
|
||||
</h3>
|
||||
<p>
|
||||
{{ $t('settings.personalization_reminder_rule_desc') }}
|
||||
</p>
|
||||
|
||||
<div class="dt dt--fixed w-100 collapse br--top br--bottom">
|
||||
<div class="dt-row">
|
||||
<div class="dtc">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_name') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2 b">
|
||||
{{ $t('settings.personalization_contact_field_type_table_actions') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="reminderRule in reminderRules" :key="reminderRule.id" class="dt-row bb b--light-gray">
|
||||
<div class="dtc">
|
||||
<div class="pa2">
|
||||
{{ $tc('settings.personalization_reminder_rule_line', reminderRule.number_of_days_before, {count: reminderRule.number_of_days_before}) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dtc" :class="[ dirltr ? 'tr' : 'tl' ]">
|
||||
<div class="pa2">
|
||||
<form-toggle
|
||||
v-model="reminderRule.active"
|
||||
:iclass="'reminder-rule-' + reminderRule.number_of_days_before"
|
||||
:labels="true"
|
||||
@change="toggle(reminderRule, $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
reminderRules: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir === 'ltr';
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.getReminderRules();
|
||||
},
|
||||
|
||||
getReminderRules() {
|
||||
axios.get('settings/personalization/reminderrules')
|
||||
.then(response => {
|
||||
this.reminderRules = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
toggle(reminderRule, event) {
|
||||
axios.post('settings/personalization/reminderrules/' + reminderRule.id)
|
||||
.then(response => {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('settings.personalization_reminder_rule_save'),
|
||||
text: '',
|
||||
type: 'success'
|
||||
});
|
||||
this.$set(reminderRule, 'active', response.data.data.active);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
108
resources/js/components/settings/ReminderTime.vue
Normal file
108
resources/js/components/settings/ReminderTime.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Timezone -->
|
||||
<div class="form-group">
|
||||
<form-select
|
||||
:id="'timezone'"
|
||||
:value="updatedTimezone"
|
||||
:options="timezones"
|
||||
:title="$t('settings.timezone')"
|
||||
:required="true"
|
||||
:iclass="'form-control'"
|
||||
@input="timezoneUpdate"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Reminders -->
|
||||
<div class="form-group">
|
||||
<form-select
|
||||
:id="'reminder_time'"
|
||||
:value="updatedReminder"
|
||||
:options="hours"
|
||||
:title="$t('settings.reminder_time_to_send')"
|
||||
:required="true"
|
||||
:iclass="'form-control'"
|
||||
@input="reminderUpdate"
|
||||
/>
|
||||
<small class="form-text text-muted" v-html="message"></small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
timezone: {
|
||||
type: String,
|
||||
default: 'UTC',
|
||||
},
|
||||
timezones: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
reminder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hours: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
message: '',
|
||||
updatedTimezone: '',
|
||||
updatedReminder: ''
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.updatedReminder = this.reminder;
|
||||
this.updatedTimezone = this.timezone;
|
||||
this.computeMessage();
|
||||
},
|
||||
|
||||
timezoneUpdate: function(event) {
|
||||
this.updatedTimezone = event;
|
||||
this.computeMessage();
|
||||
},
|
||||
|
||||
reminderUpdate: function(event) {
|
||||
this.updatedReminder = event;
|
||||
this.computeMessage();
|
||||
},
|
||||
|
||||
computeMessage() {
|
||||
var moment = require('moment-timezone');
|
||||
moment.locale(this._i18n.locale);
|
||||
moment.tz.setDefault('UTC');
|
||||
|
||||
var now = moment();
|
||||
var t = now.format('YYYY-MM-DD ' + this.updatedReminder + ':00');
|
||||
|
||||
var date = moment.tz(t, this.updatedTimezone);
|
||||
|
||||
if (date.isBefore(now)) {
|
||||
date = date.add(1, 'days');
|
||||
}
|
||||
|
||||
this.message = this.$t('settings.reminder_time_to_send_help', {
|
||||
dateTime: date.format('LLL'),
|
||||
dateTimeUtc: date.utc().format('YYYY-MM-DD HH:mm z')
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
297
resources/js/components/settings/Subscription.vue
Normal file
297
resources/js/components/settings/Subscription.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<form ref="form" class="mb4" :action="callback" method="post" @submit.prevent="subscribe()">
|
||||
<notifications group="subscription" position="top middle" :duration="5000" width="400" />
|
||||
|
||||
<div class="form-group">
|
||||
<div v-if="errors" role="alert" class="alert alert-danger w-100">
|
||||
{{ errors }}
|
||||
</div>
|
||||
|
||||
<div v-if="paymentSucceeded">
|
||||
<h1 class="text-xl mt-2 mb-4 text-gray-700">
|
||||
{{ $t('settings.subscriptions_payment_succeeded_title') }}
|
||||
</h1>
|
||||
<p v-if="successMessage" class="mb-6">
|
||||
{{ successMessage }}
|
||||
</p>
|
||||
<p v-else class="mb-6">
|
||||
{{ $t('settings.subscriptions_payment_succeeded') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="paymentCancelled">
|
||||
<h1 class="text-xl mt-2 mb-4 text-gray-700">
|
||||
{{ $t('settings.subscriptions_payment_cancelled_title') }}
|
||||
</h1>
|
||||
|
||||
<p class="mb-6">
|
||||
{{ $t('settings.subscriptions_payment_cancelled') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="! paymentProcessed" id="payment-elements" class="b--gray-monica ba pa4 br2 mb3 bg-black-05">
|
||||
<div class="form-row">
|
||||
<div class="mb3">
|
||||
<form-input
|
||||
:id="'cardholder-name'"
|
||||
v-model="selectedName"
|
||||
:input-type="'text'"
|
||||
:iclass="'br3 b--black-30 ba pa3 w-100 f4'"
|
||||
:required="true"
|
||||
:title="$t('settings.subscriptions_upgrade_name')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb3">
|
||||
<form-input
|
||||
:id="'address-zip'"
|
||||
v-model="zip"
|
||||
:input-type="'text'"
|
||||
:iclass="'br3 b--black-30 ba pa3 w-100 f4'"
|
||||
:title="$t('settings.subscriptions_upgrade_zip')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label for="card-element">
|
||||
{{ $t('settings.subscriptions_upgrade_credit') }}
|
||||
</label>
|
||||
<div id="card-element">
|
||||
<!-- a Stripe Element will be inserted here. -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="card-button"
|
||||
class="btn btn-primary w-100 mt3"
|
||||
:disabled="paymentProcessing"
|
||||
@click.prevent="confirm ? confirmPayment() : subscribe()"
|
||||
v-html="$t('settings.subscriptions_upgrade_submit', { amount: amount })"
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<a v-if="paymentProcessed" :href="callback"
|
||||
class="btn btn-secondary w-100 tc"
|
||||
>
|
||||
{{ $t('app.go_back') }}
|
||||
</a>
|
||||
</div>
|
||||
<input type="hidden" name="_token" :value="token" />
|
||||
<input type="hidden" name="plan" :value="plan" />
|
||||
<input type="hidden" name="payment_method" :value="paymentMethod" />
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
stripeKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
clientSecret: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
plan: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
amount: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
callback: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
confirm: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
paymentSucceeded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
paymentCancelled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectedName: '',
|
||||
stripe: null,
|
||||
zip: '',
|
||||
errors: '',
|
||||
successMessage: '',
|
||||
cardElement: null,
|
||||
paymentMethod: '',
|
||||
paymentProcessing: false,
|
||||
paymentProcessed: false,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
name() {
|
||||
this.selectedName = this.name;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.selectedName = this.name;
|
||||
if (this.paymentSucceeded || this.paymentCancelled) {
|
||||
this.paymentProcessed = true;
|
||||
}
|
||||
if (! this.paymentProcessed) {
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
start() {
|
||||
this.stripe = Stripe(this.stripeKey);
|
||||
|
||||
const elements = this.stripe.elements();
|
||||
|
||||
// Custom styling can be passed to options when creating an Element.
|
||||
// (Note that this demo uses a wider set of styles than the guide below.)
|
||||
const style = {
|
||||
base: {
|
||||
color: '#32325d',
|
||||
lineHeight: '18px',
|
||||
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
|
||||
fontSmoothing: 'antialiased',
|
||||
fontSize: '16px',
|
||||
'::placeholder': {
|
||||
color: '#aab7c4'
|
||||
}
|
||||
},
|
||||
invalid: {
|
||||
color: '#fa755a',
|
||||
iconColor: '#fa755a'
|
||||
}
|
||||
};
|
||||
|
||||
// Create an instance of the card Element
|
||||
this.cardElement = elements.create('card', {
|
||||
hidePostalCode: true,
|
||||
style: style
|
||||
});
|
||||
|
||||
// Add an instance of the card Element into the `card-element` <div>
|
||||
this.cardElement.mount('#card-element');
|
||||
|
||||
// Handle real-time validation errors from the card Element.
|
||||
var self = this;
|
||||
this.cardElement.addEventListener('change', function(event) {
|
||||
if (event.error) {
|
||||
self.errors = event.error.message;
|
||||
} else {
|
||||
self.errors = '';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleError(error) {
|
||||
if (error.code === 'parameter_invalid_empty' &&
|
||||
error.param === 'payment_method_data[billing_details][name]') {
|
||||
this.errors = this.$t('settings.subscriptions_payment_error_name');
|
||||
} else {
|
||||
this.errors = error.message;
|
||||
}
|
||||
},
|
||||
|
||||
subscribe() {
|
||||
var self = this;
|
||||
|
||||
this.errors = '';
|
||||
this.paymentProcessing = true;
|
||||
this.paymentProcessed = false;
|
||||
|
||||
this.stripe.handleCardSetup(
|
||||
self.clientSecret,
|
||||
self.cardElement,
|
||||
{
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
name: self.selectedName,
|
||||
address: {
|
||||
postal_code: self.zip,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
).then(function (result) {
|
||||
self.paymentProcessing = false;
|
||||
if (result.error) {
|
||||
self.handleError(result.error);
|
||||
} else {
|
||||
// The card has been verified successfully...
|
||||
self.paymentProcessed = true;
|
||||
self.paymentSucceeded = true;
|
||||
self.successMessage = self.$t('settings.subscriptions_payment_success');
|
||||
self.notify(self.successMessage, true);
|
||||
self.processPayment(result.setupIntent);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
processPayment(setupIntent) {
|
||||
var self = this;
|
||||
this.paymentMethod = setupIntent.payment_method;
|
||||
setTimeout(function () {
|
||||
self.$refs.form.submit();
|
||||
}, 10);
|
||||
},
|
||||
|
||||
confirmPayment() {
|
||||
var self = this;
|
||||
|
||||
this.paymentProcessing = true;
|
||||
this.paymentProcessed = false;
|
||||
this.errorMessage = '';
|
||||
|
||||
this.stripe.handleCardPayment(
|
||||
self.clientSecret, self.cardElement, {
|
||||
payment_method_data: {
|
||||
billing_details: { name: this.selectedName }
|
||||
}
|
||||
}
|
||||
).then(function (result) {
|
||||
self.paymentProcessing = false;
|
||||
|
||||
if (result.error) {
|
||||
self.handleError(result.error);
|
||||
} else {
|
||||
self.paymentProcessed = true;
|
||||
self.paymentSucceeded = true;
|
||||
self.successMessage = self.$t('settings.subscriptions_payment_success');
|
||||
self.notify(self.successMessage, true);
|
||||
setTimeout(function () {
|
||||
window.location = self.callback;
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'subscription',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
402
resources/js/components/settings/WebauthnConnector.vue
Normal file
402
resources/js/components/settings/WebauthnConnector.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<style scoped>
|
||||
.time {
|
||||
color: gray;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<notifications group="webauthn" position="top middle" :duration="5000" width="400" />
|
||||
|
||||
<div v-if="method === 'register-modal'">
|
||||
<h3>{{ $t('settings.webauthn_title') }}</h3>
|
||||
|
||||
<div v-if="currentkeys !== null">
|
||||
<ul class="table">
|
||||
<li v-for="key in currentkeys"
|
||||
:key="key.id"
|
||||
class="table-row"
|
||||
>
|
||||
<div class="table-cell w-30">
|
||||
<strong>{{ key.name }}</strong>
|
||||
</div>
|
||||
<div class="table-cell time w-50">
|
||||
<template v-if="key.counter > 0">
|
||||
{{ $t('settings.webauthn_last_use', {timestamp: formatTime(key.updated_at)}) }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="table-cell actions">
|
||||
<a class="pointer" href="" @click.prevent="showDeleteModal(key.id)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<a v-if="isSupported" class="btn btn-primary" href="" @click.prevent="showRegisterModal">
|
||||
{{ $t('settings.webauthn_enable_description') }}
|
||||
</a>
|
||||
<small v-else>
|
||||
{{ notSupportedMessage() }}
|
||||
</small>
|
||||
|
||||
|
||||
<sweet-modal
|
||||
id="registerModal"
|
||||
ref="registerModal"
|
||||
overlay-theme="dark"
|
||||
:title="$t('settings.webauthn_title')"
|
||||
>
|
||||
<div v-if="registerTab === '1'">
|
||||
<p>
|
||||
{{ $t('settings.webauthn_key_name_help') }}
|
||||
</p>
|
||||
<form-input
|
||||
:id="'keyName'"
|
||||
v-model="keyName"
|
||||
:title="$t('settings.webauthn_key_name')"
|
||||
:value="keyName"
|
||||
:input-type="'text'"
|
||||
:width="150"
|
||||
:required="true"
|
||||
@keyup.enter="showRegisterModalTab('2');startRegister();"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="registerTab === '2'">
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="" @click.prevent="startRegister()">
|
||||
{{ $t('app.retry') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="infoMessage !== ''" class="form-information-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ infoMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage === ''" class="tc">
|
||||
<img src="https://ssl.gstatic.com/accounts/strongauth/Challenge_2SV-Gnubby_graphic.png"
|
||||
:alt="$t('settings.webauthn_insertKey')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage === ''" class="pa2">
|
||||
<p>
|
||||
{{ $t('settings.webauthn_insertKey') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('settings.webauthn_buttonAdvise') }}
|
||||
<br />
|
||||
{{ $t('settings.webauthn_noButtonAdvise') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="button">
|
||||
<a v-if="registerTab === '1'" class="btn" href="" @click.prevent="showRegisterModalTab('2');startRegister();">
|
||||
{{ $t('pagination.next') }}
|
||||
</a>
|
||||
<a v-else class="btn" href="" @click.prevent="showRegisterModalTab('1')">
|
||||
{{ $t('pagination.previous') }}
|
||||
</a>
|
||||
<a class="btn" href="" @click.prevent="closeRegisterModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="errorMessage !== ''" class="form-error-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="" @click.prevent="start()">
|
||||
{{ $t('app.retry') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="infoMessage !== ''" class="form-information-message mb3">
|
||||
<div class="pa2">
|
||||
<p class="mb0">
|
||||
{{ infoMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tc">
|
||||
<img src="https://ssl.gstatic.com/accounts/strongauth/Challenge_2SV-Gnubby_graphic.png"
|
||||
:alt="$t('settings.webauthn_insertKey')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pa2">
|
||||
<p>
|
||||
{{ $t('settings.webauthn_insertKey') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('settings.webauthn_buttonAdvise') }}
|
||||
<br />
|
||||
{{ $t('settings.webauthn_noButtonAdvise') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<sweet-modal ref="delete" overlay-theme="dark" title="Remove a key">
|
||||
<form>
|
||||
<div class="mb4">
|
||||
{{ $t('settings.webauthn_delete_confirmation') }}
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn" href="" @click.prevent="webauthnRemove(keyToTrash)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import * as WebAuthn from '../../../../vendor/asbiin/laravel-webauthn/resources/js/webauthn.js';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
props: {
|
||||
keys: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
publicKey: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
method: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
timezone: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
script: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isSupported: true,
|
||||
errorMessage: '',
|
||||
infoMessage: '',
|
||||
success: false,
|
||||
currentkeys: [],
|
||||
keyToTrash: '',
|
||||
keyName: '',
|
||||
registerTab: '',
|
||||
data: null,
|
||||
webauthn: null,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
this.start();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.currentkeys = this.keys;
|
||||
this.data = this.registerdata;
|
||||
|
||||
this.webauthn = new WebAuthn((name, message) => {
|
||||
this.errorMessage = this._errorMessage(name, message);
|
||||
});
|
||||
},
|
||||
|
||||
_errorMessage(name, message) {
|
||||
switch (name) {
|
||||
case 'InvalidStateError':
|
||||
return this.$t('settings.webauthn_error_already_used');
|
||||
case 'NotAllowedError':
|
||||
return this.$t('settings.webauthn_error_not_allowed');
|
||||
default:
|
||||
return message;
|
||||
}
|
||||
},
|
||||
|
||||
notSupportedMessage() {
|
||||
return this.$t('settings.webauthn_'+this.webauthn.notSupportedMessage());
|
||||
},
|
||||
|
||||
start() {
|
||||
var self = this;
|
||||
this.errorMessage = '';
|
||||
|
||||
if (! this.webauthn.webAuthnSupport()) {
|
||||
this.isSupported = false;
|
||||
this.errorMessage = this.notSupportedMessage();
|
||||
}
|
||||
|
||||
switch(this.method) {
|
||||
case 'register':
|
||||
setTimeout(function () {
|
||||
self.webauthn.register(
|
||||
self.publicKey,
|
||||
function (datas) { self.webauthnRegisterCallback(datas, true); }
|
||||
);
|
||||
}, 10);
|
||||
break;
|
||||
case 'login':
|
||||
this.webauthn.sign(
|
||||
this.publicKey,
|
||||
function (data) { self.webauthnLoginCallback(data); }
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
showRegisterModal() {
|
||||
this.errorMessage = '';
|
||||
this.infoMessage = '';
|
||||
this.keyName = '';
|
||||
this.success = false;
|
||||
this.showRegisterModalTab('1');
|
||||
this.$refs.registerModal.open();
|
||||
},
|
||||
|
||||
showRegisterModalTab(tab) {
|
||||
this.registerTab = tab;
|
||||
},
|
||||
|
||||
startRegister() {
|
||||
var self = this;
|
||||
this.errorMessage = '';
|
||||
axios.post('webauthn/keys/options')
|
||||
.then(response => {
|
||||
if (self.registerTab === '2') {
|
||||
var data = response.data.publicKey;
|
||||
setTimeout(function () {
|
||||
self.webauthn.register(
|
||||
data,
|
||||
function (datas) { self.webauthnRegisterCallback(datas, false); }
|
||||
);
|
||||
}, 10);
|
||||
}
|
||||
}).catch(error => {
|
||||
self.notify(error.response.data.message, false);
|
||||
});
|
||||
},
|
||||
|
||||
closeRegisterModal() {
|
||||
this.$refs.registerModal.close();
|
||||
this.showRegisterModalTab('');
|
||||
},
|
||||
|
||||
webauthnRegisterCallback(data, redirect) {
|
||||
var self = this;
|
||||
axios.post('webauthn/keys', {
|
||||
...data,
|
||||
name: self.keyName,
|
||||
}).then(response => {
|
||||
self.success = true;
|
||||
self.notify(self.$t('settings.webauthn_success'), true);
|
||||
self.currentkeys.push({
|
||||
id: response.result.id,
|
||||
name: response.result.name,
|
||||
});
|
||||
}).then(response => {
|
||||
if (redirect) {
|
||||
setTimeout(function () {
|
||||
window.location = response.data.callback;
|
||||
}, 100);
|
||||
} else {
|
||||
self.closeRegisterModal();
|
||||
}
|
||||
}).catch(error => {
|
||||
self.errorMessage = error.message ? error.message : error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
webauthnLoginCallback(data) {
|
||||
var self = this;
|
||||
axios.post('webauthn/auth', {
|
||||
...data
|
||||
}).then(response => {
|
||||
self.success = true;
|
||||
self.notify(self.$t('settings.webauthn_success'), true);
|
||||
|
||||
window.location = response.data.callback;
|
||||
}).catch(error => {
|
||||
self.errorMessage = error.message ? error.message : error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
webauthnRemove(id) {
|
||||
var self = this;
|
||||
axios.delete('webauthn/keys/'+id)
|
||||
.then(response => {
|
||||
self.currentkeys.splice(self.currentkeys.indexOf(self.currentkeys.find(item => item.id === response.data.id)), 1);
|
||||
self.success = true;
|
||||
self.notify(self.$t('settings.webauthn_delete_success'), true);
|
||||
self.closeDeleteModal();
|
||||
}).catch(error => {
|
||||
self.errorMessage = error.response.data.message;
|
||||
});
|
||||
},
|
||||
|
||||
showDeleteModal(id) {
|
||||
this.keyToTrash = id;
|
||||
this.$refs.delete.open();
|
||||
},
|
||||
|
||||
closeDeleteModal() {
|
||||
this.$refs.delete.close();
|
||||
},
|
||||
|
||||
formatTime(value) {
|
||||
var moment = require('moment-timezone');
|
||||
moment.locale(this._i18n.locale);
|
||||
moment.tz.setDefault('UTC');
|
||||
|
||||
var t = moment(value);
|
||||
var date = moment.tz(t, this.timezone);
|
||||
|
||||
return date.format('LLLL');
|
||||
},
|
||||
|
||||
notify(text, success) {
|
||||
this.$notify({
|
||||
group: 'webauthn',
|
||||
title: text,
|
||||
text: '',
|
||||
type: success ? 'success' : 'error'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user