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) {