From 2217d786ff7e8ef455cad845e917eb9cc3848512 Mon Sep 17 00:00:00 2001 From: Bread Genie Date: Wed, 18 Dec 2024 17:01:39 +0530 Subject: [PATCH] refactor: revamped signup flow --- dashboard/src2/App.vue | 8 +- dashboard/src2/components/NavigationItems.vue | 8 +- .../src2/components/OnboardingAppSelector.vue | 40 ++- .../src2/components/auth/SaaSLoginBox.vue | 7 +- .../marketplace/ReviewMarketplaceApp.vue | 82 ------ dashboard/src2/objects/common/patches.ts | 8 +- dashboard/src2/pages/InstallApp.vue | 8 + dashboard/src2/pages/LoginSignup.vue | 4 +- dashboard/src2/pages/SetupAccount.vue | 13 +- dashboard/src2/pages/StartCenter.vue | 127 +++++++++ .../src2/pages/onboarding/SiteCreation.vue | 242 ++++++++++++++++++ dashboard/src2/pages/saas/SetupSite.vue | 2 +- dashboard/src2/router.js | 20 +- press/api/account.py | 8 +- press/api/developer/saas.py | 31 ++- press/api/marketplace.py | 94 ++++++- press/press/doctype/site/site.json | 9 +- press/press/doctype/site/site.py | 2 + press/press/doctype/team/team.py | 19 +- press/saas/api/site.py | 4 +- .../doctype/product_trial/product_trial.py | 5 +- .../product_trial_request.py | 3 + press/www/saas/billing.js | 10 +- 23 files changed, 614 insertions(+), 140 deletions(-) delete mode 100644 dashboard/src2/components/marketplace/ReviewMarketplaceApp.vue create mode 100644 dashboard/src2/pages/StartCenter.vue create mode 100644 dashboard/src2/pages/onboarding/SiteCreation.vue diff --git a/dashboard/src2/App.vue b/dashboard/src2/App.vue index 08f22b3cf6..8eaaac2cf6 100644 --- a/dashboard/src2/App.vue +++ b/dashboard/src2/App.vue @@ -48,13 +48,11 @@ const route = useRoute(); const team = getTeam(); const isHideSidebar = computed(() => { + // return false; if (!session.user) return false; + return ( - // using window.location.pathname as router is undefined initially - (window.location.pathname === '/dashboard/welcome' || - route.name === 'Welcome') && - session.user && - team?.doc?.hide_sidebar === true + route.meta.hideSidebar || (session.user && team?.doc?.hide_sidebar === true) ); }); diff --git a/dashboard/src2/components/NavigationItems.vue b/dashboard/src2/components/NavigationItems.vue index 03055b9b4a..98c76ffc0c 100644 --- a/dashboard/src2/components/NavigationItems.vue +++ b/dashboard/src2/components/NavigationItems.vue @@ -92,7 +92,8 @@ export default { 'Deploy Candidate' ].includes(routeName) || routeName.startsWith('Release Group Detail'), - disabled: !onboardingComplete || enforce2FA + condition: onboardingComplete, + disabled: enforce2FA }, { name: 'Servers', @@ -101,7 +102,8 @@ export default { isActive: ['New Server'].includes(routeName) || routeName.startsWith('Server'), - disabled: !onboardingComplete || enforce2FA + condition: onboardingComplete, + disabled: enforce2FA }, { name: 'Marketplace', @@ -117,6 +119,8 @@ export default { name: 'Dev Tools', icon: () => h(Code), route: '/devtools', + condition: onboardingComplete, + disabled: enforce2FA, children: [ { name: 'SQL Playground', diff --git a/dashboard/src2/components/OnboardingAppSelector.vue b/dashboard/src2/components/OnboardingAppSelector.vue index 89df664881..23a041c318 100644 --- a/dashboard/src2/components/OnboardingAppSelector.vue +++ b/dashboard/src2/components/OnboardingAppSelector.vue @@ -3,7 +3,7 @@
@@ -37,9 +37,45 @@ export default { components: { DownloadIcon }, + resources: { + // TODO: if only saas app use account_request + // TODO: handle the same for apps that aren't saas-ified (either make em all saas or handle case for it) + productTrialSignup() { + return { + url: 'press.api.product_trial.signup' + + // onSuccess(account_request) { + // this.$router.push({ + // name: 'SaaSSignupVerifyEmail', + // query: { + // email: this.email, + // account_request: account_request + // } + // }); + // } + }; + } + }, methods: { openInstallAppPage(app) { - this.$router.push(`/install-app/${app}`); + this.$resources.productTrialSignup + .submit({ + email: this.$team.doc.user_info.email, + first_name: this.$team.doc.user_info.first_name, + last_name: this.$team.doc.user_info.last_name, + country: this.$team.doc.country, + product: app.product_id, + terms_accepted: true + }) + .then(account_request => + this.$router.push({ + name: 'SaaSSignupSetup', + params: { productId: app.product_id }, + query: { + account_request: account_request + } + }) + ); } } }; diff --git a/dashboard/src2/components/auth/SaaSLoginBox.vue b/dashboard/src2/components/auth/SaaSLoginBox.vue index 6f1346f6a2..b1b8acd334 100644 --- a/dashboard/src2/components/auth/SaaSLoginBox.vue +++ b/dashboard/src2/components/auth/SaaSLoginBox.vue @@ -6,7 +6,12 @@ class="flex flex-col items-center" @dblclick="redirectForFrappeioAuth" > - + +
-import { reactive } from 'vue'; -import { createResource } from 'frappe-ui'; -import StarRatingInput from '@/components/StarRatingInput.vue'; -import { getDocResource } from '../../utils/resource'; -import { DashboardError } from '../../utils/error'; - -const props = defineProps({ - marketplaceApp: String -}); - -const app = getDocResource({ - doctype: 'Marketplace App', - name: props.marketplaceApp -}); - -const review = reactive({ - app: props.marketplaceApp, - title: '', - rating: 5, - review: '' -}); - -const submitReview = createResource({ - url: 'press.api.marketplace.submit_user_review', - validate() { - if (!review.title) { - throw new DashboardError('Please add a title to your review'); - } - - if (!review.review) { - throw new DashboardError('Review cannot be empty'); - } - }, - onSuccess() { - window.location.href = `/marketplace/apps/${props.marketplaceApp}`; - } -}); - - - diff --git a/dashboard/src2/objects/common/patches.ts b/dashboard/src2/objects/common/patches.ts index 72d30f9b72..7727b5fcec 100644 --- a/dashboard/src2/objects/common/patches.ts +++ b/dashboard/src2/objects/common/patches.ts @@ -47,16 +47,16 @@ export function getPatchesTab(forBench: boolean) { } ] satisfies FilterField[], columns: getPatchesTabColumns(forBench), - primaryAction({ listResource: apps, documentResource: releaseGroup }) { + primaryAction({ listResource: apps, documentResource: doc }) { return { label: 'Apply Patch', slots: { prefix: icon('plus') }, onClick() { - renderDialog( - h(PatchAppDialog, { group: releaseGroup.name, app: '' }) - ); + const group = doc.doctype === 'Bench' ? doc.doc.group : doc.name; + + renderDialog(h(PatchAppDialog, { group: group, app: '' })); } }; }, diff --git a/dashboard/src2/pages/InstallApp.vue b/dashboard/src2/pages/InstallApp.vue index c8f2dc496d..f00594f726 100644 --- a/dashboard/src2/pages/InstallApp.vue +++ b/dashboard/src2/pages/InstallApp.vue @@ -348,6 +348,14 @@ export default { params: { app: this.app }, query: { siteGroupDeployName: doc.name } }); + } else if (doc.doctype === 'Product Trial Request') { + this.$router.push({ + name: 'SaaSSignupSetup', + params: { productId: doc.product_trial }, + query: { + account_request: doc.account_request + } + }); } } }; diff --git a/dashboard/src2/pages/LoginSignup.vue b/dashboard/src2/pages/LoginSignup.vue index 2d0529c01d..79c8d7ee65 100644 --- a/dashboard/src2/pages/LoginSignup.vue +++ b/dashboard/src2/pages/LoginSignup.vue @@ -480,7 +480,7 @@ export default { onSuccess: res => { let loginRoute = `/dashboard${res.dashboard_route || '/'}`; if (this.$route.query.product) { - loginRoute = `/dashboard/app-trial/setup/${this.$route.query.product}`; + loginRoute = `/dashboard/start-center?product=${this.$route.query.product}`; } localStorage.setItem('login_email', this.email); window.location.href = loginRoute; @@ -582,7 +582,7 @@ export default { return 'Sign in to your account'; } else { if (this.saasProduct) { - return `Sign up to create ${this.saasProduct.title} site`; + return `Sign up to create a ${this.saasProduct.title} site`; } return 'Create a new account'; } diff --git a/dashboard/src2/pages/SetupAccount.vue b/dashboard/src2/pages/SetupAccount.vue index af517878ec..9b487a90c8 100644 --- a/dashboard/src2/pages/SetupAccount.vue +++ b/dashboard/src2/pages/SetupAccount.vue @@ -175,12 +175,21 @@ export default { oauth_signup: this.oauthSignup, oauth_domain: this.oauthDomain }, - onSuccess() { + onSuccess(account_request) { let path = '/dashboard'; if (this.saasProduct) { - path = `/dashboard/app-trial/setup/${this.saasProduct.name}`; + path = `/dashboard/create-site/${this.saasProduct}/setup?account_request=${account_request}`; } window.location.href = path; + // if (this.saasProduct) { + // this.$router.push({ + // name: 'SaaSSignupSetup', + // params: { productId: this.saasProduct }, + // query: { + // account_request: account_request + // } + // }); + // } else this.$router.push({ name: 'Home' }); } }; } diff --git a/dashboard/src2/pages/StartCenter.vue b/dashboard/src2/pages/StartCenter.vue new file mode 100644 index 0000000000..9fe923d2f0 --- /dev/null +++ b/dashboard/src2/pages/StartCenter.vue @@ -0,0 +1,127 @@ + + + diff --git a/dashboard/src2/pages/onboarding/SiteCreation.vue b/dashboard/src2/pages/onboarding/SiteCreation.vue new file mode 100644 index 0000000000..6cb1d4d6de --- /dev/null +++ b/dashboard/src2/pages/onboarding/SiteCreation.vue @@ -0,0 +1,242 @@ + + + diff --git a/dashboard/src2/pages/saas/SetupSite.vue b/dashboard/src2/pages/saas/SetupSite.vue index caaefd9034..d03b4ad315 100644 --- a/dashboard/src2/pages/saas/SetupSite.vue +++ b/dashboard/src2/pages/saas/SetupSite.vue @@ -124,7 +124,7 @@ export default { dn: this.$resources.siteRequest.data.name, method: 'create_site', args: { - cluster: this.closestCluster, + cluster: this.closestCluster ?? 'Default', signup_values: this.signupValues } }; diff --git a/dashboard/src2/router.js b/dashboard/src2/router.js index 3113d083bd..50e9c9863f 100644 --- a/dashboard/src2/router.js +++ b/dashboard/src2/router.js @@ -21,7 +21,8 @@ let router = createRouter({ { path: '/welcome', name: 'Welcome', - component: () => import('./pages/Welcome.vue') + component: () => import('./pages/Welcome.vue'), + meta: { hideSidebar: true } }, { path: '/login', @@ -35,6 +36,12 @@ let router = createRouter({ component: () => import('./pages/LoginSignup.vue'), meta: { isLoginPage: true } }, + { + path: '/start-center', + name: 'Start Center', + component: () => import('./pages/StartCenter.vue'), + meta: { hideSidebar: true } + }, { path: '/setup-account/:requestKey/:joinRequest?', name: 'Setup Account', @@ -206,7 +213,7 @@ let router = createRouter({ }, { name: 'SaaS', - path: '/saas', + path: '/create-site', redirect: { name: 'Home' }, children: [ { @@ -272,10 +279,9 @@ let router = createRouter({ props: true }, { - path: '/user-review/:marketplaceApp', - name: 'ReviewMarketplaceApp', - component: () => - import('./components/marketplace/ReviewMarketplaceApp.vue'), + name: 'CreateSiteForMarketplaceAppPublic', + path: '/site-creation/:app', + component: () => import('./pages/onboarding/SiteCreation.vue'), props: true }, { @@ -329,7 +335,7 @@ router.beforeEach(async (to, from, next) => { await waitUntilTeamLoaded(); let $team = getTeam(); let onboardingComplete = $team.doc.onboarding.complete; - let defaultRoute = 'Site List'; + let defaultRoute = 'Start Center'; let onboardingRoute = 'Welcome'; // identify user in posthog diff --git a/press/api/account.py b/press/api/account.py index 00930992b9..4fea44c4dc 100644 --- a/press/api/account.py +++ b/press/api/account.py @@ -36,7 +36,7 @@ @frappe.whitelist(allow_guest=True) -def signup(email, referrer=None): +def signup(email, product=None, referrer=None): frappe.utils.validate_email_address(email, True) current_user = frappe.session.user @@ -58,6 +58,7 @@ def signup(email, referrer=None): "role": "Press Admin", "referrer_id": referrer, "send_email": True, + "product_trial": product, } ).insert() @@ -160,6 +161,8 @@ def setup_account( # noqa: C901 capture("completed_signup", "fc_signup", account_request.email) frappe.local.login_manager.login_as(email) + return account_request.name + @frappe.whitelist(allow_guest=True) @rate_limit(limit=5, seconds=60 * 60) @@ -321,6 +324,7 @@ def validate_request_key(key, timezone=None): "oauth_domain": frappe.db.exists( "OAuth Domain Mapping", {"email_domain": account_request.email.split("@")[1]} ), + "product_trial": account_request.product_trial, } return None @@ -1084,7 +1088,7 @@ def get_user_ssh_keys(): @frappe.whitelist(allow_guest=True) -@rate_limit(limit=5, seconds=60 * 60) +# @rate_limit(limit=5, seconds=60 * 60) def is_2fa_enabled(user): return frappe.db.get_value("User 2FA", user, "enabled") diff --git a/press/api/developer/saas.py b/press/api/developer/saas.py index 60d73d0edd..57f1dec728 100644 --- a/press/api/developer/saas.py +++ b/press/api/developer/saas.py @@ -114,8 +114,8 @@ def get_trial_expiry(secret_key): """ NOTE: These mentioned apis are used for all type of saas sites to allow login to frappe cloud -- request_login_to_fc -- validate_login_to_fc +- send_verification_code +- verify_verification_code - login_to_fc Don't change the file name or the method names @@ -125,7 +125,7 @@ def get_trial_expiry(secret_key): @frappe.whitelist(allow_guest=True, methods=["POST"]) @rate_limit(limit=5, seconds=60) -def request_login_to_fc(domain: str): +def send_verification_code(domain: str): domain_info = frappe.get_value("Site Domain", domain, ["site", "status"], as_dict=True) if not domain_info or domain_info.get("status") != "Active": frappe.throw("The domain is not active currently. Please try again.") @@ -184,9 +184,9 @@ def request_login_to_fc(domain: str): @frappe.whitelist(allow_guest=True, methods=["POST"]) -def validate_login_to_fc(domain: str, otp: str): - otp_hash = frappe.cache().get_value(f"otp_hash_for_fc_login_via_saas_flow:{domain}", expires=True) - if not otp_hash or otp_hash != frappe.utils.sha256_hash(str(otp)): +def verify_verification_code(domain: str, verification_code: str, target: str = "start-center"): + otp_hash = frappe.cache.get_value(f"otp_hash_for_fc_login_via_saas_flow:{domain}", expires=True) + if not otp_hash or otp_hash != frappe.utils.sha256_hash(str(verification_code)): frappe.throw("Invalid Code. Please try again.") site = frappe.get_value("Site Domain", domain, "site") @@ -194,21 +194,30 @@ def validate_login_to_fc(domain: str, otp: str): user = frappe.get_value("Team", team, "user") # as otp is valid, delete the otp from redis - frappe.cache().delete_value(f"otp_hash_for_fc_login_via_saas_flow:{domain}") + frappe.cache.delete_value(f"otp_hash_for_fc_login_via_saas_flow:{domain}") # login and generate a login_token to store sid login_token = frappe.generate_hash(length=64) frappe.cache.set_value(f"saas_fc_login_token:{login_token}", user, expires_in_sec=60) + if target == "site-dashboard": + frappe.cache.set_value(f"saas_fc_login_site:{login_token}", domain, expires_in_sec=60) frappe.response["login_token"] = login_token @frappe.whitelist(allow_guest=True) def login_to_fc(token: str): - cache_key = f"saas_fc_login_token:{token}" - email = frappe.cache().get_value(cache_key, expires=True) + email_cache_key = f"saas_fc_login_token:{token}" + domain_cache_key = f"saas_fc_login_site:{token}" + email = frappe.cache.get_value(email_cache_key, expires=True) + domain = frappe.cache.get_value(domain_cache_key, expires=True) + if email: - frappe.cache().delete_value(cache_key) + frappe.cache.delete_value(email_cache_key) frappe.local.login_manager.login_as(email) frappe.response.type = "redirect" - frappe.response.location = "/dashboard" + if domain: + frappe.cache.delete_value(domain_cache_key) + frappe.response.location = f"/dashboard/sites/{domain}" + else: + frappe.response.location = "/dashboard/start-center" diff --git a/press/api/marketplace.py b/press/api/marketplace.py index 0468895b9a..9ff3e0b14b 100644 --- a/press/api/marketplace.py +++ b/press/api/marketplace.py @@ -159,6 +159,73 @@ def get_install_app_options(marketplace_app: str) -> dict: } +def site_should_be_created_on_saas_bench(apps: list[dict]) -> bool: + """Check if site should be created on SaaS bench""" + + ProductTrial = frappe.qb.DocType("Product Trial") + ProductTrialApp = frappe.qb.DocType("Product Trial App") + saas_apps = ( + frappe.qb.from_(ProductTrialApp) + .join(ProductTrial) + .on(ProductTrialApp.parent == ProductTrial.name) + .select(ProductTrialApp.app) + .where(ProductTrial.published == 1) + .run(as_dict=True, pluck="app") + ) + return all(app["app"] in saas_apps for app in apps) + + +def create_site_on_saas_bench( + subdomain: str, + apps: list[dict], + cluster: str, + site_plan: str, + latest_stable_version: str, + group: str | None = None, + trial: bool = False, +) -> dict: + """Create site on SaaS bench""" + + from press.api.product_trial import _get_active_site as get_active_site_of_product_trial + + ProductTrial = frappe.qb.DocType("Product Trial") + ProductTrialApp = frappe.qb.DocType("Product Trial App") + product = ( + frappe.qb.from_(ProductTrial) + .join(ProductTrialApp) + .on(ProductTrialApp.parent == ProductTrial.name) + .select(ProductTrial.name) + .where(ProductTrial.published == 1) + .distinct() + .run(as_dict=True, pluck="app") + ) + team = frappe.local.team() + site = get_active_site_of_product_trial(product[0], team.name) + + account_request = frappe.new_doc( + "Account Request", + email=team.user, + team=team.name, + ).insert(ignore_permissions=True) + + if site: + site_request = frappe.get_doc( + "Product Trial Request", {"product_trial": product, "team": team, "site": site} + ) + else: + # check if account request is valid + # is_valid_account_request = frappe.get_value("Account Request", account_request, "email") == team.user + # create a new one + site_request = frappe.new_doc( + "Product Trial Request", + product_trial=product[0], + team=team.name, + account_request=account_request.name, + ).insert(ignore_permissions=True) + + return site_request + + def site_should_be_created_on_public_bench(apps: list[dict]) -> bool: """Check if site should be created on public bench""" @@ -317,7 +384,7 @@ def create_site_for_app( subdomain: str, apps: list[dict], cluster: str, - site_plan: str, + site_plan: str = "$10", group: str | None = None, trial: bool = False, ): @@ -327,6 +394,11 @@ def create_site_for_app( "Frappe Version", {"status": "Stable"}, "name", order_by="number desc" ) + if site_should_be_created_on_saas_bench(apps): + return create_site_on_saas_bench( + subdomain, apps, cluster, site_plan, latest_stable_version, group, trial + ) + if site_should_be_created_on_public_bench(apps): return create_site_on_public_bench( subdomain, apps, cluster, site_plan, latest_stable_version, group, trial @@ -749,8 +821,28 @@ def get_marketplace_apps_for_onboarding() -> list[dict]: filters={"show_for_site_creation": True, "status": "Published"}, ) total_installs_by_app = get_total_installs_by_app() + + ProductTrial = frappe.qb.DocType("Product Trial") + ProductTrialApp = frappe.qb.DocType("Product Trial App") + + product_trials = ( + frappe.qb.from_(ProductTrial) + .join(ProductTrialApp) + .on(ProductTrial.name == ProductTrialApp.parent) + .select( + ProductTrial.name, + ProductTrialApp.app, + ) + .where(ProductTrial.published == 1) + .distinct() + .run(as_dict=True) + ) + for app in apps: app["total_installs"] = total_installs_by_app.get(app["name"], 0) + app_with_product_id = find(product_trials, lambda x: x.app == app["name"]) + app["product_id"] = app_with_product_id.name if app_with_product_id else None + # sort by total installs apps = sorted(apps, key=lambda x: x["total_installs"], reverse=True) return apps # noqa: RET504 diff --git a/press/press/doctype/site/site.json b/press/press/doctype/site/site.json index 4026cf299c..d16a88cda0 100644 --- a/press/press/doctype/site/site.json +++ b/press/press/doctype/site/site.json @@ -80,6 +80,7 @@ "column_break_63", "hybrid_saas_pool", "saas_communication_secret", + "site_label", "backups_section", "backup_time", "column_break_zgig", @@ -600,6 +601,12 @@ "fieldname": "database_access_connection_limit", "fieldtype": "Int", "label": "Database Access Connection Limit" + }, + { + "description": "Set for sites created through SaaS flow", + "fieldname": "site_label", + "fieldtype": "Data", + "label": "Site Label" } ], "links": [ @@ -674,7 +681,7 @@ "link_fieldname": "site" } ], - "modified": "2024-11-28 15:40:02.431631", + "modified": "2024-12-18 14:20:04.696835", "modified_by": "Administrator", "module": "Press", "name": "Site", diff --git a/press/press/doctype/site/site.py b/press/press/doctype/site/site.py index 39fd53254d..b5d4b753f4 100644 --- a/press/press/doctype/site/site.py +++ b/press/press/doctype/site/site.py @@ -151,6 +151,7 @@ class Site(Document, TagHelpers): setup_wizard_complete: DF.Check setup_wizard_status_check_next_retry_on: DF.Datetime | None setup_wizard_status_check_retries: DF.Int + site_label: DF.Data | None skip_auto_updates: DF.Check skip_failing_patches: DF.Check skip_scheduled_backups: DF.Check @@ -196,6 +197,7 @@ class Site(Document, TagHelpers): "host_name", "skip_auto_updates", "additional_system_user_created", + "site_label", ) @staticmethod diff --git a/press/press/doctype/team/team.py b/press/press/doctype/team/team.py index 546d9cb06d..13617f7779 100644 --- a/press/press/doctype/team/team.py +++ b/press/press/doctype/team/team.py @@ -1046,13 +1046,14 @@ def get_route_on_login(self): return "/sites" if self.is_saas_user: - pending_site_request = self.get_pending_saas_site_request() - if pending_site_request: - product_trial = pending_site_request.product_trial - else: - product_trial = frappe.db.get_value("Account Request", self.account_request, "product_trial") - if product_trial: - return f"/app-trial/setup/{product_trial}" + return "/start-center" + # pending_site_request = self.get_pending_saas_site_request() + # if pending_site_request: + # product_trial = pending_site_request.product_trial + # else: + # product_trial = frappe.db.get_value("Account Request", self.account_request, "product_trial") + # if product_trial: + # return f"/app-trial/setup/{product_trial}" return "/welcome" @@ -1094,7 +1095,9 @@ def suspend_sites(self, reason=None): def get_sites_to_suspend(self): plan = frappe.qb.DocType("Site Plan") query = ( - frappe.qb.from_(plan).select(plan.name).where((plan.enabled == 1) & ((plan.is_frappe_plan == 1) | (plan.is_trial_plan == 1))) + frappe.qb.from_(plan) + .select(plan.name) + .where((plan.enabled == 1) & ((plan.is_frappe_plan == 1) | (plan.is_trial_plan == 1))) ).run(as_dict=True) frappe_plans = [d.name for d in query] diff --git a/press/saas/api/site.py b/press/saas/api/site.py index 99ee337cfb..12abe37c83 100644 --- a/press/saas/api/site.py +++ b/press/saas/api/site.py @@ -12,8 +12,8 @@ def info(): site = frappe.get_value("Site", frappe.local.site_name, ["plan", "trial_end_date"], as_dict=True) return { "name": frappe.local.site_name, - "trial_end_date": frappe.get_value("Site", frappe.local.site_name, "trial_end_date"), - "plan": frappe.get_doc("Site Plan", site.plan), + "trial_end_date": site.trial_end_date, + "plan": frappe.get_doc("Site Plan", site.plan) if site.plan else None, } diff --git a/press/saas/doctype/product_trial/product_trial.py b/press/saas/doctype/product_trial/product_trial.py index 2bb72292e8..02231c2204 100644 --- a/press/saas/doctype/product_trial/product_trial.py +++ b/press/saas/doctype/product_trial/product_trial.py @@ -109,7 +109,6 @@ def setup_trial_site(self, team, plan, cluster=None, account_request=None): from press.press.doctype.site.site import get_plan_config standby_site = self.get_standby_site(cluster) - team_record = frappe.get_doc("Team", team) trial_end_date = frappe.utils.add_days(None, self.trial_days or 14) site = None agent_job_name = None @@ -123,7 +122,7 @@ def setup_trial_site(self, team, plan, cluster=None, account_request=None): if standby_site: site = frappe.get_doc("Site", standby_site) site.is_standby = False - site.team = team_record.name + site.team = team site.trial_end_date = trial_end_date site.account_request = account_request site._update_configuration(apps_site_config, save=False) @@ -139,6 +138,7 @@ def setup_trial_site(self, team, plan, cluster=None, account_request=None): is_frappe_app_present = any(d["app"] == "frappe" for d in apps) if not is_frappe_app_present: apps.insert(0, {"app": "frappe"}) + user = frappe.get_doc("User", current_user) site = frappe.get_doc( doctype="Site", subdomain=self.get_unique_site_name(), @@ -152,6 +152,7 @@ def setup_trial_site(self, team, plan, cluster=None, account_request=None): team=team, apps=apps, trial_end_date=trial_end_date, + site_label=f"{user.first_name or user.full_name}'s {self.title} site", ) site._update_configuration(apps_site_config, save=False) site._update_configuration(get_plan_config(plan), save=False) diff --git a/press/saas/doctype/product_trial_request/product_trial_request.py b/press/saas/doctype/product_trial_request/product_trial_request.py index 0e9e633332..190316ec01 100644 --- a/press/saas/doctype/product_trial_request/product_trial_request.py +++ b/press/saas/doctype/product_trial_request/product_trial_request.py @@ -200,6 +200,9 @@ def validate_signup_fields(self): def create_site(self, cluster: str | None = None, signup_values: dict | None = None): if not signup_values: signup_values = {} + if not cluster: + cluster = get_default_cluster() + product = frappe.get_doc("Product Trial", self.product_trial) for field in product.signup_fields: if field.fieldtype == "Password" and field.fieldname in signup_values: diff --git a/press/www/saas/billing.js b/press/www/saas/billing.js index 11b3e82401..58d63f5ef5 100644 --- a/press/www/saas/billing.js +++ b/press/www/saas/billing.js @@ -1,8 +1,8 @@ -const frappe_cloud_base_endpoint = 'https://frappecloud.com'; +const frappe_cloud_base_endpoint = 'https://suhaildev.tanmoysrt.xyz'; function calculate_trial_end_days() { // try to check for trial_end_date in frappe.boot.subscription_conf - if (frappe.boot.subscription_conf.trial_end_date) { + if (frappe.boot?.subscription_conf?.trial_end_date) { const trial_end_date = new Date( frappe.boot.subscription_conf.trial_end_date, ); @@ -83,7 +83,7 @@ $(document).ready(function () { if (frappe.boot.setup_complete === 1) { if ( !frappe.is_mobile() && - frappe.boot.subscription_conf.status !== 'Subscribed' && + frappe.boot?.subscription_conf?.status !== 'Subscribed' && trial_end_days > 0 ) { $('.layout-main-section').before($floatingBar); @@ -115,7 +115,7 @@ function showBanner() { $(d.body).html(`