feat: org creation modal and fixes

This commit is contained in:
Arman
2023-06-18 17:21:29 +02:00
parent b526675a07
commit 7375a59dbe
8 changed files with 196 additions and 104 deletions
+15 -1
View File
@@ -8,6 +8,7 @@
export let value = false;
export let required = false;
export let disabled = false;
export let tooltip: string = null;
let element: HTMLInputElement;
let error: string;
@@ -39,7 +40,20 @@
on:invalid={handleInvalid} />
<div class="choice-item-content">
<div class:u-hide={!showLabel} class="choice-item-title">{label}</div>
<div class:u-hide={!showLabel} class="choice-item-title">
{label}
{#if tooltip}
<button class="tooltip" aria-label="variables info">
<span class="icon-info" aria-hidden="true" />
<span class="tooltip-popup" role="tooltip">
<p class="text">
{tooltip}
</p>
</span>
</button>
{/if}
</div>
{#if $$slots}
<p class="choice-item-paragraph"><slot /></p>
{/if}
+53
View File
@@ -60,3 +60,56 @@ export class Billing {
return await this.client.call('patch', uri, { 'content-type': 'application/json' }, params);
}
}
export const apperanceLight = {
variables: {
colorPrimary: '#606a7b',
colorText: '#373B4D',
colorBackground: '#FFFFFF',
color: '#606a7b',
colorDanger: '#df1b41',
fontFamily: 'Inter, arial, sans-serif',
borderRadius: '4px'
},
rules: {
'.Input:hover': {
border: 'solid 1px #C4C6D7',
boxShadow: 'none'
},
'.Input:focus': {
border: 'solid 1px #C4C6D7',
boxShadow: 'none'
},
'.Input::placeholder': {
color: '#C4C6D7'
},
'.Input--invalid': {
border: 'solid 1px var(--colorDanger)',
boxShadow: 'none'
}
}
};
export const apperanceDark = {
variables: {
colorPrimary: '#606a7b',
colorText: '#C5C7D8',
colorBackground: '#161622',
colorDanger: '#FF453A',
fontFamily: 'Inter, arial, sans-serif',
borderRadius: '4px'
},
rules: {
'.Input:hover': {
border: 'solid 1px #4F5769',
boxShadow: 'none'
},
'.Input:focus': {
border: 'solid 1px #4F5769',
boxShadow: 'none'
},
'.Input--invalid': {
border: 'solid 1px var(--colorDanger)',
boxShadow: 'none'
}
}
};
@@ -47,20 +47,32 @@
$createOrganization = {
id: null,
name: null,
tier: Tier['PREMIUM'],
region: 'eu-central-1'
tier: Tier['PREMIUM']
};
});
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
label: 'Project details',
label: 'Organization details',
component: Step1
});
stepsComponents.set(2, {
label: 'Select region',
label: 'Payment details',
component: Step2
});
stepsComponents.set(3, {
label: 'Invite collaborators',
component: Step2
});
stepsComponents.set(4, {
label: 'Review & confirm',
component: Step2
});
</script>
<Wizard title="Create a Project" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
<Wizard
title="Create organization "
steps={stepsComponents}
on:finish={create}
on:exit={onFinish} />
@@ -15,6 +15,7 @@
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { app } from '$lib/stores/app';
import { apperanceDark, apperanceLight } from '$lib/stores/billing';
export let show = false;
@@ -26,59 +27,6 @@
let clientSecret: string;
let paymentMethod: PaymentMethod;
const apperanceLight = {
variables: {
colorPrimary: '#606a7b',
colorText: '#373B4D',
colorBackground: '#FFFFFF',
color: '#606a7b',
colorDanger: '#df1b41',
fontFamily: 'Inter, arial, sans-serif',
borderRadius: '4px'
},
rules: {
'.Input:hover': {
border: 'solid 1px #C4C6D7',
boxShadow: 'none'
},
'.Input:focus': {
border: 'solid 1px #C4C6D7',
boxShadow: 'none'
},
'.Input::placeholder': {
color: '#C4C6D7'
},
'.Input--invalid': {
border: 'solid 1px var(--colorDanger)',
boxShadow: 'none'
}
}
};
const apperanceDark = {
variables: {
colorPrimary: '#606a7b',
colorText: '#C5C7D8',
colorBackground: '#161622',
colorDanger: '#FF453A',
fontFamily: 'Inter, arial, sans-serif',
borderRadius: '4px'
},
rules: {
'.Input:hover': {
border: 'solid 1px #4F5769',
boxShadow: 'none'
},
'.Input:focus': {
border: 'solid 1px #4F5769',
boxShadow: 'none'
},
'.Input--invalid': {
border: 'solid 1px var(--colorDanger)',
boxShadow: 'none'
}
}
};
onMount(async () => {
stripe = await loadStripe(publicKey);
try {
@@ -151,7 +99,7 @@
<!-- Elements will create form elements here -->
</div>
<p class="text u-small">
You wont be charged immediately. You will be charged for your plan on the first day of
You won't be charged immediately. You will be charged for your plan on the first day of
each month using the payment method you've specified above.
</p>
</FormList>
@@ -12,7 +12,7 @@
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { ID } from '@appwrite.io/console';
import { Tier } from '$lib/system';
import { createOrganization } from '../wizard/store';
import { createProject } from './wizard/store';
const teamId = $page.params.organization;
const dispatch = createEventDispatcher();
@@ -24,19 +24,19 @@
async function create() {
try {
const project = await sdk.forConsole.projects.create(
$createOrganization?.id ?? ID.unique(),
$createOrganization.name,
$createProject?.id ?? ID.unique(),
$createProject.name,
teamId,
'default'
);
dispatch('created', project);
trackEvent(Submit.ProjectCreate, {
customId: !!$createOrganization?.id,
customId: !!$createProject?.id,
teamId
});
addNotification({
type: 'success',
message: `${$createOrganization.name} has been created`
message: `${$createProject.name} has been created`
});
await goto(`/console/project-${project.$id}`);
} catch (e) {
@@ -49,7 +49,7 @@
}
onDestroy(() => {
$createOrganization = {
$createProject = {
id: null,
name: null,
tier: Tier['PREMIUM']
@@ -58,21 +58,13 @@
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
label: 'Organization details',
label: 'Project details',
component: Step1
});
stepsComponents.set(2, {
label: 'Payment details',
component: Step2
});
stepsComponents.set(2, {
label: 'Invite collaborators',
component: Step2
});
stepsComponents.set(2, {
label: 'Review & confirm',
label: 'Select region',
component: Step2
});
</script>
<Wizard title="Create organization" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
<Wizard title="Create a project" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
+36 -12
View File
@@ -3,9 +3,12 @@
import { Pill } from '$lib/elements';
import { InputText, FormList } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { organizationList } from '$lib/stores/organization';
import { createOrganization } from './store';
let showCustomId = false;
$: anyOrgFree = $organizationList.teams?.find((org) => org?.tier === 'free');
</script>
<WizardStep>
@@ -45,25 +48,46 @@
<ul
class="u-flex u-flex-vertical u-gap-16 u-margin-block-start-16"
style="--p-grid-item-size:16em; --p-grid-item-size-small-screens:16rem; --grid-gap: 1rem;">
<li>
<LabelCard name="plan" bind:group={$createOrganization.tier} value="free">
<svelte:fragment slot="title">Free - $0/month</svelte:fragment>
For personal, passion projects.
</LabelCard>
</li>
{#if !anyOrgFree}
<li>
<LabelCard name="plan" bind:group={$createOrganization.tier} value="free">
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">Free - $0/month</h4>
<p class="u-color-text-gray u-small">For personal, passion projects.</p>
</div>
</svelte:fragment>
</LabelCard>
</li>
{/if}
<li>
<LabelCard name="plan" bind:group={$createOrganization.tier} value="starter">
<svelte:fragment slot="title"
>Starter - $10/month per organization member</svelte:fragment>
For small organizations that want flexibility.
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">
Starter - $10/month per organization member
</h4>
<p class="u-color-text-gray u-small">
For small organizations that want flexibility.
</p>
</div>
<Pill>14 DAY FREE TRIAL</Pill>
</svelte:fragment>
</LabelCard>
</li>
<li>
<LabelCard name="plan" bind:group={$createOrganization.tier} value="premium">
<svelte:fragment slot="title">
Pro - $20/month per organization member + exta usage
<svelte:fragment slot="custom">
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
<h4 class="body-text-2 u-bold">
Pro - $20/month per organization member + exta usage
</h4>
<p class="u-color-text-gray u-small">
For organizations that need the ability scale easily.
</p>
</div>
<Pill>14 DAY FREE TRIAL</Pill>
</svelte:fragment>
For organizations that need the ability scale easily.
</LabelCard>
</li>
</ul>
+61 -14
View File
@@ -1,23 +1,70 @@
<script lang="ts">
import { RegionCard } from '$lib/components';
import { Pill } from '$lib/elements';
import { FormList, InputRadio, InputText } from '$lib/elements/forms';
import InputChoice from '$lib/elements/forms/inputChoice.svelte';
import { WizardStep } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { createOrganization } from './store';
let methods = [];
let name: string;
onMount(async () => {
methods = await sdk.forConsole.billing.listPaymentMethods('TEST');
if (methods.length) {
$createOrganization.payment = methods[0].id;
}
});
</script>
<WizardStep>
<svelte:fragment slot="title">Select region</svelte:fragment>
<svelte:fragment slot="subtitle">
Choose a deployment region for your project. This region cannot be changed.
</svelte:fragment>
<svelte:fragment slot="title">Payment details</svelte:fragment>
<svelte:fragment slot="subtitle">Add a payment method to your organization.</svelte:fragment>
<p class="text u-margin-block-start-16">
Edge Network is enabled on all regions. For more details, check out our <a
class="link"
href="http://#"
target="_blank"
rel="noopener noreferrer">Documentation</a
>.
</p>
<FormList>
<div class:boxes-wrapper={methods?.length}>
{#each methods as method}
<div class="box">test</div>
<InputRadio
id="payment-method"
label="Payment method"
value={method.id}
name={method.id}
group="payment" />
{/each}
<div class="box">
<InputRadio
id="payment-method"
label="Add new payment method"
value={null}
name="test"
group={$createOrganization.payment} />
{#if $createOrganization.payment === null}
<InputText
id="name"
label="Cardholder name"
placeholder="Cardholder name"
bind:value={name}
required
autofocus={true} />
<div id="payment-element">
<!-- Elements will create form elements here -->
</div>
{/if}
</div>
</div>
<InputChoice type="switchbox" id="budget" label="Enable budget cap" tooltip="lorem ipsum">
<p class="text">
Restrict your resource usage by setting a budget cap. <a
class="link"
href="http://#"
target="_blank"
rel="noopener noreferrer">Learn more about usage rates</a
>.
</p>
</InputChoice>
</FormList>
</WizardStep>
+3 -1
View File
@@ -5,8 +5,10 @@ export const createOrganization = writable<{
id?: string;
name: string;
tier: Tier;
payment: string;
}>({
id: null,
name: null,
tier: Tier['PREMIUM']
tier: Tier['PREMIUM'],
payment: null
});