Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/lib/stores/billing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
</script>

<CardGrid>
Expand All @@ -22,16 +32,32 @@
compliance with trust service criteria such as security, availability, processing
integrity, confidentiality, and privacy.
</p>
<Button
secondary
external
class="u-margin-block-start-16"
on:click={() => (show = true)}
event="request_soc-2">
<span class="text">Request SOC-2</span>
</Button>
{#if supportsSoc2}
<Button
secondary
external
class="u-margin-block-start-16"
on:click={() => (show = true)}
event="request_soc-2">
<span class="text">Request SOC-2</span>
</Button>
{:else}
<p class="text u-margin-block-start-8">
SOC-2 reports are only available on an Enterprise contract. Contact our sales
team to discuss upgrading your organization.
</p>
<Button
secondary
external
class="u-margin-block-start-16"
href="https://appwrite.io/contact-us/enterprise">
<span class="text">Contact sales</span>
</Button>
{/if}
</Box>
</svelte:fragment>
</CardGrid>

<Soc2Modal {locale} {countryList} bind:show />
{#if supportsSoc2}
<Soc2Modal {locale} {countryList} bind:show />
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>(null);
const employeesOptions = [
{
value: '1-5',
label: '1-5'
Expand All @@ -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<string>(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) {
Expand Down