Merge branch 'main' into feat-pink-v2

This commit is contained in:
Darshan
2025-01-09 17:27:00 +05:30
committed by GitHub
19 changed files with 219 additions and 27 deletions
+1 -2
View File
@@ -8,8 +8,7 @@ Sentry.init({
dsn: 'https://c7ce178bdedd486480317b72f282fd39@o1063647.ingest.us.sentry.io/4504158071422976',
tracesSampleRate: 1,
replaysSessionSampleRate: 0,
replaysOnErrorSampleRate: 1,
integrations: [Sentry.replayIntegration()]
replaysOnErrorSampleRate: 0
});
export const handleError: HandleClientError = Sentry.handleErrorWithSentry(
+2 -1
View File
@@ -17,6 +17,7 @@
export let options: {
value: string | boolean | number | null;
label: string;
disabled?: boolean;
}[];
export let isMultiple = false;
export let fullWidth = false;
@@ -64,4 +65,4 @@
on:invalid={handleInvalid}
on:input
on:change
bind:value />
bind:value />
+13
View File
@@ -0,0 +1,13 @@
import { goto } from '$app/navigation';
import { base } from '$app/paths';
export function checkPricingRefAndRedirect(searchParams: URLSearchParams, shouldRegister = false) {
if (searchParams.has('type')) {
const paramType = searchParams.get('type');
if (paramType === 'createPro') {
shouldRegister
? goto(`${base}/register?type=createPro`)
: goto(`${base}/create-organization?type=createPro`);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
import { page } from '$app/stores';
import type { Models } from '@appwrite.io/console';
import { derived } from 'svelte/store';
export const specificationsList = derived(
page,
async ($page) => (await $page.data.specificationsList) as Models.SpecificationList
);
@@ -0,0 +1,24 @@
<script lang="ts">
import { Button } from '$lib/elements/forms';
import { upgradeURL } from '$lib/stores/billing';
</script>
<div class="u-flex-vertical u-gap-16">
<p>Additional CPU and RAM are available for Pro and Scale teams.</p>
<div class="u-flex u-gap-8 u-cross-center">
<a
class="u-bold u-block"
target="_blank"
rel="noopener noreferrer"
href="https://appwrite.io/docs/advanced/platform/gb-hours">Learn more</a>
<Button href={$upgradeURL} secondary fullWidthMobile event={'specifications_cta_upgrade'}>
<span class="text">Upgrade Plan</span>
</Button>
</div>
</div>
<style>
a {
flex-grow: 1;
}
</style>
+2 -1
View File
@@ -19,7 +19,8 @@
repositoryBehaviour: 'new',
repositoryName: template.id,
repositoryPrivate: true,
repositoryId: null
repositoryId: null,
specification: null
});
wizard.start(CreateTemplate);
}
+12 -1
View File
@@ -37,7 +37,18 @@
undefined,
undefined,
$createFunction.entrypoint,
$createFunction.commands || undefined
$createFunction.commands || undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
$createFunction.specification
);
await sdk.forProject.functions.createDeployment(
response.$id,
@@ -58,7 +58,8 @@
$template.providerRepositoryId || undefined,
$template.providerOwner || undefined,
runtimeDetail.providerRootDirectory || undefined,
$template.providerVersion || undefined
$template.providerVersion || undefined,
$templateConfig.specification || undefined
);
if ($templateConfig.variables) {
@@ -78,7 +79,8 @@
customId: !!response.$id,
runtime: response.runtime,
deployment_type: $templateConfig.repositoryBehaviour,
scopes: $templateConfig.scopes
scopes: $templateConfig.scopes,
specification: $templateConfig.specification
});
resetState();
} catch (error) {
@@ -6,16 +6,30 @@
import { onMount } from 'svelte';
import { createFunction } from '../store';
import { runtimesList } from '$lib/stores/runtimes';
import { specificationsList } from '$lib/stores/specifications';
import { isCloud } from '$lib/system';
import { organization } from '$lib/stores/organization';
import { BillingPlan } from '$lib/constants';
import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
let showCustomId = false;
let options = [];
let specificationOptions = [];
onMount(async () => {
options = (await $runtimesList).runtimes.map((runtime) => ({
label: `${runtime.name} - ${runtime.version}`,
value: runtime.$id
}));
specificationOptions = (await $specificationsList).specifications.map((size) => ({
label:
`${size.cpus} CPU, ${size.memory} MB RAM` +
(!size.enabled ? ` (Upgrade to use this)` : ''),
value: size.slug,
disabled: !size.enabled
}));
});
</script>
@@ -39,6 +53,18 @@
{options}
required />
<InputSelect
label="CPU and memory"
id="specification"
placeholder="Select specification"
required
disabled={specificationOptions.length < 1}
options={specificationOptions}
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
? SpecificationsTooltip
: null}
bind:value={$createFunction.specification} />
{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}>
@@ -4,7 +4,12 @@
import { FormList, InputSelect, InputText } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { runtimesList } from '$lib/stores/runtimes';
import { specificationsList } from '$lib/stores/specifications';
import { BillingPlan } from '$lib/constants';
import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
import { template, templateConfig } from '../store';
import { organization } from '$lib/stores/organization';
import { isCloud } from '$lib/system';
let showCustomId = false;
@@ -12,6 +17,10 @@
if (!$templateConfig.runtime) {
throw new Error('Please select a runtime.');
}
if (!$templateConfig.specification) {
throw new Error('Please select a specification.');
}
}
async function loadRuntimes() {
@@ -27,6 +36,22 @@
return options;
}
async function loadSpecifications() {
const specificationOptions = (await $specificationsList).specifications.map((size) => ({
label:
`${size.cpus} CPU, ${size.memory} MB RAM` +
(!size.enabled ? ` (Upgrade to use this)` : ''),
value: size.slug,
disabled: !size.enabled
}));
if (!$templateConfig.specification && specificationOptions.length > 0) {
$templateConfig.specification = specificationOptions[0].value;
}
return specificationOptions;
}
</script>
<WizardStep {beforeSubmit}>
@@ -61,6 +86,28 @@
{options}
bind:value={$templateConfig.runtime} />
{/await}
{#await loadSpecifications()}
<InputSelect
label="CPU and memory"
id="specification"
placeholder="Loading specifications..."
required
disabled
options={[]}
value={null} />
{:then specificationOptions}
<InputSelect
label="CPU and memory"
id="specification"
placeholder="Select specification"
required
disabled={specificationOptions.length < 1}
options={specificationOptions}
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
? SpecificationsTooltip
: null}
bind:value={$templateConfig.specification} />
{/await}
</FormList>
<FormList class="u-margin-block-start-24">
+1
View File
@@ -15,6 +15,7 @@ export const templateConfig = writable<{
repositoryId: string;
execute?: boolean;
scopes?: string[];
specification?: string;
}>();
export const repository = writable<Models.ProviderRepository>();
export const installation = writable<Models.Installation>();
@@ -79,7 +79,11 @@
billingPlan = plan as BillingPlan;
}
}
if (anyOrgFree) {
if (
anyOrgFree ||
($page.url.searchParams.has('type') &&
$page.url.searchParams.get('type') === 'createPro')
) {
billingPlan = BillingPlan.PRO;
}
});
+2 -6
View File
@@ -17,6 +17,7 @@
import { tierToPlan, type Tier, plansInfo } from '$lib/stores/billing';
import { formatCurrency } from '$lib/helpers/numbers';
import { base } from '$app/paths';
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
let name: string;
let id: string;
@@ -42,12 +43,7 @@
onMount(() => {
if (isCloud) {
if ($page.url.searchParams.has('type')) {
const paramType = $page.url.searchParams.get('type');
if (paramType === 'createPro') {
goto(`${base}/create-organization`);
}
}
checkPricingRefAndRedirect($page.url.searchParams);
}
});
@@ -32,6 +32,7 @@
import { onMount } from 'svelte';
import { organization } from '$lib/stores/organization';
import { canWriteProjects } from '$lib/stores/roles';
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
export let data;
@@ -123,12 +124,7 @@
onMount(async () => {
if (isCloud) {
regions = await sdk.forConsole.billing.listRegions();
if ($page.url.searchParams.has('type')) {
const paramType = $page.url.searchParams.get('type');
if (paramType === 'createPro') {
goto(`${base}/create-organization`);
}
}
checkPricingRefAndRedirect($page.url.searchParams);
}
});
@@ -8,10 +8,11 @@ import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async ({ depends }) => {
depends(Dependencies.FUNCTION_INSTALLATIONS);
const [runtimesList, installations, templatesList] = await Promise.all([
const [runtimesList, installations, templatesList, specificationsList] = await Promise.all([
sdk.forProject.functions.listRuntimes(),
sdk.forProject.vcs.listInstallations([Query.limit(100)]),
sdk.forProject.functions.listTemplates(undefined, undefined, 100)
sdk.forProject.functions.listTemplates(undefined, undefined, 100),
sdk.forProject.functions.listSpecifications()
]);
return {
@@ -19,6 +20,7 @@ export const load: LayoutLoad = async ({ depends }) => {
breadcrumbs: Breadcrumbs,
runtimesList,
installations,
templatesList
templatesList,
specificationsList
};
};
@@ -3,30 +3,46 @@
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { BillingPlan, Dependencies } from '$lib/constants';
import { Button, Form, FormList } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { func } from '../store';
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
import { specificationsList } from '$lib/stores/specifications';
import { runtimesList } from '$lib/stores/runtimes';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { organization } from '$lib/stores/organization';
import SpecificationsTooltip from '$lib/wizards/functions/components/specificationsTooltip.svelte';
const functionId = $page.params.function;
let runtime: string = null;
let specification: string = null;
let options = [];
let specificationOptions = [];
onMount(async () => {
runtime ??= $func.runtime;
specification ??= $func.specification;
let runtimes = await $runtimesList;
let allowedSpecifications = (await $specificationsList).specifications;
options = runtimes.runtimes.map((runtime) => ({
label: `${runtime.name} - ${runtime.version}`,
value: runtime.$id
}));
specificationOptions = allowedSpecifications.map((size) => ({
label:
`${size.cpus} CPU, ${size.memory} MB RAM` +
(!size.enabled ? ` (Upgrade to use this)` : ''),
value: size.slug,
disabled: !size.enabled
}));
});
async function updateRuntime() {
@@ -51,11 +67,12 @@
$func.providerRepositoryId || undefined,
$func.providerBranch || undefined,
$func.providerSilentMode || undefined,
$func.providerRootDirectory || undefined
$func.providerRootDirectory || undefined,
specification
);
await invalidate(Dependencies.FUNCTION);
addNotification({
message: 'Runtime has been updated',
message: 'Runtime settings have been updated',
type: 'success'
});
trackEvent(Submit.FunctionUpdateName);
@@ -67,6 +84,8 @@
trackError(error, Submit.FunctionUpdateName);
}
}
$: isUpdateButtonEnabled = runtime !== $func?.runtime || specification !== $func?.specification;
</script>
<Form onSubmit={updateRuntime}>
@@ -83,11 +102,22 @@
{options}
required
hideRequired />
<InputSelect
label="CPU and memory"
id="size"
placeholder="Select runtime specification"
bind:value={specification}
options={specificationOptions}
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
? SpecificationsTooltip
: null}
required
hideRequired />
</FormList>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={runtime === $func.runtime} submit>Update</Button>
<Button disabled={!isUpdateButtonEnabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -10,6 +10,8 @@
export let data: PageData;
$: total = data.executionsTotal;
$: count = data.executions;
$: gbHoursTotal = data.executionsMbSecondsTotal / 1000 / 3600;
$: mbSecondsCount = data.executionsMbSeconds;
</script>
<Container>
@@ -47,4 +49,24 @@
]} />
</Card>
{/if}
{#if mbSecondsCount}
<Card>
<Heading tag="h6" size="6">{formatNumberWithCommas(gbHoursTotal)}</Heading>
<p>GB Hours</p>
<div class="u-margin-block-start-16" />
<BarChart
series={[
{
name: 'Count of gbHours over time',
data: [
...mbSecondsCount.map((e) => [
e.date,
Math.ceil((e.value / 1000 / 3600) * 1000) / 1000
])
]
}
]} />
</Card>
{/if}
</Container>
@@ -20,6 +20,7 @@
import { isCloud } from '$lib/system';
import { page } from '$app/stores';
import { redirectTo } from '$routes/store';
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
export let data;
@@ -52,6 +53,8 @@
$page.url.searchParams.delete('redirect');
if (redirect) {
await goto(`${redirect}${$page.url.search}`);
} else if (isCloud) {
checkPricingRefAndRedirect($page.url.searchParams);
} else {
await goto(`${base}/${$page.url.search ?? ''}`);
}
+6
View File
@@ -9,6 +9,8 @@ import { redirectTo } from './store';
import { base } from '$app/paths';
import type { Account } from '$lib/stores/user';
import type { AppwriteException } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
export const ssr = false;
@@ -46,6 +48,10 @@ export const load: LayoutLoad = async ({ depends, url, route }) => {
}
if (!isPublicRoute) {
if (isCloud) {
checkPricingRefAndRedirect(url.searchParams, true);
}
redirect(303, withParams(`${base}/login`, url.searchParams));
}
};