Add wizard for creating provider

This commit is contained in:
Steven Nguyen
2024-01-02 15:47:09 +01:00
committed by Torsten Dittmann
parent 08d67f7318
commit f825b396fd
11 changed files with 1128 additions and 79 deletions
+4 -1
View File
@@ -287,5 +287,8 @@ export enum Submit {
SmsResetTemplate = 'submit_sms_reset_template',
SmsUpdateInviteTemplate = 'submit_sms_update_invite_template',
SmsUpdateLoginTemplate = 'submit_sms_update_login_template',
SmsUpdateVerificationTemplate = 'submit_sms_update_verification_template'
SmsUpdateVerificationTemplate = 'submit_sms_update_verification_template',
MessagingProviderCreate = 'submit_messaging_provider_create',
MessagingProviderDelete = 'submit_messaging_provider_delete',
MessagingProviderUpdate = 'submit_messaging_provider_update'
}
+10
View File
@@ -1,5 +1,7 @@
<script lang="ts">
import { tooltip } from '$lib/actions/tooltip';
import { app } from '$lib/stores/app';
import { base } from '$app/paths';
export let name: string;
export let group: string;
@@ -7,6 +9,7 @@
export let disabled = false;
export let padding = 1;
export let icon: string = null;
export let imageIcon: string = null;
export let fullHeight = true;
export let borderRadius: 'xsmall' | 'small' | 'medium' | 'large' = 'small';
export let backgroundColor: string = null;
@@ -58,5 +61,12 @@
<span class={`icon-${icon} u-margin-inline-start-auto`} aria-hidden="true" />
{/if}
{/if}
{#if imageIcon}
<img
class="u-margin-inline-start-auto"
style:--p-text-size="1.25rem"
src={`${base}/icons/${$app.themeInUse}/color/${imageIcon}.svg`}
alt={imageIcon} />
{/if}
</div>
</label>
@@ -21,25 +21,24 @@
Id,
ViewSelector
} from '$lib/components';
import Create from './create.svelte';
import { goto } from '$app/navigation';
import { Container } from '$lib/layout';
import { base } from '$app/paths';
import type { Models } from '@appwrite.io/console';
import type { PageData } from './$types';
import { showCreate, columns } from './store';
import { columns } from './store';
import { Pill } from '$lib/elements';
import Provider from '../provider.svelte';
import ProviderType from '../providerType.svelte';
import Filters from '$lib/components/filters/filters.svelte';
import CreateProviderDropdown from './createProviderDropdown.svelte';
export let data: PageData;
let showCreateDropdownMobile = false;
let showCreateDropdownDesktop = false;
let showCreateDropdownEmpty = false;
let selected: string[] = [];
const project = $page.params.project;
const topicCreated = async (event: CustomEvent<Models.Team<Record<string, unknown>>>) => {
await goto(`${base}/console/project-${project}/messaging/topics/topic-${event.detail.$id}`);
};
</script>
<Container>
@@ -47,10 +46,7 @@
<div class="u-flex u-main-space-between">
<Heading tag="h2" size="5">Providers</Heading>
<div class="is-only-mobile">
<Button on:click={() => ($showCreate = true)} event="create_provider">
<span class="icon-plus" aria-hidden="true" />
<span class="text">Create provider</span>
</Button>
<CreateProviderDropdown bind:showCreateDropdown={showCreateDropdownMobile} />
</div>
</div>
<!-- TODO: fix width of search input in mobile -->
@@ -63,10 +59,7 @@
hideView
allowNoColumns
showColsTextMobile />
<Button on:click={() => ($showCreate = true)} event="create_provider">
<span class="icon-plus" aria-hidden="true" />
<span class="text">Create provider</span>
</Button>
<CreateProviderDropdown bind:showCreateDropdown={showCreateDropdownDesktop} />
</div>
</SearchQuery>
<div class="u-flex u-gap-16 is-only-mobile u-margin-block-start-16">
@@ -147,7 +140,7 @@
limit={data.limit}
offset={data.offset}
total={data.providers.total} />
{:else if data.search}
{:else if data.search && data.search != 'empty'}
<EmptySearch>
<div class="u-text-center">
<b>Sorry, we couldn't find '{data.search}'</b>
@@ -159,11 +152,33 @@
</EmptySearch>
{:else}
<!-- TODO: Update docs links -->
<Empty
single
href="https://appwrite.io/docs/references/cloud/client-web/providers"
target="provider" />
<Empty single target="provider">
<div class="u-text-center">
<Heading size="7" tag="h2" trimmed={false}>
Create your first provider to get started.
</Heading>
<p class="body-text-2 u-bold u-margin-block-start-4">
Need a hand? Learn more in our documentation.
</p>
</div>
<div class="u-flex u-flex-wrap u-gap-16 u-main-center">
<Button
external
href="https://appwrite.io/docs/references/cloud/client-web/providers"
text
event="empty_documentation"
ariaLabel={`create provider`}>
Documentation
</Button>
<CreateProviderDropdown bind:showCreateDropdown={showCreateDropdownEmpty}>
<Button
secondary
on:click={() => (showCreateDropdownEmpty = !showCreateDropdownEmpty)}
event="create_provider">
<span class="text">Create provider</span>
</Button>
</CreateProviderDropdown>
</div>
</Empty>
{/if}
</Container>
<Create bind:showCreate={$showCreate} on:created={topicCreated} />
@@ -70,6 +70,6 @@ export const load = async ({ url, route }) => {
query,
page,
view,
providers: providers
providers
};
};
@@ -1,68 +1,274 @@
<script lang="ts">
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { Modal, CustomId } from '$lib/components';
import { Pill } from '$lib/elements';
import { InputText, Button, FormList } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { onDestroy } from 'svelte';
import { Wizard } from '$lib/layout';
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
import Provider from './wizard/provider.svelte';
import Configure from './wizard/configure.svelte';
import { sdk } from '$lib/stores/sdk';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { addNotification } from '$lib/stores/notifications';
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { project } from '../../store';
import { wizard } from '$lib/stores/wizard';
import { provider, providerParams } from './wizard/store';
import { ID } from '@appwrite.io/console';
import { createEventDispatcher } from 'svelte';
import { Providers } from '../provider.svelte';
export let showCreate = false;
const dispatch = createEventDispatcher();
let name: string, id: string, error: string;
let showCustomId = false;
const create = async () => {
async function create() {
try {
const team = await sdk.forProject.teams.create(id ?? ID.unique(), name);
name = '';
showCreate = false;
showCustomId = false;
let response = { $id: '', name: '' };
const providerId = $providerParams[$provider].providerId || ID.unique();
switch ($provider) {
case Providers.Twilio:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/twilio'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
accountSid: $providerParams[$provider].accountSid,
authToken: $providerParams[$provider].authToken,
from: $providerParams[$provider].from
}
);
break;
case Providers.Msg91:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/msg91'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
from: $providerParams[$provider].from,
senderId: $providerParams[$provider].senderId,
authKey: $providerParams[$provider].authKey
}
);
break;
case Providers.Telesign:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/telesign'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
from: $providerParams[$provider].from,
username: $providerParams[$provider].username,
password: $providerParams[$provider].password
}
);
break;
case Providers.Textmagic:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/textmagic'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
from: $providerParams[$provider].from,
username: $providerParams[$provider].username,
apiKey: $providerParams[$provider].apiKey
}
);
break;
case Providers.Vonage:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/vonage'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
from: $providerParams[$provider].from,
apiKey: $providerParams[$provider].apiKey,
apiSecret: $providerParams[$provider].apiSecret
}
);
break;
case Providers.Mailgun:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/mailgun'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
isEuRegion: $providerParams[$provider].isEuRegion,
from: $providerParams[$provider].from,
apiKey: $providerParams[$provider].apiKey,
domain: $providerParams[$provider].domain
}
);
break;
case Providers.Sendgrid:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/sendgrid'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
from: $providerParams[$provider].from,
apiKey: $providerParams[$provider].apiKey
}
);
break;
case Providers.FCM:
response = await sdk.forProject.client.call(
'POST',
new URL(sdk.forProject.client.config.endpoint + '/messaging/providers/fcm'),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
serverKey: $providerParams[$provider].serverKey
}
);
break;
case Providers.APNS:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/apns'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
authKey: $providerParams[$provider].authKey,
authKeyId: $providerParams[$provider].authKeyId,
teamId: $providerParams[$provider].teamId,
bundleId: $providerParams[$provider].bundleId
}
);
break;
case Providers.MQTT:
response = await sdk.forProject.client.call(
'POST',
new URL(
sdk.forProject.client.config.endpoint + '/messaging/providers/mqtt'
),
{
'X-Appwrite-Project': sdk.forProject.client.config.project,
'content-type': 'application/json',
'X-Appwrite-Mode': 'admin'
},
{
providerId: providerId,
name: $providerParams[$provider].name,
default: $providerParams[$provider].default,
enabled: $providerParams[$provider].enabled,
serverKey: $providerParams[$provider].serverKey
}
);
break;
}
wizard.hide();
addNotification({
type: 'success',
message: `${team.name} has been created`
message: `${response.name} has been created`
});
trackEvent(Submit.TeamCreate, {
customId: !!id
trackEvent(Submit.MessagingProviderCreate, {
provider: $provider
});
dispatch('created', team);
} catch (e) {
error = e.message;
trackError(e, Submit.TeamCreate);
await goto(
`${base}/console/project-${$project.$id}/messaging/providers/provider-${response.$id}`
);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.MessagingProviderCreate);
}
};
$: if (!showCreate) {
showCustomId = false;
error = null;
}
onDestroy(() => {
console.log('destroy');
});
const stepsComponents: WizardStepsType = new Map();
stepsComponents.set(1, {
label: 'Proivder',
component: Provider
});
stepsComponents.set(2, {
label: 'Configure',
component: Configure
});
</script>
<Modal title="Create team" {error} size="big" bind:show={showCreate} onSubmit={create}>
<FormList>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autofocus={true}
required
bind:value={name} />
{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}
><span class="icon-pencil" aria-hidden="true" />
<span class="text"> Team ID </span>
</Pill>
</div>
{:else}
<CustomId bind:show={showCustomId} name="Team" bind:id />
{/if}
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
<Button submit>Create</Button>
</svelte:fragment>
</Modal>
<Wizard title="Create provider" steps={stepsComponents} on:finish={create} />
@@ -0,0 +1,49 @@
<script lang="ts">
import { DropList, DropListItem } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { wizard } from '$lib/stores/wizard';
import { providers } from './store';
import Create from './create.svelte';
import { providerType, provider } from './wizard/store';
import { ProviderTypes } from '../providerType.svelte';
import { Providers } from '../provider.svelte';
export let showCreateDropdown = false;
const isValueOfStringEnum = <T extends Record<string, string>>(
enumType: T,
value: string
): value is T[keyof T] => Object.values<string>(enumType).includes(value);
</script>
<DropList bind:show={showCreateDropdown} scrollable>
<slot>
<Button on:click={() => (showCreateDropdown = !showCreateDropdown)} event="create_provider">
<span class="icon-plus" aria-hidden="true" />
<span class="text">Create provider</span>
</Button>
</slot>
<svelte:fragment slot="list">
{#each Object.entries(providers) as [type, option]}
<DropListItem
icon={option.icon}
on:click={() => {
if (
type !== ProviderTypes.Email &&
type !== ProviderTypes.Sms &&
type !== ProviderTypes.Push
)
return;
$providerType = type;
const p = Object.keys(providers[type].providers).shift();
if (p && isValueOfStringEnum(Providers, p)) {
$provider = p;
}
showCreateDropdown = false;
wizard.start(Create);
}}>
{option.name}
</DropListItem>
{/each}
</svelte:fragment>
</DropList>
@@ -1,5 +1,7 @@
import { writable } from 'svelte/store';
import type { Column } from '$lib/helpers/types';
import { Providers } from '../provider.svelte';
import { ProviderTypes } from '../providerType.svelte';
export let showCreate = writable(false);
@@ -10,3 +12,370 @@ export const columns = writable<Column[]>([
{ id: 'type', title: 'Type', type: 'string', show: true },
{ id: 'enabled', title: 'Status', type: 'boolean', show: true }
]);
type ProvidersMap = {
[key in ProviderTypes]: {
name: string;
text: string;
icon: string;
providers: {
[key in Providers]?: {
imageIcon: string;
title: string;
description: string;
configure: {
label: string;
name: string;
type: 'text' | 'phone' | 'email' | 'domain' | 'file' | 'switch';
placeholder?: string;
description?: string;
popover?: string[];
allowedFileExtensions?: string[];
}[];
};
};
};
};
export const providers: ProvidersMap = {
[ProviderTypes.Sms]: {
name: 'SMS',
text: 'SMS',
icon: 'annotation',
providers: {
[Providers.Twilio]: {
imageIcon: 'twilio',
title: 'Twilio',
description: '',
configure: [
{
label: 'Account SID',
name: 'accountSid',
type: 'text',
placeholder: 'Enter Account SID',
popover: [
'<b>How to get the Account SID?</b>',
'Head to <b>Twilio console -> Account info -> Account SID.</b>'
]
},
{
label: 'Auth token',
name: 'authToken',
type: 'text',
placeholder: 'Enter Auth token',
popover: [
'<b>How to get the Auth token?</b>',
'Head to <b>Twilio console -> Account info -> Auth Token.</b>'
]
},
{
label: 'Sender number',
name: 'from',
type: 'phone',
placeholder: 'Enter phone',
popover: [
'<b>How to get sender number?</b>',
'Head to <b>Twilio console -> Account info -> My Twilio phone number.</b>',
'If you have multiple Twilio phone numbers, you can select one as the default number.'
]
}
]
},
[Providers.Msg91]: {
imageIcon: 'msg91',
title: 'MSG91',
description: '',
configure: [
{
label: 'Auth key',
name: 'authKey',
type: 'text',
placeholder: 'Enter auth key',
popover: [
'<b>How to get the Auth key?</b>',
'Create an account in MSG91.',
'Click to open the <b>Username dropdown -> Authkey -> Verify your mobile number -> Create Authkey.</b>'
]
},
{
label: 'Sender ID',
name: 'senderId',
type: 'text',
placeholder: 'Enter sender ID',
popover: [
'<b>How to create a Sender ID?</b>',
'Head to <b>MSG91 dashboard -> SMS -> Sender ID -> Create sender ID.</b>'
]
},
{
label: 'Sender number',
name: 'from',
type: 'phone',
placeholder: 'Enter phone'
}
]
},
[Providers.Telesign]: {
imageIcon: 'telesign',
title: 'Telesign',
description: '',
configure: [
{
label: 'Username',
name: 'username',
type: 'text',
placeholder: 'Enter username'
},
{
label: 'Password',
name: 'password',
type: 'text',
placeholder: 'Enter password'
},
{
label: 'Sender number',
name: 'from',
type: 'phone',
placeholder: 'Enter phone'
}
]
},
[Providers.Textmagic]: {
imageIcon: 'textmagic',
title: 'Textmagic',
description: '',
configure: [
{
label: 'API key',
name: 'apiKey',
type: 'text',
placeholder: 'Enter API key',
popover: [
'<b>How to get the API key?</b>',
'Create an account in Textmagic.',
'Head to <b>TextMagic dashboard -> API Settings -> Add new API key.</b>'
]
},
{
label: 'Username',
name: 'username',
type: 'text',
placeholder: 'Enter username'
},
{
label: 'Sender number',
name: 'from',
type: 'phone',
placeholder: 'Enter phone'
}
]
},
[Providers.Vonage]: {
imageIcon: 'vonage',
title: 'Vonage',
description: '',
configure: [
{
label: 'API key',
name: 'apiKey',
type: 'text',
placeholder: 'Enter API key',
popover: [
'<b>How to get the API key?</b>',
'Create an account in Vonage.',
'Head to <b>Vonage dashboard and copy the API key.</b>'
]
},
{
label: 'API secret',
name: 'apiSecret',
type: 'text',
placeholder: 'Enter API secret',
popover: [
'<b>How to get the API secret?</b>',
'Head to <b>Vonage dashboard and copy the API secret.</b>'
]
},
{
label: 'Sender number',
name: 'from',
type: 'phone',
placeholder: 'Enter phone'
}
]
}
}
},
[ProviderTypes.Email]: {
name: 'Email',
text: 'emails',
icon: 'mail',
providers: {
[Providers.Mailgun]: {
imageIcon: 'mailgun',
title: 'Mailgun',
description: '',
configure: [
{
label: 'API key',
name: 'apiKey',
type: 'text',
placeholder: 'Enter API key',
popover: [
'<b>How to get the API key?</b>',
'Create an account in Mailgun.',
'Head to <b>Profile -> API Security -> Add new key.</b>'
]
},
{
label: 'Domain',
name: 'domain',
type: 'domain',
placeholder: 'Enter domain',
popover: [
'<b>How to create a domain?</b>',
'Head to <b>Sending -> Domains -> Add new domain.</b>',
'Follow <b>Mailgun instructions</b> to verify the domain name.'
]
},
{
label: 'EU region',
name: 'isEuRegion',
type: 'switch',
description:
'Enable the EU region setting if your domain is within the European Union.'
},
{
label: 'Sender email',
name: 'from',
type: 'email',
placeholder: 'Enter email'
}
]
},
[Providers.Sendgrid]: {
imageIcon: 'sendgrid',
title: 'Sendgrid',
description: '',
configure: [
{
label: 'API key',
name: 'apiKey',
type: 'text',
placeholder: 'Enter API key',
popover: [
'<b>How to get the API key?</b>',
'Create an account in Mailgun.',
'Head to <b>Profile -> API Security -> Add new key.</b>'
]
},
{
label: 'Sender email',
name: 'from',
type: 'email',
placeholder: 'Enter email'
}
]
}
}
},
[ProviderTypes.Push]: {
name: 'Push notification',
text: 'notifications',
icon: 'device-mobile',
providers: {
[Providers.FCM]: {
imageIcon: 'firebase',
title: 'FCM',
description: 'Firebase Cloud Messaging',
configure: [
{
label: 'Server key (.json file)',
name: 'serverKey',
type: 'file',
allowedFileExtensions: ['json'],
placeholder: 'Enter server key',
popover: [
'<b>How to get the FCM server key?</b>',
'Head to <b>Project settings -> Service accounts -> Generate new private key.</b>',
'Generating the new key will result in the download of a JSON file.'
]
}
]
},
[Providers.APNS]: {
imageIcon: 'apple',
title: 'APNS',
description: 'Apple Push Notification Service',
configure: [
{
label: 'Team ID',
name: 'teamId',
type: 'text',
placeholder: 'Enter team ID',
popover: [
'<b>How to get the team ID?</b>',
'Head to <b>Apple Developer Member Center -> Membership details -> Team ID.</b>'
]
},
{
label: 'Bundle ID',
name: 'bundleId',
type: 'text',
placeholder: 'Enter bundle ID',
popover: [
'<b>How to get the bundle ID?</b>',
'Head to <b>Apple Developer Member Center -> Certificates, Identifiers & Profiles -> Identifiers.</b>',
`<a
href="/images/apns-bundle-id.png"
class="file-preview is-with-image"
target="_blank"
rel="noopener noreferrer"
aria-label="open file in new window">
<div class="file-preview-image">
<img
width="205"
height="125"
src="/images/apns-bundle-id.png"
alt="Screenshot of Bundle ID in Apple" />
</div>
<div class="file-preview-content">
<div class="avatar">
<span class="icon-external-link" aria-hidden="true" />
</div>
</div>
</a>`
]
},
{
label: 'Authentication key ID',
name: 'authKeyId',
type: 'text',
placeholder: 'Enter key ID',
popover: [
'<b>How to get the auth key ID?</b>',
'Head to <b>Apple Developer Member Center -> Certificates, Identifiers & Profiles -> Keys.</b>',
'Click on your key to view details.'
]
},
{
label: 'Auth key (.p8 file)',
name: 'authKey',
type: 'file',
allowedFileExtensions: ['p8'],
popover: [
'<b>How to get the authentication key?</b>',
'Head to <b>Apple Developer Member Center</b> (under Program resources) <b>-> Certificates, Identifiers & Profiles -> Keys.</b>',
'Create a key and give it a name. Enable the Apple Push Notifications service (APNS), and register your key.'
]
}
]
}
// [Providers.MQTT]: {
// imageIcon: 'mqtt',
// title: 'MQTT',
// description: 'Message Queuing Telemtry Transport'
// }
}
}
};
@@ -0,0 +1,133 @@
<script lang="ts">
import {
FormList,
InputDomain,
InputEmail,
InputFile,
InputSwitch,
InputText
} from '$lib/elements/forms';
import InputPhone from '$lib/elements/forms/inputPhone.svelte';
import { WizardStep } from '$lib/layout';
import { providers } from '../store';
import { providerType, provider, providerParams } from './store';
let files: Record<string, FileList> = {};
const inputs = providers[$providerType].providers[$provider].configure;
async function beforeSubmit() {
const promises = [];
for (const [key, value] of Object.entries(files)) {
const promise = value[0].text().then((text) => {
$providerParams[$provider][key] = text;
});
promises.push(promise);
}
await Promise.all(promises);
console.log($providerParams[$provider]);
}
</script>
<WizardStep {beforeSubmit}>
<svelte:fragment slot="title">Configure</svelte:fragment>
<svelte:fragment slot="subtitle">
Set up the credentials below to enable {providers[$providerType].providers[$provider].title}
for sending
{providers[$providerType].text}.
</svelte:fragment>
<FormList>
{#each inputs as input}
{#if input.type === 'text'}
<InputText
id={input.name}
label={input.label}
placeholder={input.placeholder}
bind:value={$providerParams[$provider][input.name]}>
<svelte:fragment slot="popover">
{@html input.popover?.join('<br/><br/>')}
</svelte:fragment>
</InputText>
{:else if input.type === 'email'}
<InputEmail
id={input.name}
label={input.label}
placeholder={input.placeholder}
bind:value={$providerParams[$provider][input.name]}>
<svelte:fragment slot="popover">
<p class="body-text-2 u-margin-block-end-16">
{@html input.popover?.join('<br/><br/>')}
</p>
</svelte:fragment>
</InputEmail>
{:else if input.type === 'domain'}
<InputDomain
id={input.name}
label={input.label}
placeholder={input.placeholder}
bind:value={$providerParams[$provider][input.name]}>
<svelte:fragment slot="popover">
<p class="body-text-2 u-margin-block-end-16">
{@html input.popover?.join('<br/><br/>')}
</p>
</svelte:fragment>
</InputDomain>
{:else if input.type === 'phone'}
<InputPhone
id={input.name}
label={input.label}
placeholder={input.placeholder}
bind:value={$providerParams[$provider][input.name]}>
<svelte:fragment slot="popover">
<p class="body-text-2 u-margin-block-end-16">
{@html input.popover?.join('<br/><br/>')}
</p>
</svelte:fragment>
</InputPhone>
{:else if input.type === 'file'}
<InputFile
label={input.label}
allowedFileExtensions={input.allowedFileExtensions}
bind:files={files[input.name]}>
<svelte:fragment slot="popover">
<p class="body-text-2 u-margin-block-end-16">
{@html input.popover?.join('<br/><br/>')}
</p>
</svelte:fragment>
</InputFile>
{:else if input.type === 'switch'}
<InputSwitch
label={input.label}
id={input.name}
bind:value={$providerParams[$provider][input.name]}>
<svelte:fragment slot="description">
{input.description}
</svelte:fragment>
</InputSwitch>
{/if}
{/each}
</FormList>
<p class="body-text-2 u-bold u-margin-block-start-48">Need a hand?</p>
<div
class="u-flex u-cross-center u-main-space-between u-padding-block-16"
style="border-block-end: solid .0625rem hsl(var(--color-border))">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span class="icon-book-open" style:--p-text-size="1.25rem" aria-hidden="true" />
</div>
Read the full guide in the documentation
</div>
<span class="icon-arrow-right" aria-hidden="true" />
</div>
<div class="u-flex u-cross-center u-main-space-between u-padding-block-16">
<div class="u-flex u-cross-center u-gap-16">
<div class="avatar is-size-small">
<span class="icon-user-group" style:--p-text-size="1.25rem" aria-hidden="true" />
</div>
Invite a team member to complete this step
</div>
<span class="icon-arrow-right" aria-hidden="true" />
</div>
</WizardStep>
@@ -0,0 +1,161 @@
<script lang="ts">
import { WizardStep } from '$lib/layout';
import { providerType, provider, providerParams } from './store';
import { CustomId, LabelCard } from '$lib/components';
import { providers } from '../store';
import { FormList, InputText } from '$lib/elements/forms';
import { Pill } from '$lib/elements';
import { Providers } from '../../provider.svelte';
let name = '';
let showCustomId = false;
let id: string = null;
async function beforeSubmit() {
console.log($provider);
switch ($provider) {
case Providers.Twilio:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
accountSid: '',
authToken: '',
from: ''
};
break;
case Providers.Msg91:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
from: '',
senderId: '',
authKey: ''
};
break;
case Providers.Telesign:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
username: '',
password: '',
from: ''
};
break;
case Providers.Textmagic:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
username: '',
apiKey: '',
from: ''
};
break;
case Providers.Vonage:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
apiKey: '',
apiSecret: '',
from: ''
};
break;
case Providers.Mailgun:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
isEuRegion: false,
from: '',
apiKey: '',
domain: ''
};
break;
case Providers.Sendgrid:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
apiKey: '',
from: ''
};
break;
case Providers.FCM:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
serverKey: ''
};
break;
case Providers.APNS:
$providerParams[$provider] = {
providerId: id,
name: name,
default: false,
enabled: false,
authKey: '',
authKeyId: '',
teamId: '',
bundleId: ''
};
break;
case Providers.MQTT:
break;
}
}
</script>
<WizardStep {beforeSubmit}>
<svelte:fragment slot="title">Provider</svelte:fragment>
<FormList>
<InputText
id="name"
label="Name"
placeholder="Provider name"
bind:value={name}
autofocus
required />
{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}
><span class="icon-pencil" aria-hidden="true" /><span class="text">
Provider ID
</span></Pill>
</div>
{:else}
<CustomId bind:show={showCustomId} name="Provider" bind:id autofocus={false} />
{/if}
<p class="u-margin-block-start-24">
Select a provider you would like to enable for sending {providers[$providerType].text}.
</p>
<div class="grid-box">
{#each Object.entries(providers[$providerType].providers) as [value, option]}
<LabelCard
name="provider"
{value}
bind:group={$provider}
imageIcon={option.imageIcon}>
<svelte:fragment slot="title">{option.title}</svelte:fragment>
{#if option.description}
{option.description}
{/if}
</LabelCard>
{/each}
</div>
</FormList>
</WizardStep>
@@ -0,0 +1,103 @@
import type { Providers } from '../../provider.svelte';
import type { ProviderTypes } from '../../providerType.svelte';
import { writable } from 'svelte/store';
type ProviderParams = {
providerId: string;
name: string;
default: boolean;
enabled: boolean;
};
/**
* SMS providers
*/
export type TwilioProviderParams = ProviderParams & {
accountSid: string;
authToken: string;
from: string;
};
export type Msg91ProviderParams = ProviderParams & {
from: string;
senderId: string;
authKey: string;
};
export type TelesignProviderParams = ProviderParams & {
from: string;
username: string;
password: string;
};
export type TextmagicProviderParams = ProviderParams & {
from: string;
username: string;
apiKey: string;
};
export type VonageProviderParams = ProviderParams & {
from: string;
apiKey: string;
apiSecret: string;
};
/**
* Email providers
*/
export type MailgunProviderParams = ProviderParams & {
isEuRegion: boolean;
from: string;
apiKey: string;
domain: string;
};
export type SendgridProviderParams = ProviderParams & {
from: string;
apiKey: string;
};
/**
* Push providers
*/
export type FCMProviderParams = ProviderParams & {
serverKey: string;
};
export type APNSProviderParams = ProviderParams & {
authKey: string;
authKeyId: string;
teamId: string;
bundleId: string;
};
export type MQTTProviderParams = ProviderParams & {
serverKey: string;
};
export const providerType = writable<ProviderTypes>(null);
export const provider = writable<Providers>(null);
export const providerParams = writable<{
twilio: Partial<TwilioProviderParams>;
msg91: Partial<Msg91ProviderParams>;
telesign: Partial<TelesignProviderParams>;
textmagic: Partial<TextmagicProviderParams>;
vonage: Partial<VonageProviderParams>;
mailgun: Partial<MailgunProviderParams>;
sendgrid: Partial<SendgridProviderParams>;
fcm: Partial<FCMProviderParams>;
apns: Partial<APNSProviderParams>;
}>({
twilio: null,
msg91: null,
telesign: null,
textmagic: null,
vonage: null,
mailgun: null,
sendgrid: null,
fcm: null,
apns: null
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB