Merge pull request #444 from shariquerik/saas-signup
feat: SaaS Billing page
This commit is contained in:
commit
8f312b02bf
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils import cint
|
||||||
from frappe.utils.telemetry import capture
|
from frappe.utils.telemetry import capture
|
||||||
|
|
||||||
no_cache = 1
|
no_cache = 1
|
||||||
@ -32,6 +33,7 @@ def get_boot():
|
|||||||
"site_name": frappe.local.site,
|
"site_name": frappe.local.site,
|
||||||
"read_only_mode": frappe.flags.read_only,
|
"read_only_mode": frappe.flags.read_only,
|
||||||
"csrf_token": frappe.sessions.get_csrf_token(),
|
"csrf_token": frappe.sessions.get_csrf_token(),
|
||||||
|
"setup_complete": cint(frappe.get_system_settings("setup_complete"))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit b2dbd41936905aa46b18d3c22e5d09a7b08a9b98
|
Subproject commit ee1e7d915a3147d387c419cd317d5982d060fd14
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"@vueuse/core": "^10.3.0",
|
"@vueuse/core": "^10.3.0",
|
||||||
"@vueuse/integrations": "^10.3.0",
|
"@vueuse/integrations": "^10.3.0",
|
||||||
"feather-icons": "^4.28.0",
|
"feather-icons": "^4.28.0",
|
||||||
"frappe-ui": "^0.1.71",
|
"frappe-ui": "^0.1.74",
|
||||||
"gemoji": "^8.1.0",
|
"gemoji": "^8.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime": "^4.0.1",
|
"mime": "^4.0.1",
|
||||||
@ -22,7 +22,6 @@
|
|||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.2",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"vite": "^4.4.9",
|
|
||||||
"vue": "^3.4.12",
|
"vue": "^3.4.12",
|
||||||
"vue-router": "^4.2.2",
|
"vue-router": "^4.2.2",
|
||||||
"vuedraggable": "^4.1.0"
|
"vuedraggable": "^4.1.0"
|
||||||
|
|||||||
@ -12,11 +12,11 @@ import { sessionStore as session } from '@/stores/session'
|
|||||||
import { Toasts } from 'frappe-ui'
|
import { Toasts } from 'frappe-ui'
|
||||||
import { computed, defineAsyncComponent } from 'vue'
|
import { computed, defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
const MobileLayout = defineAsyncComponent(() =>
|
const MobileLayout = defineAsyncComponent(
|
||||||
import('./components/Layouts/MobileLayout.vue')
|
() => import('./components/Layouts/MobileLayout.vue'),
|
||||||
)
|
)
|
||||||
const DesktopLayout = defineAsyncComponent(() =>
|
const DesktopLayout = defineAsyncComponent(
|
||||||
import('./components/Layouts/DesktopLayout.vue')
|
() => import('./components/Layouts/DesktopLayout.vue'),
|
||||||
)
|
)
|
||||||
const Layout = computed(() => {
|
const Layout = computed(() => {
|
||||||
if (window.innerWidth < 640) {
|
if (window.innerWidth < 640) {
|
||||||
|
|||||||
@ -71,6 +71,7 @@
|
|||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<TrialBanner v-if="isFCSite.data" />
|
||||||
<div class="m-2 flex flex-col gap-1">
|
<div class="m-2 flex flex-col gap-1">
|
||||||
<SidebarLink
|
<SidebarLink
|
||||||
:label="isSidebarCollapsed ? __('Expand') : __('Collapse')"
|
:label="isSidebarCollapsed ? __('Expand') : __('Collapse')"
|
||||||
@ -89,6 +90,7 @@
|
|||||||
</SidebarLink>
|
</SidebarLink>
|
||||||
</div>
|
</div>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
|
<Settings />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -108,14 +110,15 @@ import CollapseSidebar from '@/components/Icons/CollapseSidebar.vue'
|
|||||||
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
import NotificationsIcon from '@/components/Icons/NotificationsIcon.vue'
|
||||||
import SidebarLink from '@/components/SidebarLink.vue'
|
import SidebarLink from '@/components/SidebarLink.vue'
|
||||||
import Notifications from '@/components/Notifications.vue'
|
import Notifications from '@/components/Notifications.vue'
|
||||||
|
import Settings from '@/components/Settings/Settings.vue'
|
||||||
import { viewsStore } from '@/stores/views'
|
import { viewsStore } from '@/stores/views'
|
||||||
import {
|
import {
|
||||||
unreadNotificationsCount,
|
unreadNotificationsCount,
|
||||||
notificationsStore,
|
notificationsStore,
|
||||||
} from '@/stores/notifications'
|
} from '@/stores/notifications'
|
||||||
import { FeatherIcon } from 'frappe-ui'
|
import { FeatherIcon, TrialBanner, createResource } from 'frappe-ui'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { computed, h } from 'vue'
|
import { computed, h, provide } from 'vue'
|
||||||
|
|
||||||
const { getPinnedViews, getPublicViews } = viewsStore()
|
const { getPinnedViews, getPublicViews } = viewsStore()
|
||||||
const { toggle: toggleNotificationPanel } = notificationsStore()
|
const { toggle: toggleNotificationPanel } = notificationsStore()
|
||||||
@ -226,4 +229,13 @@ function getIcon(routeName, icon) {
|
|||||||
return PinIcon
|
return PinIcon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFCSite = createResource({
|
||||||
|
url: 'frappe.integrations.frappe_providers.frappecloud_billing.is_fc_site',
|
||||||
|
cache: 'isFCSite',
|
||||||
|
auto: true,
|
||||||
|
transform: (data) => Boolean(data),
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('isFCSite', isFCSite)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog v-model="show" :options="{ size: '5xl' }">
|
<Dialog
|
||||||
|
v-model="showSettings"
|
||||||
|
:options="{ size: '5xl' }"
|
||||||
|
@close="activeSettingsPage = ''"
|
||||||
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex h-[calc(100vh_-_8rem)]">
|
<div class="flex h-[calc(100vh_-_8rem)]">
|
||||||
<div class="flex w-52 shrink-0 flex-col bg-gray-50 p-2">
|
<div class="flex w-52 shrink-0 flex-col bg-gray-50 p-2">
|
||||||
@ -47,11 +51,16 @@ import WhatsAppSettings from '@/components/Settings/WhatsAppSettings.vue'
|
|||||||
import ERPNextSettings from '@/components/Settings/ERPNextSettings.vue'
|
import ERPNextSettings from '@/components/Settings/ERPNextSettings.vue'
|
||||||
import TwilioSettings from '@/components/Settings/TwilioSettings.vue'
|
import TwilioSettings from '@/components/Settings/TwilioSettings.vue'
|
||||||
import SidebarLink from '@/components/SidebarLink.vue'
|
import SidebarLink from '@/components/SidebarLink.vue'
|
||||||
import { isWhatsappInstalled } from '@/composables/settings'
|
import { usersStore } from '@/stores/users'
|
||||||
|
import {
|
||||||
|
isWhatsappInstalled,
|
||||||
|
showSettings,
|
||||||
|
activeSettingsPage,
|
||||||
|
} from '@/composables/settings'
|
||||||
import { Dialog } from 'frappe-ui'
|
import { Dialog } from 'frappe-ui'
|
||||||
import { ref, markRaw, computed, h } from 'vue'
|
import { ref, markRaw, computed, watch } from 'vue'
|
||||||
|
|
||||||
const show = defineModel()
|
const { isManager } = usersStore()
|
||||||
|
|
||||||
const tabs = computed(() => {
|
const tabs = computed(() => {
|
||||||
let _tabs = [
|
let _tabs = [
|
||||||
@ -68,6 +77,7 @@ const tabs = computed(() => {
|
|||||||
label: __('Invite Members'),
|
label: __('Invite Members'),
|
||||||
icon: 'user-plus',
|
icon: 'user-plus',
|
||||||
component: markRaw(InviteMemberPage),
|
component: markRaw(InviteMemberPage),
|
||||||
|
condition: () => isManager(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -94,16 +104,29 @@ const tabs = computed(() => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return _tabs.map((tab) => {
|
return _tabs.filter((tab) => {
|
||||||
tab.items = tab.items.filter((item) => {
|
if (tab.condition && !tab.condition()) return false
|
||||||
if (item.condition) {
|
if (tab.items) {
|
||||||
return item.condition()
|
tab.items = tab.items.filter((item) => {
|
||||||
}
|
if (item.condition && !item.condition()) return false
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
return tab
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const activeTab = ref(tabs.value[0].items[0])
|
const activeTab = ref(tabs.value[0].items[0])
|
||||||
|
|
||||||
|
function setActiveTab(tabName) {
|
||||||
|
activeTab.value =
|
||||||
|
(tabName &&
|
||||||
|
tabs.value
|
||||||
|
.map((tab) => tab.items)
|
||||||
|
.flat()
|
||||||
|
.find((tab) => tab.label === tabName)) ||
|
||||||
|
tabs.value[0].items[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(activeSettingsPage, (activePage) => setActiveTab(activePage))
|
||||||
</script>
|
</script>
|
||||||
@ -44,17 +44,16 @@
|
|||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<SettingsModal v-if="showSettingsModal" v-model="showSettingsModal" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import SettingsModal from '@/components/Settings/SettingsModal.vue'
|
|
||||||
import CRMLogo from '@/components/Icons/CRMLogo.vue'
|
import CRMLogo from '@/components/Icons/CRMLogo.vue'
|
||||||
import Apps from '@/components/Apps.vue'
|
import Apps from '@/components/Apps.vue'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { showSettings } from '@/composables/settings'
|
||||||
import { Dropdown } from 'frappe-ui'
|
import { Dropdown } from 'frappe-ui'
|
||||||
import { computed, ref, markRaw} from 'vue'
|
import { computed, ref, markRaw, inject } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isCollapsed: {
|
isCollapsed: {
|
||||||
@ -68,7 +67,7 @@ const { getUser } = usersStore()
|
|||||||
|
|
||||||
const user = computed(() => getUser() || {})
|
const user = computed(() => getUser() || {})
|
||||||
|
|
||||||
const showSettingsModal = ref(false)
|
const isFCSite = inject('isFCSite')
|
||||||
|
|
||||||
let dropdownOptions = ref([
|
let dropdownOptions = ref([
|
||||||
{
|
{
|
||||||
@ -94,10 +93,16 @@ let dropdownOptions = ref([
|
|||||||
group: 'Others',
|
group: 'Others',
|
||||||
hideLabel: true,
|
hideLabel: true,
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
icon: 'credit-card',
|
||||||
|
label: computed(() => __('Billing')),
|
||||||
|
onClick: () => (window.location.href = '/billing'),
|
||||||
|
condition: () => isFCSite.data,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'settings',
|
icon: 'settings',
|
||||||
label: computed(() => __('Settings')),
|
label: computed(() => __('Settings')),
|
||||||
onClick: () => (showSettingsModal.value = true),
|
onClick: () => (showSettings.value = true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'log-out',
|
icon: 'log-out',
|
||||||
|
|||||||
@ -33,3 +33,6 @@ createResource({
|
|||||||
export const mobileSidebarOpened = ref(false)
|
export const mobileSidebarOpened = ref(false)
|
||||||
|
|
||||||
export const isMobileView = computed(() => window.innerWidth < 768)
|
export const isMobileView = computed(() => window.innerWidth < 768)
|
||||||
|
|
||||||
|
export const showSettings = ref(false)
|
||||||
|
export const activeSettingsPage = ref('')
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export const sessionStore = defineStore('crm-session', () => {
|
|||||||
onSuccess() {
|
onSuccess() {
|
||||||
userResource.reset()
|
userResource.reset()
|
||||||
user.value = null
|
user.value = null
|
||||||
router.replace({ name: 'Home' })
|
window.location.href = '/login?redirect-to=/crm'
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -3780,11 +3780,6 @@ human-signals@^5.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
|
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
|
||||||
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
|
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
|
||||||
|
|
||||||
husky@>=6:
|
|
||||||
version "9.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.6.tgz#e23aa996b6203ab33534bdc82306b0cf2cb07d6c"
|
|
||||||
integrity sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==
|
|
||||||
|
|
||||||
iconv-lite@0.6.3:
|
iconv-lite@0.6.3:
|
||||||
version "0.6.3"
|
version "0.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user