Merge pull request #1317 from pateljannat/trial-signup
feat: billing banner for FC trial sites
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.csrf_token = '{{ csrf_token }}'
|
window.csrf_token = '{{ csrf_token }}'
|
||||||
|
window.setup_complete = '{{ setup_complete }}'
|
||||||
document.getElementById('seo-content').style.display = 'none';
|
document.getElementById('seo-content').style.display = 'none';
|
||||||
</script>
|
</script>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
|||||||
@@ -62,25 +62,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SidebarLink
|
<div>
|
||||||
:link="{
|
<TrialBanner
|
||||||
label: sidebarStore.isSidebarCollapsed ? 'Expand' : 'Collapse',
|
v-if="
|
||||||
}"
|
userResource.data?.user_type == 'System User' &&
|
||||||
:isCollapsed="sidebarStore.isSidebarCollapsed"
|
userResource.data?.is_fc_site
|
||||||
@click="toggleSidebar()"
|
"
|
||||||
class="m-2"
|
:isSidebarCollapsed="sidebarStore.isSidebarCollapsed"
|
||||||
>
|
/>
|
||||||
<template #icon>
|
<SidebarLink
|
||||||
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
|
:link="{
|
||||||
<CollapseSidebar
|
label: sidebarStore.isSidebarCollapsed ? 'Expand' : 'Collapse',
|
||||||
class="h-4.5 w-4.5 text-ink-gray-7 duration-300 ease-in-out"
|
}"
|
||||||
:class="{
|
:isCollapsed="sidebarStore.isSidebarCollapsed"
|
||||||
'[transform:rotateY(180deg)]': sidebarStore.isSidebarCollapsed,
|
@click="toggleSidebar()"
|
||||||
}"
|
class="m-2"
|
||||||
/>
|
>
|
||||||
</span>
|
<template #icon>
|
||||||
</template>
|
<span class="grid h-5 w-6 flex-shrink-0 place-items-center">
|
||||||
</SidebarLink>
|
<CollapseSidebar
|
||||||
|
class="h-4.5 w-4.5 text-ink-gray-7 duration-300 ease-in-out"
|
||||||
|
:class="{
|
||||||
|
'[transform:rotateY(180deg)]': sidebarStore.isSidebarCollapsed,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</SidebarLink>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<PageModal
|
<PageModal
|
||||||
v-model="showPageModal"
|
v-model="showPageModal"
|
||||||
@@ -101,7 +110,7 @@ import { sessionStore } from '@/stores/session'
|
|||||||
import { useSidebar } from '@/stores/sidebar'
|
import { useSidebar } from '@/stores/sidebar'
|
||||||
import { useSettings } from '@/stores/settings'
|
import { useSettings } from '@/stores/settings'
|
||||||
import { ChevronRight, Plus } from 'lucide-vue-next'
|
import { ChevronRight, Plus } from 'lucide-vue-next'
|
||||||
import { createResource, Button } from 'frappe-ui'
|
import { Button, createResource, TrialBanner } from 'frappe-ui'
|
||||||
import PageModal from '@/components/Modals/PageModal.vue'
|
import PageModal from '@/components/Modals/PageModal.vue'
|
||||||
|
|
||||||
const { user, sidebarSettings } = sessionStore()
|
const { user, sidebarSettings } = sessionStore()
|
||||||
|
|||||||
94
frontend/src/components/Modals/FCVerfiyCodeModal.vue
Normal file
94
frontend/src/components/Modals/FCVerfiyCodeModal.vue
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model="show"
|
||||||
|
:options="{
|
||||||
|
size: 'xl',
|
||||||
|
title: __('Login to Frappe Cloud'),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: __('Verify'),
|
||||||
|
variant: 'solid',
|
||||||
|
onClick: (close) => {
|
||||||
|
verifyCode(close)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body-content>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ __('We have sent the verificaton code to your email id ') }}
|
||||||
|
<b>{{ props.email }}</b>
|
||||||
|
</p>
|
||||||
|
<FormControl
|
||||||
|
v-model="code"
|
||||||
|
:label="__('Verification Code')"
|
||||||
|
class="mb-4"
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
{{ __("Didn't receive the code?") }}
|
||||||
|
<a href="#" @click="resendCode">{{ __('Resend') }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { call, Dialog } from 'frappe-ui'
|
||||||
|
import { showToast } from '@/utils'
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
const code = ref('')
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const verifyCode = (close) => {
|
||||||
|
if (!code.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
call(
|
||||||
|
'frappe.integrations.frappe_providers.frappecloud_billing.verify_verification_code',
|
||||||
|
{
|
||||||
|
verification_code: code.value,
|
||||||
|
route: window.route,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.message.login_token) {
|
||||||
|
close()
|
||||||
|
window.open(
|
||||||
|
`${frappeCloudBaseEndpoint}/api/method/press.api.developer.saas.login_to_fc?token=${data.message.login_token}`,
|
||||||
|
'_blank'
|
||||||
|
)
|
||||||
|
showToast(
|
||||||
|
__('Frappe Cloud Login Successful'),
|
||||||
|
`<p>${__('You will be redirected to Frappe Cloud soon.')}</p><p>${__(
|
||||||
|
"If you haven't been redirected,"
|
||||||
|
)} <a href="${frappeCloudBaseEndpoint}/api/method/press.api.developer.saas.login_to_fc?token=${
|
||||||
|
data.message.login_token
|
||||||
|
}" target="_blank">${__('Click here to login')}</a></p>`,
|
||||||
|
'check'
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
showToast(__('Login failed'), __('Please try again'), 'x')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToast(__('Login failed'), __('Please try again'), 'x')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resendCode = () => {
|
||||||
|
call(
|
||||||
|
'frappe.integrations.frappe_providers.frappecloud_billing.send_verification_code'
|
||||||
|
).catch((err) => {
|
||||||
|
showToast(__('Failed to resend code'), __('Please try again'), 'x')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -59,13 +59,22 @@
|
|||||||
v-if="userResource.data?.is_moderator"
|
v-if="userResource.data?.is_moderator"
|
||||||
v-model="showSettingsModal"
|
v-model="showSettingsModal"
|
||||||
/>
|
/>
|
||||||
|
<FCVerfiyCodeModal v-if="showFCLoginDialog" :email="verificationEmail" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { Dropdown } from 'frappe-ui'
|
import { call, Dropdown } from 'frappe-ui'
|
||||||
import Apps from '@/components/Apps.vue'
|
import Apps from '@/components/Apps.vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { convertToTitleCase, showToast } from '@/utils'
|
||||||
|
import { usersStore } from '@/stores/user'
|
||||||
|
import { useSettings } from '@/stores/settings'
|
||||||
|
import { markRaw, watch, ref, onMounted, computed } from 'vue'
|
||||||
|
import SettingsModal from '@/components/Modals/Settings.vue'
|
||||||
|
import { createDialog } from '@/utils/dialogs'
|
||||||
|
import FCVerfiyCodeModal from './Modals/FCVerfiyCodeModal.vue'
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
LogIn,
|
LogIn,
|
||||||
@@ -74,13 +83,8 @@ import {
|
|||||||
User,
|
User,
|
||||||
Settings,
|
Settings,
|
||||||
Sun,
|
Sun,
|
||||||
|
LogInIcon,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { convertToTitleCase } from '../utils'
|
|
||||||
import { usersStore } from '@/stores/user'
|
|
||||||
import { useSettings } from '@/stores/settings'
|
|
||||||
import { markRaw, watch, ref, onMounted, computed } from 'vue'
|
|
||||||
import SettingsModal from '@/components/Modals/Settings.vue'
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { logout, branding } = sessionStore()
|
const { logout, branding } = sessionStore()
|
||||||
@@ -89,6 +93,11 @@ const settingsStore = useSettings()
|
|||||||
let { isLoggedIn } = sessionStore()
|
let { isLoggedIn } = sessionStore()
|
||||||
const showSettingsModal = ref(false)
|
const showSettingsModal = ref(false)
|
||||||
const theme = ref('light')
|
const theme = ref('light')
|
||||||
|
const $dialog = createDialog
|
||||||
|
const frappeCloudBaseEndpoint = 'https://frappecloud.com'
|
||||||
|
|
||||||
|
const showFCLoginDialog = ref(false)
|
||||||
|
const verificationEmail = ref(null)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isCollapsed: {
|
isCollapsed: {
|
||||||
@@ -130,6 +139,13 @@ const userDropdownOptions = computed(() => {
|
|||||||
return isLoggedIn
|
return isLoggedIn
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: theme.value === 'light' ? Moon : Sun,
|
||||||
|
label: 'Toggle Theme',
|
||||||
|
onClick: () => {
|
||||||
|
toggleTheme()
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: markRaw(Apps),
|
component: markRaw(Apps),
|
||||||
condition: () => {
|
condition: () => {
|
||||||
@@ -139,13 +155,6 @@ const userDropdownOptions = computed(() => {
|
|||||||
else return false
|
else return false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: theme.value === 'light' ? Moon : Sun,
|
|
||||||
label: 'Toggle Theme',
|
|
||||||
onClick: () => {
|
|
||||||
toggleTheme()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: Settings,
|
icon: Settings,
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
@@ -156,6 +165,19 @@ const userDropdownOptions = computed(() => {
|
|||||||
return userResource.data?.is_moderator
|
return userResource.data?.is_moderator
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: LogInIcon,
|
||||||
|
label: 'Login to Frappe Cloud',
|
||||||
|
onClick: () => {
|
||||||
|
initiateRequestForLoginToFrappeCloud()
|
||||||
|
},
|
||||||
|
condition: () => {
|
||||||
|
return (
|
||||||
|
userResource.data?.user_type == 'System User' &&
|
||||||
|
userResource.data?.is_fc_site
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: LogOut,
|
icon: LogOut,
|
||||||
label: 'Log out',
|
label: 'Log out',
|
||||||
@@ -180,4 +202,48 @@ const userDropdownOptions = computed(() => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const initiateRequestForLoginToFrappeCloud = () => {
|
||||||
|
$dialog({
|
||||||
|
title: __('Login to Frappe Cloud?'),
|
||||||
|
message: __(
|
||||||
|
'Are you sure you want to login to your Frappe Cloud dashboard?'
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: __('Confirm'),
|
||||||
|
variant: 'solid',
|
||||||
|
onClick(close) {
|
||||||
|
requestLoginToFC()
|
||||||
|
close()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestLoginToFC = () => {
|
||||||
|
call(
|
||||||
|
'frappe.integrations.frappe_providers.frappecloud_billing.send_verification_code'
|
||||||
|
)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.message.is_user_logged_in) {
|
||||||
|
window.open(
|
||||||
|
`${frappeCloudBaseEndpoint}${data.message.redirect_to}`,
|
||||||
|
'_blank'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
showFCLoginDialog.value = true
|
||||||
|
verificationEmail.value = data.message.email
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToast(
|
||||||
|
__('Failed to login to Frappe Cloud'),
|
||||||
|
__('Please try again'),
|
||||||
|
'x'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
allowedHosts: ['fs'],
|
allowedHosts: ['fs', 'bs'],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from frappe.utils import (
|
|||||||
from lms.lms.utils import get_average_rating, get_lesson_count
|
from lms.lms.utils import get_average_rating, get_lesson_count
|
||||||
from xml.dom.minidom import parseString
|
from xml.dom.minidom import parseString
|
||||||
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
||||||
from frappe.core.doctype.communication.email import make
|
from frappe.integrations.frappe_providers.frappecloud_billing import is_fc_site
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -175,6 +175,7 @@ def get_user_info():
|
|||||||
user.is_moderator = "Moderator" in user.roles
|
user.is_moderator = "Moderator" in user.roles
|
||||||
user.is_evaluator = "Batch Evaluator" in user.roles
|
user.is_evaluator = "Batch Evaluator" in user.roles
|
||||||
user.is_student = "LMS Student" in user.roles
|
user.is_student = "LMS Student" in user.roles
|
||||||
|
user.is_fc_site = is_fc_site()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils.telemetry import capture
|
|
||||||
from frappe import _
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
import re
|
import re
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils.telemetry import capture
|
||||||
|
from frappe.utils import cint
|
||||||
|
|
||||||
no_cache = 1
|
no_cache = 1
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ def get_context():
|
|||||||
csrf_token = frappe.sessions.get_csrf_token()
|
csrf_token = frappe.sessions.get_csrf_token()
|
||||||
frappe.db.commit() # nosemgrep
|
frappe.db.commit() # nosemgrep
|
||||||
context.csrf_token = csrf_token
|
context.csrf_token = csrf_token
|
||||||
|
context.setup_complete = cint(frappe.get_system_settings("setup_complete"))
|
||||||
capture("active_site", "lms")
|
capture("active_site", "lms")
|
||||||
context.favicon = frappe.db.get_single_value("Website Settings", "favicon")
|
context.favicon = frappe.db.get_single_value("Website Settings", "favicon")
|
||||||
return context
|
return context
|
||||||
|
|||||||
Reference in New Issue
Block a user