Skip to content

Commit 1bded60

Browse files
committed
Create a wizard to send email messages
1 parent 137f92d commit 1bded60

File tree

9 files changed

+607
-83
lines changed

9 files changed

+607
-83
lines changed

src/lib/actions/analytics.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,5 +285,6 @@ export enum Submit {
285285
SmsUpdateVerificationTemplate = 'submit_sms_update_verification_template',
286286
MessagingProviderCreate = 'submit_messaging_provider_create',
287287
MessagingProviderDelete = 'submit_messaging_provider_delete',
288-
MessagingProviderUpdate = 'submit_messaging_provider_update'
288+
MessagingProviderUpdate = 'submit_messaging_provider_update',
289+
MessagingMessageCreate = 'submit_messaging_message_create'
289290
}

src/routes/console/project-[project]/messaging/+page.svelte

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<script lang="ts">
2-
import { goto } from '$app/navigation';
32
import { base } from '$app/paths';
43
import { page } from '$app/stores';
54
import {
@@ -21,40 +20,34 @@
2120
TableCellHeadCheck,
2221
TableCellText,
2322
TableHeader,
24-
TableRowLink
23+
TableRowLink,
24+
TableScroll
2525
} from '$lib/elements/table';
26-
import TableScroll from '$lib/elements/table/tableScroll.svelte';
2726
import { toLocaleDateTime } from '$lib/helpers/date';
2827
import { Container } from '$lib/layout';
29-
import type { Models } from '@appwrite.io/console';
3028
import Filters from '../databases/database-[database]/collection-[collection]/(filters)/filters.svelte';
3129
import type { PageData } from './$types';
32-
import Create from './create.svelte';
3330
import { columns, showCreate } from './store';
3431
import MessageStatusPill from './messageStatusPill.svelte';
32+
import CreateMessageDropdown from './createMessageDropdown.svelte';
3533
import ProviderType, { ProviderTypes } from './providerType.svelte';
3634
3735
export let data: PageData;
3836
let selected: string[] = [];
3937
let showDelete = false;
38+
let showCreateDropdownMobile = false;
39+
let showCreateDropdownDesktop = false;
40+
let showCreateDropdownEmpty = false;
4041
4142
const project = $page.params.project;
42-
43-
async function messageCreated(event: CustomEvent<Models.Bucket>) {
44-
$showCreate = false;
45-
await goto(`${base}/console/project-${project}/messaging/message-${event.detail.$id}`);
46-
}
4743
</script>
4844

4945
<Container>
5046
<div class="u-flex u-flex-vertical">
5147
<div class="u-flex u-main-space-between">
5248
<Heading tag="h2" size="5">Messages</Heading>
5349
<div class="is-only-mobile">
54-
<Button on:click={() => ($showCreate = true)} event="create_message">
55-
<span class="icon-plus" aria-hidden="true" />
56-
<span class="text">Create message</span>
57-
</Button>
50+
<CreateMessageDropdown bind:showCreateDropdown={showCreateDropdownMobile} />
5851
</div>
5952
</div>
6053
<!-- TODO: fix width of search input in mobile -->
@@ -68,10 +61,7 @@
6861
hideView
6962
allowNoColumns
7063
showColsTextMobile />
71-
<Button on:click={() => ($showCreate = true)} event="create_message">
72-
<span class="icon-plus" aria-hidden="true" />
73-
<span class="text">Create message</span>
74-
</Button>
64+
<CreateMessageDropdown bind:showCreateDropdown={showCreateDropdownDesktop} />
7565
</div>
7666
</SearchQuery>
7767
<div class="u-flex u-gap-16 is-only-mobile u-margin-block-start-16">
@@ -210,9 +200,33 @@
210200
single
211201
href="https://appwrite.io/docs"
212202
target="message"
213-
on:click={() => ($showCreate = true)} />
203+
on:click={() => ($showCreate = true)}>
204+
<div class="u-text-center">
205+
<Heading size="7" tag="h2" trimmed={false}>
206+
Create your first message to get started.
207+
</Heading>
208+
<p class="body-text-2 u-bold u-margin-block-start-4">
209+
Need a hand? Learn more in our documentation.
210+
</p>
211+
</div>
212+
<div class="u-flex u-flex-wrap u-gap-16 u-main-center">
213+
<Button
214+
external
215+
href="https://appwrite.io/docs/references/cloud/client-web/messages"
216+
text
217+
event="empty_documentation"
218+
ariaLabel={`create message`}>
219+
Documentation
220+
</Button>
221+
<CreateMessageDropdown bind:showCreateDropdown={showCreateDropdownEmpty}>
222+
<Button
223+
secondary
224+
on:click={() => (showCreateDropdownEmpty = !showCreateDropdownEmpty)}
225+
event="create_message">
226+
<span class="text">Create message</span>
227+
</Button>
228+
</CreateMessageDropdown>
229+
</div>
230+
</Empty>
214231
{/if}
215232
</Container>
216-
217-
<!-- TODO: handle create -->
218-
<Create bind:showCreate={$showCreate} on:created={messageCreated} />
Lines changed: 98 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,111 @@
11
<script lang="ts">
2-
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
3-
import { Modal, CustomId } from '$lib/components';
4-
import { Pill } from '$lib/elements';
5-
import { Button, InputText, FormList } from '$lib/elements/forms';
6-
import { addNotification } from '$lib/stores/notifications';
2+
import { onDestroy } from 'svelte';
3+
import { Wizard } from '$lib/layout';
4+
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
5+
import Step1 from './wizard/step1.svelte';
6+
import Step2 from './wizard/step2.svelte';
7+
import Step3 from './wizard/step3.svelte';
78
import { sdk } from '$lib/stores/sdk';
9+
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
10+
import { addNotification } from '$lib/stores/notifications';
11+
import { goto } from '$app/navigation';
12+
import { base } from '$app/paths';
13+
import { project } from '../store';
14+
import { wizard } from '$lib/stores/wizard';
15+
import { providerType, messageParams } from './wizard/store';
16+
import { ProviderTypes } from './providerType.svelte';
817
import { ID } from '@appwrite.io/console';
9-
import { createEventDispatcher } from 'svelte';
10-
11-
export let showCreate = false;
1218
13-
const dispatch = createEventDispatcher();
14-
15-
let name = '';
16-
let id: string = null;
17-
let showCustomId = false;
18-
let error: string;
19-
20-
const create = async () => {
19+
async function create() {
2120
try {
22-
const bucket = await sdk.forProject.storage.createBucket(id ? id : ID.unique(), name);
23-
showCreate = false;
24-
dispatch('created', bucket);
21+
let response = { $id: '' };
22+
const messageId = $messageParams[$providerType].messageId || ID.unique();
23+
switch ($providerType) {
24+
case ProviderTypes.Email:
25+
response = await sdk.forProject.client.call(
26+
'POST',
27+
new URL(
28+
sdk.forProject.client.config.endpoint + '/messaging/messages/email'
29+
),
30+
{
31+
'X-Appwrite-Project': sdk.forProject.client.config.project,
32+
'content-type': 'application/json',
33+
'X-Appwrite-Mode': 'admin'
34+
},
35+
{
36+
...$messageParams[$providerType],
37+
messageId
38+
}
39+
);
40+
break;
41+
case ProviderTypes.Sms:
42+
response = await sdk.forProject.client.call(
43+
'POST',
44+
new URL(sdk.forProject.client.config.endpoint + '/messaging/messages/sms'),
45+
{
46+
'X-Appwrite-Project': sdk.forProject.client.config.project,
47+
'content-type': 'application/json',
48+
'X-Appwrite-Mode': 'admin'
49+
},
50+
{
51+
...$messageParams[$providerType],
52+
messageId
53+
}
54+
);
55+
break;
56+
case ProviderTypes.Push:
57+
response = await sdk.forProject.client.call(
58+
'POST',
59+
new URL(
60+
sdk.forProject.client.config.endpoint + '/messaging/providers/telesign'
61+
),
62+
{
63+
'X-Appwrite-Project': sdk.forProject.client.config.project,
64+
'content-type': 'application/json',
65+
'X-Appwrite-Mode': 'admin'
66+
},
67+
{
68+
...$messageParams[$providerType],
69+
messageId
70+
}
71+
);
72+
break;
73+
}
74+
wizard.hide();
2575
addNotification({
2676
type: 'success',
27-
message: `${name} has been created`
77+
message: `The message has been sent.`
78+
});
79+
trackEvent(Submit.MessagingMessageCreate, {
80+
providerType: $providerType
2881
});
29-
name = null;
30-
trackEvent(Submit.BucketCreate, {
31-
customId: !!id
82+
await goto(`${base}/console/project-${$project.$id}/messaging/message-${response.$id}`);
83+
} catch (error) {
84+
addNotification({
85+
type: 'error',
86+
message: error.message
3287
});
33-
} catch (e) {
34-
error = e.message;
35-
trackError(e, Submit.BucketCreate);
88+
trackError(error, Submit.MessagingProviderCreate);
3689
}
37-
};
38-
39-
$: if (!showCustomId) {
40-
id = null;
41-
}
42-
$: if (!showCreate) {
43-
showCustomId = false;
44-
error = null;
4590
}
46-
</script>
4791
48-
<Modal title="Create message" {error} onSubmit={create} size="big" bind:show={showCreate}>
49-
<FormList>
50-
<InputText
51-
id="name"
52-
label="Name"
53-
placeholder="New bucket"
54-
bind:value={name}
55-
autofocus
56-
required />
92+
onDestroy(() => {
93+
console.log('destroy');
94+
});
95+
96+
const stepsComponents: WizardStepsType = new Map();
97+
stepsComponents.set(1, {
98+
label: 'Message',
99+
component: Step1
100+
});
101+
stepsComponents.set(2, {
102+
label: 'Targets',
103+
component: Step2
104+
});
105+
stepsComponents.set(3, {
106+
label: 'Schedule',
107+
component: Step3
108+
});
109+
</script>
57110

58-
{#if !showCustomId}
59-
<div>
60-
<Pill button on:click={() => (showCustomId = !showCustomId)}>
61-
<span class="icon-pencil" aria-hidden="true" />
62-
<span class="text"> Bucket ID </span>
63-
</Pill>
64-
</div>
65-
{:else}
66-
<CustomId bind:show={showCustomId} name="Bucket" bind:id />
67-
{/if}
68-
</FormList>
69-
<svelte:fragment slot="footer">
70-
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
71-
<Button submit>Create</Button>
72-
</svelte:fragment>
73-
</Modal>
111+
<Wizard title="Create message" steps={stepsComponents} on:finish={create} finalAction="Send" />
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<script lang="ts">
2+
import { DropList, DropListItem } from '$lib/components';
3+
import { Button } from '$lib/elements/forms';
4+
import { wizard } from '$lib/stores/wizard';
5+
import { providers } from './providers/store';
6+
import Create from './create.svelte';
7+
import { messageParams, providerType, targetsById } from './wizard/store';
8+
import { ProviderTypes } from './providerType.svelte';
9+
10+
export let showCreateDropdown = false;
11+
</script>
12+
13+
<DropList bind:show={showCreateDropdown} scrollable>
14+
<slot>
15+
<Button on:click={() => (showCreateDropdown = !showCreateDropdown)} event="create_message">
16+
<span class="icon-plus" aria-hidden="true" />
17+
<span class="text">Create message</span>
18+
</Button>
19+
</slot>
20+
<svelte:fragment slot="list">
21+
{#each Object.entries(providers) as [type, option]}
22+
<DropListItem
23+
icon={option.icon}
24+
on:click={() => {
25+
if (
26+
type !== ProviderTypes.Email &&
27+
type !== ProviderTypes.Sms &&
28+
type !== ProviderTypes.Push
29+
)
30+
return;
31+
$providerType = type;
32+
$targetsById = {};
33+
const common = {
34+
topics: [],
35+
users: [],
36+
targets: []
37+
};
38+
switch (type) {
39+
case ProviderTypes.Email:
40+
$messageParams[$providerType] = {
41+
...common,
42+
subject: '',
43+
content: ''
44+
};
45+
break;
46+
case ProviderTypes.Sms:
47+
$messageParams[$providerType] = {
48+
...common,
49+
content: ''
50+
};
51+
break;
52+
case ProviderTypes.Push:
53+
$messageParams[$providerType] = {
54+
...common,
55+
title: '',
56+
body: ''
57+
};
58+
break;
59+
}
60+
showCreateDropdown = false;
61+
wizard.start(Create);
62+
}}>
63+
{option.name}
64+
</DropListItem>
65+
{/each}
66+
</svelte:fragment>
67+
</DropList>

0 commit comments

Comments
 (0)