mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Add messaging overview route
This commit is contained in:
13 files changed
+949
No files matched your search
@@ -17,6 +17,7 @@ const groups = [
|
||||
'platforms',
|
||||
'databases',
|
||||
'functions',
|
||||
'messaging',
|
||||
'storage',
|
||||
'domains',
|
||||
'webhooks',
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { registerCommands, updateCommandGroupRanks } from '$lib/commandCenter';
|
||||
import { project } from '../store';
|
||||
import { showCreate } from './store';
|
||||
|
||||
// TODO: finalize the commands
|
||||
|
||||
$: $registerCommands([
|
||||
{
|
||||
label: 'Create message',
|
||||
callback: () => {
|
||||
if (!$page.url.pathname.endsWith('messaging')) {
|
||||
goto(`/console/project-${$project.$id}/messaging`);
|
||||
}
|
||||
$showCreate = true;
|
||||
},
|
||||
keys: $page.url.pathname.endsWith('messaging') ? ['c'] : ['c', 'm'],
|
||||
icon: 'plus',
|
||||
group: 'messaging'
|
||||
},
|
||||
{
|
||||
label: 'Go to topics',
|
||||
callback() {
|
||||
goto(`/console/project-${$project.$id}/messaging/topics`);
|
||||
},
|
||||
keys: ['g', 't'],
|
||||
disabled:
|
||||
$page.url.pathname.endsWith('topics') || $page.url.pathname.includes('message-'),
|
||||
group: 'navigation',
|
||||
rank: 10
|
||||
},
|
||||
{
|
||||
label: 'Go to providers',
|
||||
callback() {
|
||||
goto(`/console/project-${$project.$id}/messaging/providers`);
|
||||
},
|
||||
keys: ['g', 'p'],
|
||||
disabled:
|
||||
$page.url.pathname.endsWith('topics') || $page.url.pathname.includes('message-'),
|
||||
group: 'navigation',
|
||||
rank: 10
|
||||
}
|
||||
// {
|
||||
// label: 'Find messages',
|
||||
// callback: () => {
|
||||
// addSubPanel(BucketsPanel);
|
||||
// },
|
||||
// group: 'messaging',
|
||||
// rank: -1
|
||||
// }
|
||||
]);
|
||||
|
||||
$: $updateCommandGroupRanks({ messaging: 200, navigation: 100 });
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Messaging - Appwrite</title>
|
||||
</svelte:head>
|
||||
|
||||
<slot />
|
||||
@@ -0,0 +1,10 @@
|
||||
import Breadcrumbs from './breadcrumbs.svelte';
|
||||
import Header from './header.svelte';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = async () => {
|
||||
return {
|
||||
header: Header,
|
||||
breadcrumbs: Breadcrumbs
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,229 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import {
|
||||
Empty,
|
||||
EmptySearch,
|
||||
FloatingActionBar,
|
||||
Heading,
|
||||
Id,
|
||||
PaginationWithLimit,
|
||||
SearchQuery,
|
||||
ViewSelector
|
||||
} from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import {
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellCheck,
|
||||
TableCellHead,
|
||||
TableCellHeadCheck,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRowLink
|
||||
} from '$lib/elements/table';
|
||||
import TableScroll from '$lib/elements/table/tableScroll.svelte';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { PageData } from './$types';
|
||||
import Create from './create.svelte';
|
||||
import { columns, showCreate } from './store';
|
||||
import MessageStatusPill from './messageStatusPill.svelte';
|
||||
import ProviderType, { ProviderTypes } from './providerType.svelte';
|
||||
import Filters from '$lib/components/filters/filters.svelte';
|
||||
import FailedModal from './failedModal.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
let selected: string[] = [];
|
||||
let showDelete = false;
|
||||
let showFailed = false;
|
||||
let errors: string[] = [];
|
||||
|
||||
const project = $page.params.project;
|
||||
|
||||
async function messageCreated(event: CustomEvent<Models.Bucket>) {
|
||||
$showCreate = false;
|
||||
await goto(`${base}/console/project-${project}/messaging/message-${event.detail.$id}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-flex-vertical">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<Heading tag="h2" size="5">Messages</Heading>
|
||||
<div class="is-only-mobile">
|
||||
<Button on:click={() => ($showCreate = true)} event="create_message">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create message</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: fix width of search input in mobile -->
|
||||
<SearchQuery search={data.search} placeholder="Search by channel, topic, provider, or ID">
|
||||
<div class="u-flex u-gap-16 is-not-mobile">
|
||||
<!-- TODO: make this not database-specific -->
|
||||
<Filters query={data.query} {columns} />
|
||||
<ViewSelector
|
||||
view={data.view}
|
||||
{columns}
|
||||
hideView
|
||||
allowNoColumns
|
||||
showColsTextMobile />
|
||||
<Button on:click={() => ($showCreate = true)} event="create_message">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create message</span>
|
||||
</Button>
|
||||
</div>
|
||||
</SearchQuery>
|
||||
<div class="u-flex u-gap-16 is-only-mobile u-margin-block-start-16">
|
||||
<div class="u-flex-basis-50-percent">
|
||||
<!-- TODO: fix width -->
|
||||
<ViewSelector
|
||||
view={data.view}
|
||||
{columns}
|
||||
hideView
|
||||
allowNoColumns
|
||||
showColsTextMobile />
|
||||
</div>
|
||||
<div class="u-flex-basis-50-percent">
|
||||
<!-- TODO: fix width -->
|
||||
<Filters query={data.query} {columns} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if data.messages.total}
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHeadCheck
|
||||
bind:selected
|
||||
pageItemsIds={data.messages.messages.map((d) => d.$id)} />
|
||||
{#each $columns as column}
|
||||
{#if column.show}
|
||||
<TableCellHead width={column.width}>{column.title}</TableCellHead>
|
||||
{/if}
|
||||
{/each}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each data.messages.messages as message}
|
||||
<TableRowLink
|
||||
href={`${base}/console/project-${project}/messaging/message-${message.$id}`}>
|
||||
<TableCellCheck bind:selectedIds={selected} id={message.$id} />
|
||||
|
||||
{#each $columns as column (column.id)}
|
||||
{#if column.show}
|
||||
{#if column.id === '$id'}
|
||||
{#key $columns}
|
||||
<TableCell title={column.title} width={column.width}>
|
||||
<Id value={message.$id}>{message.$id}</Id>
|
||||
</TableCell>
|
||||
{/key}
|
||||
{:else if column.id === 'message'}
|
||||
<TableCellText title={column.title} width={column.width}>
|
||||
{#if message.providerType === ProviderTypes.Push}
|
||||
{message.data.title}
|
||||
{:else if message.providerType === ProviderTypes.Sms}
|
||||
{message.data.content}
|
||||
{:else if message.providerType === ProviderTypes.Email}
|
||||
{message.data.subject}
|
||||
{:else}
|
||||
Invalid provider
|
||||
{/if}
|
||||
</TableCellText>
|
||||
{:else if column.id === 'providerType'}
|
||||
<TableCellText title={column.title} width={column.width}>
|
||||
<ProviderType type={message.providerType} size="s" />
|
||||
</TableCellText>
|
||||
{:else if column.id === 'status'}
|
||||
<TableCellText onlyDesktop title="Status">
|
||||
<MessageStatusPill
|
||||
status={message.status}
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
errors = message.deliveryErrors;
|
||||
showFailed = true;
|
||||
}} />
|
||||
</TableCellText>
|
||||
{:else if column.type === 'datetime'}
|
||||
<TableCellText title={column.title} width={column.width}>
|
||||
{#if !message[column.id]}
|
||||
-
|
||||
{:else}
|
||||
{toLocaleDateTime(message[column.id])}
|
||||
{/if}
|
||||
</TableCellText>
|
||||
{:else}
|
||||
<TableCellText title={column.title} width={column.width}>
|
||||
{message[column.id]}
|
||||
</TableCellText>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
</TableRowLink>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
|
||||
<FloatingActionBar show={selected.length > 0}>
|
||||
<div class="u-flex u-cross-center u-main-space-between actions">
|
||||
<div class="u-flex u-cross-center u-gap-8">
|
||||
<span class="indicator body-text-2 u-bold">{selected.length}</span>
|
||||
<p>
|
||||
<span class="is-only-desktop">
|
||||
{selected.length > 1 ? 'messages' : 'message'}
|
||||
</span>
|
||||
selected
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="u-flex u-cross-center u-gap-8">
|
||||
<Button text on:click={() => (selected = [])}>Cancel</Button>
|
||||
<!-- TODO: handle delete -->
|
||||
<Button secondary on:click={() => (showDelete = true)}>
|
||||
<p>Delete</p>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FloatingActionBar>
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Messages"
|
||||
limit={data.limit}
|
||||
offset={data.offset}
|
||||
total={data.messages.total} />
|
||||
<!-- TODO: remove data.search != 'empty' when the API is ready with data -->
|
||||
{:else if data.search && data.search != 'empty'}
|
||||
<EmptySearch>
|
||||
<div class="u-text-center">
|
||||
<b>Sorry, we couldn't find '{data.search}'</b>
|
||||
<p>There are no messages that match your search.</p>
|
||||
</div>
|
||||
<div class="u-flex u-gap-16">
|
||||
<!-- TODO: update docs link -->
|
||||
<Button
|
||||
external
|
||||
href="https://appwrite.io/docs/products/storage/upload-download"
|
||||
text>
|
||||
Documentation
|
||||
</Button>
|
||||
<Button secondary href={`/console/project-${$page.params.project}/messaging`}>
|
||||
Clear search
|
||||
</Button>
|
||||
</div>
|
||||
</EmptySearch>
|
||||
{:else}
|
||||
<!-- TODO: update docs link -->
|
||||
<Empty
|
||||
single
|
||||
href="https://appwrite.io/docs"
|
||||
target="message"
|
||||
on:click={() => ($showCreate = true)} />
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<FailedModal bind:show={showFailed} {errors} />
|
||||
|
||||
<!-- TODO: handle create -->
|
||||
<Create bind:showCreate={$showCreate} on:created={messageCreated} />
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
View,
|
||||
getLimit,
|
||||
getPage,
|
||||
getQuery,
|
||||
getSearch,
|
||||
getView,
|
||||
pageToOffset
|
||||
} from '$lib/helpers/load';
|
||||
import { CARD_LIMIT } from '$lib/constants';
|
||||
import type { PageLoad } from './$types';
|
||||
import { messages as data, providersById } from './store';
|
||||
import type { Message } from './store';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { queries, queryParamToMap } from '$lib/components/filters/store';
|
||||
|
||||
export const load: PageLoad = async ({ url, route }) => {
|
||||
const page = getPage(url);
|
||||
const search = getSearch(url);
|
||||
const view = getView(url, route, View.Grid);
|
||||
const limit = getLimit(url, route, CARD_LIMIT);
|
||||
const offset = pageToOffset(page, limit);
|
||||
const query = getQuery(url);
|
||||
|
||||
const parsedQueries = queryParamToMap(query || '[]');
|
||||
queries.set(parsedQueries);
|
||||
|
||||
// TODO: remove when the API is ready with data
|
||||
// This allows us to mock w/ data and when search returns 0 results
|
||||
let messages: { messages: Message[]; total: number } = { messages: [], total: 0 };
|
||||
if (search === 'demo') {
|
||||
messages = data;
|
||||
} else {
|
||||
const params = {
|
||||
queries: [
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc(''),
|
||||
...parsedQueries.values()
|
||||
]
|
||||
};
|
||||
|
||||
if (search) {
|
||||
params['search'] = search;
|
||||
}
|
||||
|
||||
const response = await sdk.forProject.client.call(
|
||||
'GET',
|
||||
new URL(sdk.forProject.client.config.endpoint + '/messaging/messages'),
|
||||
{
|
||||
'X-Appwrite-Project': sdk.forProject.client.config.project,
|
||||
'content-type': 'application/json',
|
||||
'X-Appwrite-Mode': 'admin'
|
||||
},
|
||||
params
|
||||
);
|
||||
|
||||
messages = response;
|
||||
}
|
||||
|
||||
return {
|
||||
offset,
|
||||
limit,
|
||||
search,
|
||||
query,
|
||||
page,
|
||||
view,
|
||||
messages,
|
||||
providersById
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { Breadcrumbs } from '$lib/layout';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { project } from '../store';
|
||||
|
||||
$: breadcrumbs = [
|
||||
{
|
||||
href: `/console/organization-${$organization.$id}`,
|
||||
title: $organization.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project.$id}`,
|
||||
title: $project.name
|
||||
},
|
||||
{
|
||||
href: `/console/project-${$project.$id}/messaging`,
|
||||
title: 'Messaging'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Breadcrumbs {breadcrumbs} />
|
||||
@@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal, CustomId } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Button, InputText, FormList } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let showCreate = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let name = '';
|
||||
let id: string = null;
|
||||
let showCustomId = false;
|
||||
let error: string;
|
||||
|
||||
const create = async () => {
|
||||
try {
|
||||
const bucket = await sdk.forProject.storage.createBucket(id ? id : ID.unique(), name);
|
||||
showCreate = false;
|
||||
dispatch('created', bucket);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${name} has been created`
|
||||
});
|
||||
name = null;
|
||||
trackEvent(Submit.BucketCreate, {
|
||||
customId: !!id
|
||||
});
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
trackError(e, Submit.BucketCreate);
|
||||
}
|
||||
};
|
||||
|
||||
$: if (!showCustomId) {
|
||||
id = null;
|
||||
}
|
||||
$: if (!showCreate) {
|
||||
showCustomId = false;
|
||||
error = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Create message" {error} onSubmit={create} size="big" bind:show={showCreate}>
|
||||
<FormList>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="New bucket"
|
||||
bind:value={name}
|
||||
autofocus
|
||||
required />
|
||||
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" />
|
||||
<span class="text"> Bucket ID </span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId bind:show={showCustomId} name="Bucket" bind:id />
|
||||
{/if}
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
|
||||
<Button submit>Create</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import Code from '$lib/components/code.svelte';
|
||||
import Modal from '$lib/components/modal.svelte';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
|
||||
export let show: boolean;
|
||||
export let errors: string[];
|
||||
</script>
|
||||
|
||||
<Modal title="Message error" headerDivider={false} bind:show size="big">
|
||||
<div class="box u-flex-vertical u-gap-24">
|
||||
<p>Some messages failed to send.</p>
|
||||
<div style="max-inline-size: 524px">
|
||||
<Code language="html" code={errors.join('\n')} noMargin noBoxPadding allowScroll />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Close</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Tab, Tabs } from '$lib/components';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { Cover, CoverTitle } from '$lib/layout';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
const path = `/console/project-${projectId}/messaging`;
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Messages',
|
||||
event: 'messages',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/topics`,
|
||||
title: 'Topics',
|
||||
event: 'topics',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/providers`,
|
||||
title: 'Providers',
|
||||
event: 'providers'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<Cover>
|
||||
<svelte:fragment slot="header">
|
||||
<CoverTitle>Messaging</CoverTitle>
|
||||
</svelte:fragment>
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, $page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</Cover>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Pill } from '$lib/elements';
|
||||
|
||||
export let status: string;
|
||||
</script>
|
||||
|
||||
<Pill
|
||||
success={status === 'sent'}
|
||||
info={status === 'scheduled'}
|
||||
button={status == 'failed'}
|
||||
on:click>
|
||||
{#if status === 'sent'}
|
||||
<span class="icon-check-circle" aria-hidden="true"></span>
|
||||
{:else if status === 'scheduled'}
|
||||
<span class="icon-clock" aria-hidden="true"></span>
|
||||
{/if}
|
||||
<span class="text u-trim">
|
||||
{status}
|
||||
</span>
|
||||
</Pill>
|
||||
@@ -0,0 +1,96 @@
|
||||
<script context="module" lang="ts">
|
||||
export enum Providers {
|
||||
Twilio = 'twilio',
|
||||
Msg91 = 'msg91',
|
||||
Telesign = 'telesign',
|
||||
Textmagic = 'textmagic',
|
||||
Vonage = 'vonage',
|
||||
Mailgun = 'mailgun',
|
||||
Sendgrid = 'sendgrid',
|
||||
FCM = 'fcm',
|
||||
APNS = 'apns',
|
||||
MQTT = 'mqtt'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { app } from '$lib/stores/app';
|
||||
|
||||
export let provider: Providers;
|
||||
export let name: string = '';
|
||||
export let noIcon = false;
|
||||
export let size: 's' | 'm' | 'l' = 'm';
|
||||
|
||||
let icon = '';
|
||||
let displayName = name || provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
|
||||
let textSize = '1.25rem';
|
||||
switch (size) {
|
||||
case 's':
|
||||
textSize = '1rem';
|
||||
break;
|
||||
case 'l':
|
||||
textSize = '1.5rem';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (provider) {
|
||||
case Providers.FCM:
|
||||
icon = 'firebase';
|
||||
displayName = name || 'FCM';
|
||||
break;
|
||||
case Providers.APNS:
|
||||
icon = 'apple';
|
||||
displayName = name || 'APNS';
|
||||
break;
|
||||
case Providers.MQTT:
|
||||
icon = 'mqtt';
|
||||
displayName = name || 'MQTT';
|
||||
break;
|
||||
case Providers.Sendgrid:
|
||||
icon = 'sendgrid';
|
||||
break;
|
||||
case Providers.Mailgun:
|
||||
icon = 'mailgun';
|
||||
break;
|
||||
case Providers.Twilio:
|
||||
icon = 'twilio';
|
||||
break;
|
||||
case Providers.Telesign:
|
||||
icon = 'telesign';
|
||||
break;
|
||||
case Providers.Msg91:
|
||||
icon = 'msg91';
|
||||
displayName = name || 'MSG91';
|
||||
break;
|
||||
case Providers.Textmagic:
|
||||
icon = 'textmagic';
|
||||
displayName = name || 'TextMagic';
|
||||
break;
|
||||
case Providers.Vonage:
|
||||
icon = 'vonage';
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if icon === ''}
|
||||
Invalid provider
|
||||
{:else}
|
||||
<div class="u-inline-flex u-cross-center u-gap-8">
|
||||
{#if !noIcon}
|
||||
<div
|
||||
class="avatar"
|
||||
class:is-size-large={size === 'l'}
|
||||
class:is-size-small={size === 's'}>
|
||||
<img
|
||||
style:--p-text-size={textSize}
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${icon}.svg`}
|
||||
alt={displayName} />
|
||||
</div>
|
||||
{/if}
|
||||
<slot>
|
||||
{displayName}
|
||||
</slot>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,59 @@
|
||||
<script context="module" lang="ts">
|
||||
export enum ProviderTypes {
|
||||
Email = 'email',
|
||||
Sms = 'sms',
|
||||
Push = 'push'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let type: ProviderTypes;
|
||||
export let noIcon = false;
|
||||
export let size: 's' | 'm' | 'l' = 'm';
|
||||
|
||||
let icon = '';
|
||||
let text = '';
|
||||
|
||||
switch (type) {
|
||||
case ProviderTypes.Email:
|
||||
icon = 'icon-mail';
|
||||
text = 'Email';
|
||||
break;
|
||||
case ProviderTypes.Sms:
|
||||
icon = 'icon-annotation';
|
||||
text = 'SMS';
|
||||
break;
|
||||
case ProviderTypes.Push:
|
||||
icon = 'icon-device-mobile';
|
||||
text = 'Push';
|
||||
break;
|
||||
}
|
||||
|
||||
let textSize = '1.25rem';
|
||||
switch (size) {
|
||||
case 's':
|
||||
textSize = '1rem';
|
||||
break;
|
||||
case 'l':
|
||||
textSize = '1.5rem';
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if text === ''}
|
||||
Invalid provider type
|
||||
{:else}
|
||||
<div class="u-inline-flex u-cross-center u-gap-8">
|
||||
{#if !noIcon}
|
||||
<div
|
||||
class="avatar"
|
||||
class:is-size-large={size === 'l'}
|
||||
class:is-size-small={size === 's'}>
|
||||
<span class={icon} style:--p-text-size={textSize} aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
<slot>
|
||||
{text}
|
||||
</slot>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,240 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { Providers } from './provider.svelte';
|
||||
import { ProviderTypes } from './providerType.svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const showCreate = writable(false);
|
||||
|
||||
export const columns = writable<Column[]>([
|
||||
{ id: '$id', title: 'Message ID', type: 'string', show: true, width: 140 },
|
||||
{ id: 'description', title: 'Description', type: 'string', show: true, width: 140 },
|
||||
{ id: 'message', title: 'Message', type: 'string', show: false, width: 140 },
|
||||
{ id: 'providerType', title: 'Type', type: 'string', show: true, width: 100 },
|
||||
{ id: 'status', title: 'Status', type: 'string', show: true, width: 120 },
|
||||
{ id: 'scheduledAt', title: 'Scheduled at', type: 'datetime', show: true, width: 120 },
|
||||
{ id: 'deliveredAt', title: 'Delivered at', type: 'datetime', show: false, width: 120 }
|
||||
]);
|
||||
|
||||
// TODO: remove this when the SDK and API are ready
|
||||
export type Message = {
|
||||
$id: string;
|
||||
$createdAt: string;
|
||||
$updatedAt: string;
|
||||
topics: string[];
|
||||
users: string[];
|
||||
targets: string[];
|
||||
scheduledAt: string;
|
||||
deliveredAt: string;
|
||||
deliveryErrors: string[];
|
||||
deliveredTo: number;
|
||||
status: string;
|
||||
providerType: ProviderTypes;
|
||||
description: string;
|
||||
data: {
|
||||
content?: string;
|
||||
body?: string;
|
||||
subject?: string;
|
||||
title?: string;
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: remove this when the SDK and API are ready
|
||||
export const messages: { messages: Message[]; total: number } = {
|
||||
messages: [
|
||||
{
|
||||
$id: '637a40ba7a703e3936e1',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
description: 'Welcome',
|
||||
providerType: ProviderTypes.Push,
|
||||
topics: [],
|
||||
users: ['user-1', 'user-2'],
|
||||
targets: [],
|
||||
scheduledAt: '2021-03-01T00:00:00.000Z',
|
||||
deliveredAt: '2021-03-01T00:00:00.000Z',
|
||||
deliveryErrors: [],
|
||||
deliveredTo: 2,
|
||||
status: 'sent',
|
||||
data: {
|
||||
title: 'Welcome to the Cloud',
|
||||
body: 'Detailed body'
|
||||
}
|
||||
},
|
||||
{
|
||||
$id: '637a40ba7a703e3936e2',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
description: 'Public beta announcement',
|
||||
providerType: ProviderTypes.Sms,
|
||||
topics: [],
|
||||
users: ['user-1', 'user-2'],
|
||||
targets: [],
|
||||
scheduledAt: '2021-03-01T00:00:00.000Z',
|
||||
deliveredAt: '2021-03-01T00:00:00.000Z',
|
||||
deliveryErrors: [],
|
||||
deliveredTo: 2,
|
||||
status: 'scheduled',
|
||||
data: {
|
||||
content: 'Cloud is live on public beta'
|
||||
}
|
||||
},
|
||||
{
|
||||
$id: '637a40ba7a703e3936e3',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
description: 'Welcome',
|
||||
providerType: ProviderTypes.Email,
|
||||
topics: [],
|
||||
users: ['user-1', 'user-2'],
|
||||
targets: [],
|
||||
scheduledAt: '',
|
||||
deliveredAt: '',
|
||||
deliveryErrors: [],
|
||||
deliveredTo: 2,
|
||||
status: 'draft',
|
||||
data: {
|
||||
subject: 'Welcome to the Cloud',
|
||||
content: 'Detailed content'
|
||||
}
|
||||
}
|
||||
],
|
||||
total: 3
|
||||
};
|
||||
|
||||
// TODO: remove this when the SDK and API are ready
|
||||
export type Provider = {
|
||||
$id: string;
|
||||
$createdAt: string;
|
||||
$updatedAt: string;
|
||||
name: string;
|
||||
provider: Providers;
|
||||
default: boolean;
|
||||
enabled: boolean;
|
||||
type: ProviderTypes;
|
||||
credentials: object;
|
||||
options: object;
|
||||
};
|
||||
|
||||
// TODO: remove this when the SDK and API are ready
|
||||
export const providersById: { [providerId: string]: Provider } = {
|
||||
'637a40ba7a703e3936e1': {
|
||||
$id: '637a40ba7a703e3936e1',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Firebase',
|
||||
provider: Providers.FCM,
|
||||
type: ProviderTypes.Push,
|
||||
default: false,
|
||||
enabled: true,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e2': {
|
||||
$id: '637a40ba7a703e3936e2',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My APNS',
|
||||
provider: Providers.APNS,
|
||||
type: ProviderTypes.Push,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e3': {
|
||||
$id: '637a40ba7a703e3936e3',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My MQTT',
|
||||
provider: Providers.MQTT,
|
||||
type: ProviderTypes.Push,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e4': {
|
||||
$id: '637a40ba7a703e3936e4',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Sendgrid',
|
||||
provider: Providers.Sendgrid,
|
||||
type: ProviderTypes.Email,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e5': {
|
||||
$id: '637a40ba7a703e3936e5',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Mailgun',
|
||||
provider: Providers.Mailgun,
|
||||
type: ProviderTypes.Email,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e6': {
|
||||
$id: '637a40ba7a703e3936e6',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Telesign',
|
||||
provider: Providers.Telesign,
|
||||
type: ProviderTypes.Sms,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e7': {
|
||||
$id: '637a40ba7a703e3936e7',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Msg91',
|
||||
provider: Providers.Msg91,
|
||||
type: ProviderTypes.Sms,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e8': {
|
||||
$id: '637a40ba7a703e3936e8',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Textmagic',
|
||||
provider: Providers.Textmagic,
|
||||
type: ProviderTypes.Sms,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936e9': {
|
||||
$id: '637a40ba7a703e3936e9',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Vonage',
|
||||
provider: Providers.Vonage,
|
||||
type: ProviderTypes.Sms,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
},
|
||||
'637a40ba7a703e3936f0': {
|
||||
$id: '637a40ba7a703e3936f0',
|
||||
$createdAt: '2021-08-31T12:00:00.000Z',
|
||||
$updatedAt: '2021-08-31T12:00:00.000Z',
|
||||
name: 'My Twilio',
|
||||
provider: Providers.Twilio,
|
||||
type: ProviderTypes.Sms,
|
||||
default: false,
|
||||
enabled: false,
|
||||
credentials: {},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user