Feature flags (#543)
* Re-organize a bit controllers * Added the featureflagcontroller * Implement feature flags in the front-end * Clean env files * Clean console.log messages * Fix feature flag test
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
:max-date="maxDate"
|
||||
:is-dark="props.isDark"
|
||||
color="form-color"
|
||||
@update:modelValue="updateModelValue"
|
||||
@update:model-value="updateModelValue"
|
||||
/>
|
||||
<DatePicker
|
||||
v-else
|
||||
@@ -84,7 +84,7 @@
|
||||
:max-date="maxDate"
|
||||
:is-dark="props.isDark"
|
||||
color="form-color"
|
||||
@update:modelValue="updateModelValue"
|
||||
@update:model-value="updateModelValue"
|
||||
/>
|
||||
</template>
|
||||
</UPopover>
|
||||
@@ -201,7 +201,7 @@ const formattedDate = (value) => {
|
||||
try {
|
||||
return format(new Date(value), props.dateFormat + (props.timeFormat == 12 ? ' p':' HH:mm'))
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.log('Error formatting date', e)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</a>
|
||||
</template>
|
||||
<NuxtLink
|
||||
v-if="($route.name !== 'ai-form-builder' && user === null) && (!appStore.selfHosted || appStore.aiFeaturesEnabled)"
|
||||
v-if="($route.name !== 'ai-form-builder' && user === null) && (!useFeatureFlag('self_hosted') || useFeatureFlag('ai_features'))"
|
||||
:to="{ name: 'ai-form-builder' }"
|
||||
:class="navLinkClasses"
|
||||
class="hidden lg:inline"
|
||||
@@ -63,9 +63,9 @@
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
v-if="
|
||||
(appStore.paidPlansEnabled &&
|
||||
(useFeatureFlag('billing.enabled') &&
|
||||
(user === null || (user && workspace && !workspace.is_pro)) &&
|
||||
$route.name !== 'pricing') && !appStore.selfHosted
|
||||
$route.name !== 'pricing') && !isSelfHosted
|
||||
"
|
||||
:to="{ name: 'pricing' }"
|
||||
:class="navLinkClasses"
|
||||
@@ -248,7 +248,7 @@
|
||||
</NuxtLink>
|
||||
|
||||
<v-button
|
||||
v-if="!appStore.selfHosted"
|
||||
v-if="!isSelfHosted"
|
||||
v-track.nav_create_form_click
|
||||
size="small"
|
||||
class="shrink-0"
|
||||
@@ -274,6 +274,7 @@ import Dropdown from "~/components/global/Dropdown.vue"
|
||||
import WorkspaceDropdown from "./WorkspaceDropdown.vue"
|
||||
import opnformConfig from "~/opnform.config.js"
|
||||
import { useRuntimeConfig } from "#app"
|
||||
import { useFeatureFlag } from "~/composables/useFeatureFlag"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -294,6 +295,7 @@ export default {
|
||||
config: useRuntimeConfig(),
|
||||
user: computed(() => authStore.user),
|
||||
isIframe: useIsIframe(),
|
||||
isSelfHosted: computed(() => useFeatureFlag('self_hosted')),
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const user = computed(() => authStore.user)
|
||||
const workspace = computed(() => workspacesStore.getCurrent)
|
||||
|
||||
const shouldDisplayProTag = computed(() => {
|
||||
if (!useRuntimeConfig().public.paidPlansEnabled) return false
|
||||
if (!useFeatureFlag('billing.enabled')) return false
|
||||
if (!user.value || !workspace.value) return true
|
||||
return !workspace.value.is_pro
|
||||
})
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
<template>
|
||||
<ErrorBoundary @on-error="onFormEditorError">
|
||||
|
||||
<template #error="{ error, clearError }">
|
||||
<div class="flex-grow w-full flex items-center justify-center flex-col gap-4">
|
||||
<h1 class="text-blue-800 text-2xl font-medium">Oops! Something went wrong.</h1>
|
||||
<p class="text-gray-500 max-w-lg text-center">It looks like your last action caused an issue on our side. We
|
||||
apologize for
|
||||
the
|
||||
inconvenience.</p>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<UButton icon="i-material-symbols-undo" @click="clearEditorError(error, clearError)">Go back one step
|
||||
</UButton>
|
||||
<UButton variant="outline" icon="i-heroicons-chat-bubble-left-right-16-solid"
|
||||
@click="onErrorContact(error)">
|
||||
Report this error
|
||||
</UButton>
|
||||
</div>
|
||||
<ErrorBoundary @on-error="onFormEditorError">
|
||||
<template #error="{ error, clearError }">
|
||||
<div class="flex-grow w-full flex items-center justify-center flex-col gap-4">
|
||||
<h1 class="text-blue-800 text-2xl font-medium">
|
||||
Oops! Something went wrong.
|
||||
</h1>
|
||||
<p class="text-gray-500 max-w-lg text-center">
|
||||
It looks like your last action caused an issue on our side. We
|
||||
apologize for
|
||||
the
|
||||
inconvenience.
|
||||
</p>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<UButton
|
||||
icon="i-material-symbols-undo"
|
||||
@click="clearEditorError(error, clearError)"
|
||||
>
|
||||
Go back one step
|
||||
</UButton>
|
||||
<UButton
|
||||
variant="outline"
|
||||
icon="i-heroicons-chat-bubble-left-right-16-solid"
|
||||
@click="onErrorContact(error)"
|
||||
>
|
||||
Report this error
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<slot />
|
||||
</ErrorBoundary>
|
||||
</template>
|
||||
<slot />
|
||||
</ErrorBoundary>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
@@ -51,7 +61,6 @@
|
||||
}
|
||||
}
|
||||
const onErrorContact = (error) => {
|
||||
console.log('Contacting via crisp for an error', error)
|
||||
crisp.pauseChatBot()
|
||||
let errorReport = 'Hi there, I have a technical issue with the form editor.'
|
||||
if (form.value.slug) {
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
on other sites (Open Graph).
|
||||
</p>
|
||||
<select-input
|
||||
v-if="customDomainAllowed"
|
||||
v-if="useFeatureFlag('custom_domains')"
|
||||
v-model="form.custom_domain"
|
||||
:clearable="true"
|
||||
:disabled="customDomainOptions.length <= 0"
|
||||
@@ -98,9 +98,6 @@ export default {
|
||||
})
|
||||
: []
|
||||
},
|
||||
customDomainAllowed() {
|
||||
return useRuntimeConfig().public.customDomainsEnabled
|
||||
},
|
||||
},
|
||||
watch: {},
|
||||
mounted() {
|
||||
|
||||
@@ -53,23 +53,25 @@
|
||||
label="Form Theme"
|
||||
/>
|
||||
|
||||
<label class="text-gray-700 font-medium text-sm">Font Style</label>
|
||||
<v-button
|
||||
color="white"
|
||||
class="w-full mb-4"
|
||||
size="small"
|
||||
@click="showGoogleFontPicker = true"
|
||||
>
|
||||
<span :style="{ 'font-family': (form.font_family?form.font_family+' !important':null) }">
|
||||
{{ form.font_family || 'Default' }}
|
||||
</span>
|
||||
</v-button>
|
||||
<GoogleFontPicker
|
||||
:show="showGoogleFontPicker"
|
||||
:font="form.font_family || null"
|
||||
@close="showGoogleFontPicker=false"
|
||||
@apply="onApplyFont"
|
||||
/>
|
||||
<template v-if="useFeatureFlag('services.google.fonts')">
|
||||
<label class="text-gray-700 font-medium text-sm">Font Style</label>
|
||||
<v-button
|
||||
color="white"
|
||||
class="w-full mb-4"
|
||||
size="small"
|
||||
@click="showGoogleFontPicker = true"
|
||||
>
|
||||
<span :style="{ 'font-family': (form.font_family?form.font_family+' !important':null) }">
|
||||
{{ form.font_family || 'Default' }}
|
||||
</span>
|
||||
</v-button>
|
||||
<GoogleFontPicker
|
||||
:show="showGoogleFontPicker"
|
||||
:font="form.font_family || null"
|
||||
@close="showGoogleFontPicker=false"
|
||||
@apply="onApplyFont"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="flex space-x-4 justify-stretch">
|
||||
<select-input
|
||||
@@ -216,7 +218,7 @@ const user = computed(() => authStore.user)
|
||||
const workspace = computed(() => workspacesStore.getCurrent)
|
||||
|
||||
const isPro = computed(() => {
|
||||
if (!useRuntimeConfig().public.paidPlansEnabled) return true
|
||||
if (!useFeatureFlag('billing.enabled')) return true
|
||||
if (!user.value || !workspace.value) return false
|
||||
return workspace.value.is_pro
|
||||
})
|
||||
@@ -237,7 +239,6 @@ const onChangeNoBranding = (val) => {
|
||||
subscriptionModalStore.openModal()
|
||||
setTimeout(() => {
|
||||
form.value.no_branding = false
|
||||
console.log("form.value.no_branding", form.value.no_branding)
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,21 +21,21 @@
|
||||
</div>
|
||||
<div class="flex justify-center mt-5 md:mt-0">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<router-link
|
||||
v-if="!appStore.selfHosted"
|
||||
:to="{ name: 'privacy-policy' }"
|
||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||
>
|
||||
Privacy Policy
|
||||
</router-link>
|
||||
<template v-if="!useFeatureFlag('self_hosted')">
|
||||
<router-link
|
||||
:to="{ name: 'privacy-policy' }"
|
||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||
>
|
||||
Privacy Policy
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
v-if="!appStore.selfHosted"
|
||||
:to="{ name: 'terms-conditions' }"
|
||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||
>
|
||||
Terms & Conditions
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'terms-conditions' }"
|
||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||
>
|
||||
Terms & Conditions
|
||||
</router-link>
|
||||
</template>
|
||||
<a
|
||||
:href="opnformConfig.links.feature_requests"
|
||||
target="_blank"
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
</v-button>
|
||||
|
||||
<v-button
|
||||
v-if="useFeatureFlag('services.google.auth')"
|
||||
native-type="button"
|
||||
color="white"
|
||||
class="space-x-4 mt-4 flex items-center w-full"
|
||||
@@ -72,7 +73,7 @@
|
||||
<span class="mx-2">Sign in with Google</span>
|
||||
</v-button>
|
||||
<p
|
||||
v-if="!appStore.selfHosted"
|
||||
v-if="!useFeatureFlag('self_hosted')"
|
||||
class="text-gray-500 text-sm text-center mt-4"
|
||||
>
|
||||
Don't have an account?
|
||||
|
||||
@@ -87,19 +87,21 @@
|
||||
Create account
|
||||
</v-button>
|
||||
|
||||
<p class="text-gray-600/50 text-sm text-center my-4">
|
||||
Or
|
||||
</p>
|
||||
<v-button
|
||||
native-type="buttom"
|
||||
color="white"
|
||||
class="space-x-4 flex items-center w-full"
|
||||
:loading="false"
|
||||
@click.prevent="signInwithGoogle"
|
||||
>
|
||||
<Icon name="devicon:google" />
|
||||
<span class="mx-2">Sign in with Google</span>
|
||||
</v-button>
|
||||
<template v-if="useFeatureFlag('services.google.auth')">
|
||||
<p class="text-gray-600/50 text-sm text-center my-4">
|
||||
Or
|
||||
</p>
|
||||
<v-button
|
||||
native-type="buttom"
|
||||
color="white"
|
||||
class="space-x-4 flex items-center w-full"
|
||||
:loading="false"
|
||||
@click.prevent="signInwithGoogle"
|
||||
>
|
||||
<Icon name="devicon:google" />
|
||||
<span class="mx-2">Sign in with Google</span>
|
||||
</v-button>
|
||||
</template>
|
||||
|
||||
<p class="text-gray-500 mt-4 text-sm text-center">
|
||||
Already have an account?
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-if="aiFeaturesEnabled"
|
||||
v-if="useFeatureFlag('ai_features')"
|
||||
v-track.select_form_base="{ base: 'ai' }"
|
||||
class="rounded-md border p-4 flex flex-col items-center cursor-pointer hover:bg-gray-50"
|
||||
role="button"
|
||||
@@ -185,12 +185,6 @@ export default {
|
||||
loading: false,
|
||||
}),
|
||||
|
||||
computed: {
|
||||
aiFeaturesEnabled() {
|
||||
return this.runtimeConfig.public.aiFeaturesEnabled
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateForm() {
|
||||
if (this.loading) return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="customDomainsEnabled"
|
||||
v-if="useFeatureFlag('custom_domains')"
|
||||
id="custom-domains"
|
||||
>
|
||||
<UButton
|
||||
@@ -82,10 +82,6 @@ const customDomainsForm = useForm({
|
||||
const customDomainsLoading = ref(false)
|
||||
const showCustomDomainModal = ref(false)
|
||||
|
||||
const customDomainsEnabled = computed(
|
||||
() => useRuntimeConfig().public.customDomainsEnabled,
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
initCustomDomains()
|
||||
})
|
||||
|
||||
@@ -202,7 +202,7 @@ const showEditUserModal = ref(false)
|
||||
const selectedUser = ref(null)
|
||||
const userNewRole = ref("")
|
||||
|
||||
const paidPlansEnabled = computed(() => useRuntimeConfig().public.paidPlansEnabled)
|
||||
const paidPlansEnabled = ref(useFeatureFlag('billing.enabled'))
|
||||
const canInviteUser = computed(() => {
|
||||
return paidPlansEnabled.value ? workspace.value.is_pro : true
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user