diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index d2b42db269..f413955175 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -134,6 +134,20 @@ export function isStarterPlan( return planHasGroup(billingPlan, BillingPlanGroup.Starter); } +export function planSupportsSoc2( + billingPlanOrId: string | Models.BillingPlan | null | undefined +): boolean { + const billingPlan = makeBillingPlan(billingPlanOrId); + if (!billingPlan) { + return false; + } + + // no SOC-2 entitlement exists on the plan model, so key off a real API field + // instead of guessing: SOC-2 is only offered on negotiated/custom contracts, + // never on a self-service plan (including self-service Scale). + return billingPlan.selfService === false; +} + export function canUpgrade( billingPlanOrId: string | Models.BillingPlan | null | undefined ): boolean { diff --git a/src/routes/(console)/organization-[organization]/settings/Soc2.svelte b/src/routes/(console)/organization-[organization]/settings/Soc2.svelte index 167a802613..7571515c6f 100644 --- a/src/routes/(console)/organization-[organization]/settings/Soc2.svelte +++ b/src/routes/(console)/organization-[organization]/settings/Soc2.svelte @@ -2,11 +2,21 @@ import { Box, CardGrid } from '$lib/components'; import { Button } from '$lib/elements/forms'; import Soc2Modal from './Soc2Modal.svelte'; + import { planSupportsSoc2 } from '$lib/stores/billing'; + import { currentPlan } from '$lib/stores/organization'; import type { Models } from '@appwrite.io/console'; - let show = false; - export let locale: Models.Locale; - export let countryList: Models.CountryList; + let { + locale, + countryList + }: { + locale: Models.Locale; + countryList: Models.CountryList; + } = $props(); + + let show = $state(false); + + const supportsSoc2 = $derived(planSupportsSoc2($currentPlan)); @@ -22,16 +32,32 @@ compliance with trust service criteria such as security, availability, processing integrity, confidentiality, and privacy.

- + {#if supportsSoc2} + + {:else} +

+ SOC-2 reports are only available on an Enterprise contract. Contact our sales + team to discuss upgrading your organization. +

+ + {/if}
- +{#if supportsSoc2} + +{/if} diff --git a/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte b/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte index c4b3b645d1..3a311861b4 100644 --- a/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte +++ b/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte @@ -7,16 +7,22 @@ import { organization } from '$lib/stores/organization'; import { user } from '$lib/stores/user'; import { VARS } from '$lib/system'; - import { onMount } from 'svelte'; + import { untrack } from 'svelte'; import type { Models } from '@appwrite.io/console'; - export let show = false; - export let locale: Models.Locale; - export let countryList: Models.CountryList; + let { + show = $bindable(false), + locale, + countryList + }: { + show?: boolean; + locale: Models.Locale; + countryList: Models.CountryList; + } = $props(); - let email = ''; - let employees: string = null; - let employeesOptions = [ + let email = $state($user?.email ?? ''); + let employees = $state(null); + const employeesOptions = [ { value: '1-5', label: '1-5' @@ -35,50 +41,49 @@ } ]; - let country = ''; - let countryOptions = []; - - let role = ''; - let website = ''; - - let error: string; - - onMount(async () => { - if (locale.countryCode) { - country = locale.countryCode; - } - countryOptions = countryList.countries.map((country) => { + let country = $state(untrack(() => locale?.countryCode ?? '')); + const countryOptions = $derived( + (countryList?.countries ?? []).map((country) => { return { value: country.code, label: country.name }; - }); - email = $user.email; - }); + }) + ); + + let role = $state(''); + let website = $state(''); + + let error = $state(null); async function handleSubmit() { + const formData = new FormData(); + formData.append('subject', 'SOC-2 Request'); + formData.append('email', email); + formData.append('firstName', ($user?.name ?? '').slice(0, 40)); + formData.append( + 'message', + `SOC-2 request for ${$organization?.name ?? ''} (${$organization?.$id ?? ''})` + ); + formData.append('tags[]', 'cloud'); + formData.append( + 'metaFields', + JSON.stringify({ + category: 'SOC-2', + userName: $user?.name ?? '', + orgId: $organization?.$id ?? '', + userId: $user?.$id ?? '', + billingPlan: $organization?.billingPlanId ?? '', + employees: employees, + country: country, + role: role, + website: website + }) + ); + const response = await fetch(`${VARS.GROWTH_ENDPOINT}/support`, { method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - subject: 'SOC-2 Request', - email: email, - firstName: ($user?.name ?? '').slice(0, 40), - message: `SOC-2 request for ${$organization?.name ?? ''} (${$organization?.$id ?? ''})`, - tags: ['cloud'], - metaFields: { - category: 'SOC-2', - userName: $user?.name ?? '', - orgId: $organization?.$id ?? '', - userId: $user?.$id ?? '', - employees: employees, - country: country, - role: role, - website: website - } - }) + body: formData }); trackEvent(Submit.RequestSoc2); if (response.status !== 200) {