Migrate to nuxt settings page AND remove axios (#266)
* Settings pages migration * remove axios and use opnFetch * Make created form reactive (#267) * Remove verify pages and axios lib --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
6fd2985ff5
commit
178424a184
|
@ -76,7 +76,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
import { inputProps, useFormInput } from './useFormInput.js'
|
import { inputProps, useFormInput } from './useFormInput.js'
|
||||||
import InputWrapper from './components/InputWrapper.vue'
|
import InputWrapper from './components/InputWrapper.vue'
|
||||||
import UploadedFile from './components/UploadedFile.vue'
|
import UploadedFile from './components/UploadedFile.vue'
|
||||||
|
@ -193,13 +192,14 @@ export default {
|
||||||
}
|
}
|
||||||
if (this.moveToFormAssets) {
|
if (this.moveToFormAssets) {
|
||||||
// Move file to permanent storage for form assets
|
// Move file to permanent storage for form assets
|
||||||
axios.post('/api/open/forms/assets/upload', {
|
opnFetch('/open/forms/assets/upload', {
|
||||||
|
method: 'POST',
|
||||||
type: 'files',
|
type: 'files',
|
||||||
url: file.name.split('.').slice(0, -1).join('.') + '_' + response.uuid + '.' + response.extension
|
url: file.name.split('.').slice(0, -1).join('.') + '_' + response.uuid + '.' + response.extension
|
||||||
}).then(moveFileResponse => {
|
}).then(moveFileResponseData => {
|
||||||
this.files.push({
|
this.files.push({
|
||||||
file: file,
|
file: file,
|
||||||
url: moveFileResponse.data.url,
|
url: moveFileResponseData.url,
|
||||||
src: this.getFileSrc(file)
|
src: this.getFileSrc(file)
|
||||||
})
|
})
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
|
@ -107,7 +107,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
import { inputProps, useFormInput } from './useFormInput.js'
|
import { inputProps, useFormInput } from './useFormInput.js'
|
||||||
import InputWrapper from './components/InputWrapper.vue'
|
import InputWrapper from './components/InputWrapper.vue'
|
||||||
import Modal from '../global/Modal.vue'
|
import Modal from '../global/Modal.vue'
|
||||||
|
@ -190,13 +189,14 @@ export default {
|
||||||
// Store file in s3
|
// Store file in s3
|
||||||
this.storeFile(this.file).then(response => {
|
this.storeFile(this.file).then(response => {
|
||||||
// Move file to permanent storage for form assets
|
// Move file to permanent storage for form assets
|
||||||
axios.post('/api/open/forms/assets/upload', {
|
opnFetch('/open/forms/assets/upload', {
|
||||||
|
method: 'POST',
|
||||||
url: this.file.name.split('.').slice(0, -1).join('.') + '_' + response.uuid + '.' + response.extension
|
url: this.file.name.split('.').slice(0, -1).join('.') + '_' + response.uuid + '.' + response.extension
|
||||||
}).then(moveFileResponse => {
|
}).then(moveFileResponseData => {
|
||||||
if (!this.multiple) {
|
if (!this.multiple) {
|
||||||
this.files = []
|
this.files = []
|
||||||
}
|
}
|
||||||
this.compVal = moveFileResponse.data.url
|
this.compVal = moveFileResponseData.url
|
||||||
this.showUploadModal = false
|
this.showUploadModal = false
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { },
|
components: { },
|
||||||
|
@ -52,10 +51,10 @@ export default {
|
||||||
this.useAlert.confirm('Do you really want to delete this record?', this.deleteRecord)
|
this.useAlert.confirm('Do you really want to delete this record?', this.deleteRecord)
|
||||||
},
|
},
|
||||||
async deleteRecord () {
|
async deleteRecord () {
|
||||||
axios.delete('/api/open/forms/' + this.form.id + '/records/' + this.rowid + '/delete').then(async (response) => {
|
opnFetch('/open/forms/' + this.form.id + '/records/' + this.rowid + '/delete', {method:'DELETE'}).then(async (data) => {
|
||||||
if (response.data.type === 'success') {
|
if (data.type === 'success') {
|
||||||
this.$emit('deleted')
|
this.$emit('deleted')
|
||||||
this.useAlert.success(response.data.message)
|
this.useAlert.success(data.message)
|
||||||
} else {
|
} else {
|
||||||
this.useAlert.error('Something went wrong!')
|
this.useAlert.error('Something went wrong!')
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
import clonedeep from 'clone-deep'
|
import clonedeep from 'clone-deep'
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
import OpenFormButton from './OpenFormButton.vue'
|
import OpenFormButton from './OpenFormButton.vue'
|
||||||
|
@ -293,8 +292,8 @@ export default {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
await this.recordsStore.loadRecord(
|
await this.recordsStore.loadRecord(
|
||||||
axios.get('/api/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((response) => {
|
opnFetch('/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((data) => {
|
||||||
return { submission_id: this.form.submission_id, ...response.data.data }
|
return { submission_id: this.form.submission_id, ...data.data }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
return this.recordsStore.getById(this.form.submission_id)
|
return this.recordsStore.getById(this.form.submission_id)
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
import { Line as LineChart } from 'vue-chartjs'
|
import { Line as LineChart } from 'vue-chartjs'
|
||||||
import {
|
import {
|
||||||
Chart as ChartJS,
|
Chart as ChartJS,
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import QuestionsEditor from './QuestionsEditor.vue'
|
import QuestionsEditor from './QuestionsEditor.vue'
|
||||||
|
|
||||||
|
@ -175,9 +174,9 @@ export default {
|
||||||
},
|
},
|
||||||
async deleteFormTemplate () {
|
async deleteFormTemplate () {
|
||||||
if (!this.template) return
|
if (!this.template) return
|
||||||
axios.delete('/api/templates/' + this.template.id).then((response) => {
|
opnFetch('/templates/' + this.template.id, {method:'DELETE'}).then((data) => {
|
||||||
if (response.data.message) {
|
if (data.message) {
|
||||||
this.useAlert.success(response.data.message)
|
this.useAlert.success(data.message)
|
||||||
}
|
}
|
||||||
this.$router.push({ name: 'templates' })
|
this.$router.push({ name: 'templates' })
|
||||||
this.templatesStore.remove(this.template)
|
this.templatesStore.remove(this.template)
|
||||||
|
|
|
@ -63,7 +63,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import axios from 'axios'
|
|
||||||
import { useAuthStore } from '../../../stores/auth';
|
import { useAuthStore } from '../../../stores/auth';
|
||||||
import VTransition from '~/components/global/transitions/VTransition.vue'
|
import VTransition from '~/components/global/transitions/VTransition.vue'
|
||||||
|
|
||||||
|
@ -98,8 +97,8 @@ export default {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadChangelogEntries () {
|
loadChangelogEntries () {
|
||||||
axios.get('/api/content/changelog/entries').then(response => {
|
opnFetch('/content/changelog/entries').then(data => {
|
||||||
this.changelogEntries = response.data.splice(0, 3)
|
this.changelogEntries = data.splice(0, 3)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -133,12 +132,12 @@ export default {
|
||||||
fetchGeneratedForm (generationId) {
|
fetchGeneratedForm (generationId) {
|
||||||
// check every 4 seconds if form is generated
|
// check every 4 seconds if form is generated
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
axios.get('/api/forms/ai/' + generationId).then(response => {
|
opnFetch('/forms/ai/' + generationId).then(data => {
|
||||||
if (response.data.ai_form_completion.status === 'completed') {
|
if (data.ai_form_completion.status === 'completed') {
|
||||||
this.useAlert.success(response.data.message)
|
this.useAlert.success(data.message)
|
||||||
this.$emit('form-generated', JSON.parse(response.data.ai_form_completion.result))
|
this.$emit('form-generated', JSON.parse(data.ai_form_completion.result))
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
} else if (response.data.ai_form_completion.status === 'failed') {
|
} else if (data.ai_form_completion.status === 'failed') {
|
||||||
this.useAlert.error('Something went wrong, please try again.')
|
this.useAlert.error('Something went wrong, please try again.')
|
||||||
this.state = 'default'
|
this.state = 'default'
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
|
@ -140,7 +140,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import axios from 'axios'
|
|
||||||
import Dropdown from '~/components/global/Dropdown.vue'
|
import Dropdown from '~/components/global/Dropdown.vue'
|
||||||
import FormTemplateModal from '../../../open/forms/components/templates/FormTemplateModal.vue'
|
import FormTemplateModal from '../../../open/forms/components/templates/FormTemplateModal.vue'
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import axios from 'axios'
|
|
||||||
import { useFormsStore } from '../../../../stores/forms'
|
import { useFormsStore } from '../../../../stores/forms'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -103,10 +102,10 @@ export default {
|
||||||
regenerateLink(option) {
|
regenerateLink(option) {
|
||||||
if (this.loadingNewLink) return
|
if (this.loadingNewLink) return
|
||||||
this.loadingNewLink = true
|
this.loadingNewLink = true
|
||||||
axios.put(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option).then((response) => {
|
opnFetch(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option, {method:'PUT'}).then((data) => {
|
||||||
this.formsStore.addOrUpdate(response.data.form)
|
this.formsStore.addOrUpdate(data.form)
|
||||||
this.$router.push({name: 'forms-slug-show-share', params: {slug: response.data.form.slug}})
|
this.$router.push({name: 'forms-slug-show-share', params: {slug: data.form.slug}})
|
||||||
useAlert().success(response.data.message)
|
useAlert().success(data.message)
|
||||||
this.loadingNewLink = false
|
this.loadingNewLink = false
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.showGenerateFormLinkModal = false
|
this.showGenerateFormLinkModal = false
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import axios from 'axios'
|
|
||||||
import TextInput from '../../forms/TextInput.vue'
|
import TextInput from '../../forms/TextInput.vue'
|
||||||
import VButton from '~/components/global/VButton.vue'
|
import VButton from '~/components/global/VButton.vue'
|
||||||
|
|
||||||
|
@ -81,8 +80,8 @@ export default {
|
||||||
if (this.form.busy) return
|
if (this.form.busy) return
|
||||||
this.form.put('api/subscription/update-customer-details').then(() => {
|
this.form.put('api/subscription/update-customer-details').then(() => {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
axios.get('/api/subscription/new/' + this.plan + '/' + (!this.yearly ? 'monthly' : 'yearly') + '/checkout/with-trial').then((response) => {
|
opnFetch('/subscription/new/' + this.plan + '/' + (!this.yearly ? 'monthly' : 'yearly') + '/checkout/with-trial').then((data) => {
|
||||||
window.location = response.data.checkout_url
|
window.location = data.checkout_url
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
useAlert().error(error.response.data.message)
|
useAlert().error(error.response.data.message)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
|
|
@ -109,7 +109,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from '../../../stores/auth'
|
import { useAuthStore } from '../../../stores/auth'
|
||||||
import axios from 'axios'
|
|
||||||
import MonthlyYearlySelector from './MonthlyYearlySelector.vue'
|
import MonthlyYearlySelector from './MonthlyYearlySelector.vue'
|
||||||
import CheckoutDetailsModal from './CheckoutDetailsModal.vue'
|
import CheckoutDetailsModal from './CheckoutDetailsModal.vue'
|
||||||
import CustomPlan from './CustomPlan.vue'
|
import CustomPlan from './CustomPlan.vue'
|
||||||
|
@ -160,9 +159,9 @@ export default {
|
||||||
},
|
},
|
||||||
openBilling () {
|
openBilling () {
|
||||||
this.billingLoading = true
|
this.billingLoading = true
|
||||||
axios.get('/api/subscription/billing-portal').then((response) => {
|
opnFetch('/subscription/billing-portal').then((data) => {
|
||||||
this.billingLoading = false
|
this.billingLoading = false
|
||||||
const url = response.data.portal_url
|
const url = data.portal_url
|
||||||
window.location = url
|
window.location = url
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Form {
|
||||||
Object.keys(this)
|
Object.keys(this)
|
||||||
.filter(key => !Form.ignore.includes(key))
|
.filter(key => !Form.ignore.includes(key))
|
||||||
.forEach((key) => {
|
.forEach((key) => {
|
||||||
this[key] = deepCopy(this.originalData[key]);
|
this[key] = JSON.parse(JSON.stringify(this.originalData[key]));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ class Form {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.handleErrors(error);
|
this.handleErrors(error);
|
||||||
resolve(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Form from "~/composables/lib/vForm/Form.js"
|
import Form from "~/composables/lib/vForm/Form.js"
|
||||||
|
|
||||||
export const useForm = (formData = {}) => {
|
export const useForm = (formData = {}) => {
|
||||||
return new Form(formData)
|
return reactive(new Form(formData))
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,7 +27,6 @@
|
||||||
"@vueuse/motion": "^2.0.0",
|
"@vueuse/motion": "^2.0.0",
|
||||||
"@vueuse/nuxt": "^10.7.0",
|
"@vueuse/nuxt": "^10.7.0",
|
||||||
"amplitude-js": "^8.21.9",
|
"amplitude-js": "^8.21.9",
|
||||||
"axios": "^0.21.1",
|
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.0",
|
||||||
"clone-deep": "^4.0.1",
|
"clone-deep": "^4.0.1",
|
||||||
"crisp-sdk-web": "^1.0.21",
|
"crisp-sdk-web": "^1.0.21",
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-8 m-auto px-4">
|
|
||||||
<h1 class="my-6">
|
|
||||||
Verify Email
|
|
||||||
</h1>
|
|
||||||
<form @submit.prevent="send" @keydown="form.onKeydown($event)">
|
|
||||||
<alert-success :form="form" :message="status" />
|
|
||||||
|
|
||||||
<!-- Email -->
|
|
||||||
<text-input name="email" :form="form" label="Email" :required="true" />
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-md-9 ml-md-auto">
|
|
||||||
<v-button :loading="form.busy">
|
|
||||||
Send Verification Link
|
|
||||||
</v-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
middleware: 'guest',
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
metaTitle: 'Verify Email',
|
|
||||||
status: '',
|
|
||||||
form: useForm({
|
|
||||||
email: ''
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
|
|
||||||
created () {
|
|
||||||
if (this.$route.query.email) {
|
|
||||||
this.form.email = this.$route.query.email
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async send () {
|
|
||||||
const { data } = await this.form.post('/api/email/resend')
|
|
||||||
|
|
||||||
this.status = data.status
|
|
||||||
|
|
||||||
this.form.reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,59 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-8 m-auto px-4">
|
|
||||||
<h1 class="my-6">
|
|
||||||
Verify Email
|
|
||||||
</h1>
|
|
||||||
<template v-if="success">
|
|
||||||
<div class="alert alert-success" role="alert">
|
|
||||||
{{ success }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<NuxtLink :to="{ name: 'login' }" class="btn btn-primary">
|
|
||||||
Login
|
|
||||||
</NuxtLink>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="alert alert-danger" role="alert">
|
|
||||||
{{ error || 'Failed to verify email.' }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<NuxtLink :to="{ name: 'auth-verification-resend' }" class="small float-right">
|
|
||||||
Resend Verification Link?
|
|
||||||
</NuxtLink>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
import SeoMeta from '../../../mixins/seo-meta.js'
|
|
||||||
|
|
||||||
const qs = (params) => Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [SeoMeta],
|
|
||||||
async beforeRouteEnter (to, from, next) {
|
|
||||||
try {
|
|
||||||
const { data } = await axios.post(`/api/email/verify/${to.params.id}?${qs(to.query)}`)
|
|
||||||
|
|
||||||
next(vm => {
|
|
||||||
vm.success = data.status
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
next(vm => {
|
|
||||||
vm.error = e.response.data.status
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
middleware: 'guest',
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
metaTitle: 'Verify Email',
|
|
||||||
error: '',
|
|
||||||
success: ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<div class="bg-white">
|
||||||
|
<div class="flex bg-gray-50">
|
||||||
|
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||||
|
<div class="pt-4 pb-0">
|
||||||
|
<div class="flex">
|
||||||
|
<h2 class="flex-grow text-gray-900">
|
||||||
|
My Account
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<ul class="flex text-gray-500">
|
||||||
|
<li>{{ user.email }}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="mt-4 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center">
|
||||||
|
<li v-for="(tab, i) in tabsList" :key="i+1" class="mr-6">
|
||||||
|
<nuxt-link :to="{ name: tab.route }"
|
||||||
|
class="hover:no-underline inline-block py-4 rounded-t-lg border-b-2 text-gray-500 hover:text-gray-600"
|
||||||
|
active-class="text-blue-600 hover:text-blue-900 dark:text-blue-500 dark:hover:text-blue-500 border-blue-600 dark:border-blue-500"
|
||||||
|
>
|
||||||
|
{{ tab.name }}
|
||||||
|
</nuxt-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex bg-white">
|
||||||
|
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||||
|
<div class="mt-8 pb-0">
|
||||||
|
<NuxtPage />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useAuthStore } from '../stores/auth'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: "auth"
|
||||||
|
})
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const user = computed(() => authStore.user)
|
||||||
|
const tabsList = computed(() => {
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
name: 'Profile',
|
||||||
|
route: 'settings-profile'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Workspace Settings',
|
||||||
|
route: 'settings-workspace'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Password',
|
||||||
|
route: 'settings-password'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete Account',
|
||||||
|
route: 'settings-account'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
if (user.value.is_subscribed) {
|
||||||
|
tabs.splice(1, 0, {
|
||||||
|
name: 'Billing',
|
||||||
|
route: 'settings-billing'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.value.admin) {
|
||||||
|
tabs.push({
|
||||||
|
name: 'Admin',
|
||||||
|
route: 'settings-admin'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabs
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
|
@ -9,49 +9,34 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Submit Button -->
|
<!-- Submit Button -->
|
||||||
<v-button :loading="loading" class="mt-4" color="red" @click="alertConfirm('Do you really want to delete your account?',deleteAccount)">
|
<v-button :loading="loading" class="mt-4" color="red" @click="useAlert().confirm('Do you really want to delete your account?',deleteAccount)">
|
||||||
Delete account
|
Delete account
|
||||||
</v-button>
|
</v-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import axios from 'axios'
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
export default {
|
const router = useRouter()
|
||||||
scrollToTop: false,
|
const authStore = useAuthStore()
|
||||||
|
const metaTitle = 'Account'
|
||||||
|
let loading = false
|
||||||
|
|
||||||
setup () {
|
const deleteAccount = () => {
|
||||||
const authStore = useAuthStore()
|
loading = true
|
||||||
return {
|
opnFetch('/user', {method:'DELETE'}).then(async (data) => {
|
||||||
authStore
|
loading = false
|
||||||
}
|
useAlert().success(data.message)
|
||||||
},
|
|
||||||
|
// Log out the user.
|
||||||
|
await authStore.logout()
|
||||||
|
|
||||||
data: () => ({
|
// Redirect to login.
|
||||||
metaTitle: 'Account',
|
router.push({ name: 'login' })
|
||||||
form: useForm({
|
}).catch((error) => {
|
||||||
identifier: ''
|
useAlert().error(error.response.data.message)
|
||||||
}),
|
loading = false
|
||||||
loading: false
|
})
|
||||||
}),
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async deleteAccount () {
|
|
||||||
this.loading = true
|
|
||||||
axios.delete('/api/user').then(async (response) => {
|
|
||||||
this.loading = false
|
|
||||||
useAlert().success(response.data.message)
|
|
||||||
// Log out the user.
|
|
||||||
await this.authStore.logout()
|
|
||||||
|
|
||||||
// Redirect to login.
|
|
||||||
this.$router.push({ name: 'login' })
|
|
||||||
}).catch((error) => {
|
|
||||||
useAlert().error(error.response.data.message)
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -34,54 +34,40 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import axios from 'axios'
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
export default {
|
definePageMeta({
|
||||||
components: { },
|
middleware: "admin"
|
||||||
middleware: 'admin',
|
})
|
||||||
scrollToTop: false,
|
|
||||||
|
|
||||||
setup () {
|
const metaTitle = 'Admin'
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const workspacesStore = useWorkspacesStore()
|
const workspacesStore = useWorkspacesStore()
|
||||||
return {
|
const router = useRouter()
|
||||||
authStore,
|
let form = useForm({
|
||||||
workspacesStore
|
identifier: ''
|
||||||
}
|
})
|
||||||
},
|
let loading = false
|
||||||
|
|
||||||
data: () => ({
|
const impersonate = () => {
|
||||||
metaTitle: 'Admin',
|
loading = true
|
||||||
form: useForm({
|
authStore.startImpersonating()
|
||||||
identifier: ''
|
opnFetch('/admin/impersonate/' + encodeURI(form.identifier)).then(async (data) => {
|
||||||
}),
|
loading = false
|
||||||
loading: false
|
|
||||||
}),
|
|
||||||
|
|
||||||
methods: {
|
// Save the token.
|
||||||
async impersonate () {
|
authStore.saveToken(data.token, false)
|
||||||
this.loading = true
|
|
||||||
this.authStore.startImpersonating()
|
|
||||||
axios.get('/api/admin/impersonate/' + encodeURI(this.form.identifier)).then(async (response) => {
|
|
||||||
this.loading = false
|
|
||||||
|
|
||||||
// Save the token.
|
// Fetch the user.
|
||||||
this.authStore.saveToken(response.data.token, false)
|
await authStore.fetchUser()
|
||||||
|
|
||||||
// Fetch the user.
|
// Redirect to the dashboard.
|
||||||
await this.authStore.fetchUser()
|
workspacesStore.set([])
|
||||||
|
router.push({ name: 'home' })
|
||||||
// Redirect to the dashboard.
|
}).catch((error) => {
|
||||||
this.workspacesStore.set([])
|
useAlert().error(error.response.data.message)
|
||||||
this.$router.push({ name: 'home' })
|
loading = false
|
||||||
}).catch((error) => {
|
})
|
||||||
useAlert().error(error.response.data.message)
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
|
|
||||||
// this.form.reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -19,45 +19,25 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import axios from 'axios'
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from '../../stores/auth'
|
import { useAuthStore } from '../../stores/auth'
|
||||||
import VButton from '~/components/global/VButton.vue'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
import AppSumoBilling from '../../components/vendor/appsumo/AppSumoBilling.vue'
|
import AppSumoBilling from '../../components/vendor/appsumo/AppSumoBilling.vue'
|
||||||
|
|
||||||
export default {
|
const metaTitle = 'Billing'
|
||||||
components: { AppSumoBilling, VButton },
|
const authStore = useAuthStore()
|
||||||
mixins: [SeoMeta],
|
let user = computed(() => authStore.user)
|
||||||
scrollToTop: false,
|
let billingLoading = false
|
||||||
|
|
||||||
setup () {
|
const openBillingDashboard = () => {
|
||||||
const authStore = useAuthStore()
|
billingLoading = true
|
||||||
return {
|
opnFetch('/subscription/billing-portal').then((data) => {
|
||||||
user : computed(() => authStore.user)
|
const url = data.portal_url
|
||||||
}
|
window.location = url
|
||||||
},
|
}).catch((error) => {
|
||||||
|
useAlert().error(error.response.data.message)
|
||||||
data: () => ({
|
}).finally(() => {
|
||||||
metaTitle: 'Billing',
|
billingLoading = false
|
||||||
billingLoading: false
|
})
|
||||||
}),
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
openBillingDashboard () {
|
|
||||||
this.billingLoading = true
|
|
||||||
axios.get('/api/subscription/billing-portal').then((response) => {
|
|
||||||
const url = response.data.portal_url
|
|
||||||
window.location = url
|
|
||||||
}).catch((error) => {
|
|
||||||
useAlert().error(error.response.data.message)
|
|
||||||
}).finally(() => {
|
|
||||||
this.billingLoading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,105 +1,5 @@
|
||||||
<template>
|
<script setup>
|
||||||
<div class="bg-white">
|
useRouter().push({
|
||||||
<div class="flex bg-gray-50">
|
name: 'settings-profile'
|
||||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
})
|
||||||
<div class="pt-4 pb-0">
|
|
||||||
<div class="flex">
|
|
||||||
<h2 class="flex-grow text-gray-900">
|
|
||||||
My Account
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<ul class="flex text-gray-500">
|
|
||||||
<li>{{ user.email }}</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="mt-4 border-b border-gray-200 dark:border-gray-700">
|
|
||||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center">
|
|
||||||
<li v-for="(tab, i) in tabsList" :key="i+1" class="mr-6">
|
|
||||||
<NuxtLink :to="{ name: tab.route }"
|
|
||||||
class="hover:no-underline inline-block py-4 rounded-t-lg border-b-2 text-gray-500 hover:text-gray-600"
|
|
||||||
active-class="text-blue-600 hover:text-blue-900 dark:text-blue-500 dark:hover:text-blue-500 border-blue-600 dark:border-blue-500"
|
|
||||||
>
|
|
||||||
{{ tab.name }}
|
|
||||||
</NuxtLink>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex bg-white">
|
|
||||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
|
||||||
<div class="mt-8 pb-0">
|
|
||||||
<router-view v-slot="{ Component }">
|
|
||||||
<transition name="page" mode="out-in">
|
|
||||||
<component :is="Component" />
|
|
||||||
</transition>
|
|
||||||
</router-view>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useAuthStore } from '../../stores/auth'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
middleware: 'auth',
|
|
||||||
|
|
||||||
setup () {
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
return {
|
|
||||||
user: computed(() => authStore.user)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
tabsList () {
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
name: 'Profile',
|
|
||||||
route: 'settings-profile'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Workspace Settings',
|
|
||||||
route: 'settings-workspace'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Password',
|
|
||||||
route: 'settings-password'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Delete Account',
|
|
||||||
route: 'settings-account'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
if (this.user.is_subscribed) {
|
|
||||||
tabs.splice(1, 0, {
|
|
||||||
name: 'Billing',
|
|
||||||
route: 'settings-billing'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.user.admin) {
|
|
||||||
tabs.push({
|
|
||||||
name: 'Admin',
|
|
||||||
route: 'settings-admin'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabs
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
<small class="text-gray-600">Manage your password.</small>
|
<small class="text-gray-600">Manage your password.</small>
|
||||||
|
|
||||||
<form class="mt-3" @submit.prevent="update" @keydown="form.onKeydown($event)">
|
<form class="mt-3" @submit.prevent="update" @keydown="form.onKeydown($event)">
|
||||||
<alert-success class="mb-5" :form="form" message="Password updated." />
|
|
||||||
|
|
||||||
<!-- Password -->
|
<!-- Password -->
|
||||||
<text-input native-type="password"
|
<text-input native-type="password"
|
||||||
name="password" :form="form" label="Password" :required="true"
|
name="password" :form="form" label="Password" :required="true"
|
||||||
|
@ -26,27 +24,17 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
const metaTitle = 'Password'
|
||||||
|
let form = useForm({
|
||||||
|
password: '',
|
||||||
|
password_confirmation: ''
|
||||||
|
})
|
||||||
|
|
||||||
export default {
|
const update = () => {
|
||||||
mixins: [SeoMeta],
|
form.patch('/settings/password').then((response) => {
|
||||||
scrollToTop: false,
|
form.reset()
|
||||||
|
useAlert().success('Password updated.')
|
||||||
data: () => ({
|
})
|
||||||
metaTitle: 'Password',
|
|
||||||
form: useForm({
|
|
||||||
password: '',
|
|
||||||
password_confirmation: ''
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async update () {
|
|
||||||
await this.form.patch('/api/settings/password')
|
|
||||||
|
|
||||||
this.form.reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
<small class="text-gray-600">Update your username and manage your account details.</small>
|
<small class="text-gray-600">Update your username and manage your account details.</small>
|
||||||
|
|
||||||
<form class="mt-3" @submit.prevent="update" @keydown="form.onKeydown($event)">
|
<form class="mt-3" @submit.prevent="update" @keydown="form.onKeydown($event)">
|
||||||
<alert-success class="mb-5" :form="form" message="Your info has been updated!" />
|
|
||||||
|
|
||||||
<!-- Name -->
|
<!-- Name -->
|
||||||
<text-input name="name" :form="form" label="Name" :required="true" />
|
<text-input name="name" :form="form" label="Name" :required="true" />
|
||||||
|
|
||||||
|
@ -22,39 +20,26 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const authStore = useAuthStore()
|
||||||
scrollToTop: false,
|
const user = computed(() => authStore.user)
|
||||||
|
const metaTitle = 'Profile'
|
||||||
|
let form = useForm({
|
||||||
|
name: '',
|
||||||
|
email: ''
|
||||||
|
})
|
||||||
|
|
||||||
setup () {
|
const update = () => {
|
||||||
const authStore = useAuthStore()
|
form.patch('/settings/profile').then((response) => {
|
||||||
return {
|
authStore.updateUser(response)
|
||||||
authStore,
|
useAlert().success('Your info has been updated!')
|
||||||
user : computed(() => authStore.user)
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
metaTitle: 'Profile',
|
|
||||||
form: useForm({
|
|
||||||
name: '',
|
|
||||||
email: ''
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
|
|
||||||
created () {
|
|
||||||
// Fill the form with user data.
|
|
||||||
this.form.keys().forEach(key => {
|
|
||||||
this.form[key] = this.user[key]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async update () {
|
|
||||||
const { data } = await this.form.patch('/api/settings/profile')
|
|
||||||
|
|
||||||
this.authStore.updateUser(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// Fill the form with user data.
|
||||||
|
form.keys().forEach(key => {
|
||||||
|
form[key] = user.value[key]
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
/>
|
/>
|
||||||
<p class="text-gray-500 text-sm">
|
<p class="text-gray-500 text-sm">
|
||||||
Read our <a href="#"
|
Read our <a href="#"
|
||||||
@click.prevent="$crisp.push(['do', 'helpdesk:article:open', ['en', 'how-to-use-my-own-domain-9m77g7']])"
|
@click.prevent="crisp.openHelpdeskArticle('how-to-use-my-own-domain-9m77g7')"
|
||||||
>custom
|
>custom
|
||||||
domain instructions</a> to learn how to use your own domain.
|
domain instructions</a> to learn how to use your own domain.
|
||||||
</p>
|
</p>
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
Save Domains
|
Save Domains
|
||||||
</v-button>
|
</v-button>
|
||||||
<v-button v-if="workspaces.length > 1" color="white" class="group w-full sm:w-auto" :loading="loading"
|
<v-button v-if="workspaces.length > 1" color="white" class="group w-full sm:w-auto" :loading="loading"
|
||||||
@click="deleteWorkspace(workspace)"
|
@click="deleteWorkspace(workspace.id)"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 -mt-1 inline group-hover:text-red-700" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 -mt-1 inline group-hover:text-red-700" fill="none"
|
||||||
viewBox="0 0 24 24" stroke="currentColor"
|
viewBox="0 0 24 24" stroke="currentColor"
|
||||||
|
@ -114,103 +114,88 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import TextAreaInput from '../../components/forms/TextAreaInput.vue'
|
import {watch} from "vue";
|
||||||
import axios from 'axios'
|
import opnformConfig from "~/opnform.config.js";
|
||||||
|
import {fetchAllWorkspaces} from "~/stores/workspaces.js";
|
||||||
|
|
||||||
export default {
|
const crisp = useCrisp()
|
||||||
components: { TextAreaInput },
|
const workspacesStore = useWorkspacesStore()
|
||||||
scrollToTop: false,
|
const workspaces = computed(() => workspacesStore.getAll)
|
||||||
|
let loading = computed(() => workspacesStore.loading)
|
||||||
|
const metaTitle = 'Workspaces'
|
||||||
|
let form = useForm({
|
||||||
|
name: '',
|
||||||
|
emoji: ''
|
||||||
|
})
|
||||||
|
let workspaceModal = ref(false)
|
||||||
|
let customDomains = ''
|
||||||
|
let customDomainsLoading = ref(false)
|
||||||
|
|
||||||
setup () {
|
let workspace = computed(() => workspacesStore.getCurrent)
|
||||||
const formsStore = useFormsStore()
|
let customDomainsEnabled = computed(() => opnformConfig.custom_domains_enabled)
|
||||||
const workspacesStore = useWorkspacesStore()
|
|
||||||
return {
|
|
||||||
formsStore,
|
|
||||||
workspacesStore,
|
|
||||||
workspaces: computed(() => workspacesStore.content),
|
|
||||||
loading: computed(() => workspacesStore.loading)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
watch(() => workspace, () => {
|
||||||
metaTitle: 'Workspaces',
|
initCustomDomains()
|
||||||
form: useForm({
|
})
|
||||||
name: '',
|
|
||||||
emoji: ''
|
|
||||||
}),
|
|
||||||
workspaceModal: false,
|
|
||||||
customDomains: '',
|
|
||||||
customDomainsLoading: false
|
|
||||||
}),
|
|
||||||
|
|
||||||
mounted () {
|
onMounted(() => {
|
||||||
this.workspacesStore.loadIfEmpty()
|
fetchAllWorkspaces()
|
||||||
this.initCustomDomains()
|
initCustomDomains()
|
||||||
},
|
})
|
||||||
|
|
||||||
computed: {
|
const saveChanges = () => {
|
||||||
workspace () {
|
if (customDomainsLoading.value) return
|
||||||
return this.workspacesStore.getCurrent()
|
customDomainsLoading.value = true
|
||||||
},
|
// Update the workspace custom domain
|
||||||
customDomainsEnabled () {
|
opnFetch('/open/workspaces/' + workspace.value.id + '/custom-domains', {
|
||||||
return this.$config.custom_domains_enabled
|
method:'PUT',
|
||||||
}
|
custom_domains: customDomains.split('\n')
|
||||||
},
|
.map(domain => domain.trim())
|
||||||
|
.filter(domain => domain && domain.length > 0)
|
||||||
methods: {
|
}).then((data) => {
|
||||||
saveChanges () {
|
workspacesStore.addOrUpdate(data)
|
||||||
if (this.customDomainsLoading) return
|
useAlert().success('Custom domains saved.')
|
||||||
this.customDomainsLoading = true
|
}).catch((error) => {
|
||||||
// Update the workspace custom domain
|
useAlert().error('Failed to update custom domains: ' + error.response.data.message)
|
||||||
axios.put('/api/open/workspaces/' + this.workspace.id + '/custom-domains', {
|
}).finally(() => {
|
||||||
custom_domains: this.customDomains.split('\n')
|
customDomainsLoading.value = false
|
||||||
.map(domain => domain.trim())
|
})
|
||||||
.filter(domain => domain && domain.length > 0)
|
|
||||||
}).then((response) => {
|
|
||||||
this.workspacesStore.addOrUpdate(response.data)
|
|
||||||
useAlert().success('Custom domains saved.')
|
|
||||||
}).catch((error) => {
|
|
||||||
useAlert().error('Failed to update custom domains: ' + error.response.data.message)
|
|
||||||
}).finally(() => {
|
|
||||||
this.customDomainsLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
initCustomDomains () {
|
|
||||||
if (!this.workspace) return
|
|
||||||
this.customDomains = this.workspace.custom_domains.join('\n')
|
|
||||||
},
|
|
||||||
deleteWorkspace (workspace) {
|
|
||||||
if (this.workspaces.length <= 1) {
|
|
||||||
useAlert().error('You cannot delete your only workspace.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
useAlert().confirm('Do you really want to delete this workspace? All forms created in this workspace will be removed.', () => {
|
|
||||||
this.workspacesStore.delete(workspace.id).then(() => {
|
|
||||||
useAlert().success('Workspace successfully removed.')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
isUrl (str) {
|
|
||||||
const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
|
|
||||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
|
||||||
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
|
||||||
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
|
|
||||||
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
|
|
||||||
'(\\#[-a-z\\d_]*)?$', 'i') // fragment locator
|
|
||||||
return !!pattern.test(str)
|
|
||||||
},
|
|
||||||
async createWorkspace() {
|
|
||||||
const {data} = await this.form.post('/api/open/workspaces/create')
|
|
||||||
this.workspacesStore.load()
|
|
||||||
this.workspaceModal = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
workspace () {
|
|
||||||
this.initCustomDomains()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initCustomDomains = () => {
|
||||||
|
if (!workspace || !workspace.value.custom_domains) return
|
||||||
|
customDomains = workspace.value.custom_domains.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteWorkspace = (workspaceId) => {
|
||||||
|
if (workspaces.length <= 1) {
|
||||||
|
useAlert().error('You cannot delete your only workspace.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
useAlert().confirm('Do you really want to delete this workspace? All forms created in this workspace will be removed.', () => {
|
||||||
|
opnFetch('/open/workspaces/' + workspaceId, {method:'DELETE'}).then((data) => {
|
||||||
|
useAlert().success('Workspace successfully removed.')
|
||||||
|
workspacesStore.remove(workspaceId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUrl = (str) => {
|
||||||
|
const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
|
||||||
|
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
||||||
|
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
|
||||||
|
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
|
||||||
|
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
|
||||||
|
'(\\#[-a-z\\d_]*)?$', 'i') // fragment locator
|
||||||
|
return !!pattern.test(str)
|
||||||
|
}
|
||||||
|
const createWorkspace = () => {
|
||||||
|
form.post('/open/workspaces/create').then((response) => {
|
||||||
|
fetchAllWorkspaces()
|
||||||
|
workspaceModal.value = false
|
||||||
|
useAlert().success('Workspace successfully created.')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -35,7 +35,7 @@ export const useWorkspacesStore = defineStore('workspaces', () => {
|
||||||
const remove = (itemId) => {
|
const remove = (itemId) => {
|
||||||
contentStore.remove(itemId)
|
contentStore.remove(itemId)
|
||||||
if (currentId.value === itemId) {
|
if (currentId.value === itemId) {
|
||||||
setCurrentId(contentStore.length.value > 0 ? contentStore.getAll[0].id : null)
|
setCurrentId(contentStore.length.value > 0 ? contentStore.getAll.value[0].id : null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue