mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #906 from appwrite/feat-messaging-topics-overview
Update topic pages
This commit is contained in:
11 files changed
+207
-218
No files matched your search
@@ -1,14 +1,178 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import {
|
||||
Empty,
|
||||
EmptySearch,
|
||||
SearchQuery,
|
||||
PaginationWithLimit,
|
||||
Heading,
|
||||
ViewSelector,
|
||||
EmptyFilter
|
||||
} from '$lib/components';
|
||||
import { Container } from '$lib/layout';
|
||||
import DangerZone from './dangerZone.svelte';
|
||||
import Details from './details.svelte';
|
||||
import UpdateName from './updateName.svelte';
|
||||
import UpdatePermissions from './updatePermissions.svelte';
|
||||
import { ID, type Models } from '@appwrite.io/console';
|
||||
import type { PageData } from './$types';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import Table from './table.svelte';
|
||||
import { targetsById } from '../../store';
|
||||
import UserTargetsModal from '../../userTargetsModal.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { Filters, hasPageQueries } from '$lib/components/filters';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
|
||||
export let data: PageData;
|
||||
let showAdd = false;
|
||||
let subscribersByTargetId: Record<string, Models.Subscriber> = {};
|
||||
const columns = writable<Column[]>([
|
||||
{ id: '$id', title: 'Subscriber ID', type: 'string', show: true, width: 140 },
|
||||
{ id: 'userName', title: 'Name', type: 'string', show: true, filter: false, width: 100 },
|
||||
{ id: 'targetId', title: 'Target ID', type: 'string', show: true, width: 140 },
|
||||
{ id: 'target', title: 'Target', type: 'string', show: true, filter: false, width: 140 },
|
||||
{ id: 'type', title: 'Type', type: 'string', show: true, width: 80 },
|
||||
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 100 }
|
||||
]);
|
||||
|
||||
onMount(() => {
|
||||
$targetsById = {};
|
||||
for (const subscriber of $page.data.subscribers.subscribers) {
|
||||
const { target } = subscriber;
|
||||
$targetsById[target.$id] = target;
|
||||
subscribersByTargetId[target.$id] = subscriber;
|
||||
}
|
||||
});
|
||||
|
||||
async function addTargets(event: CustomEvent<Record<string, Models.Target>>) {
|
||||
showAdd = false;
|
||||
$targetsById = event.detail;
|
||||
|
||||
const targetIds = Object.keys($targetsById).filter(
|
||||
(targetId) => !(targetId in subscribersByTargetId)
|
||||
);
|
||||
const promises = targetIds.map(async (targetId) => {
|
||||
const subscriber = await sdk.forProject.messaging.createSubscriber(
|
||||
$page.params.topic,
|
||||
ID.unique(),
|
||||
targetId
|
||||
);
|
||||
subscribersByTargetId[targetId] = subscriber;
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Added ${targetIds.length} subscriber${
|
||||
targetIds.length > 1 ? 's' : ''
|
||||
} to topic`
|
||||
});
|
||||
trackEvent(Submit.MessagingTopicSubscriberAdd);
|
||||
await invalidate(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.MessagingTopicSubscriberAdd);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Details />
|
||||
<UpdateName />
|
||||
<UpdatePermissions />
|
||||
<DangerZone />
|
||||
<div class="u-flex u-flex-vertical">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<Heading tag="h2" size="5">Subscribers</Heading>
|
||||
<div class="is-only-mobile">
|
||||
<Button on:click={() => (showAdd = true)} event="create_subscriber">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add subscriber</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: fix width of search input in mobile -->
|
||||
<SearchQuery
|
||||
search={data.search}
|
||||
placeholder="Search by subscriber ID, target ID, user ID, or type">
|
||||
<div class="u-flex u-gap-16 is-not-mobile">
|
||||
<Filters query={data.query} {columns} />
|
||||
<ViewSelector
|
||||
view={View.Table}
|
||||
{columns}
|
||||
hideView
|
||||
allowNoColumns
|
||||
showColsTextMobile />
|
||||
<Button on:click={() => (showAdd = true)} event="create_subscriber">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add subscriber</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={View.Table}
|
||||
{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.subscribers.total}
|
||||
<Table columns={$columns} {data} />
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Subscribers"
|
||||
limit={data.limit}
|
||||
offset={data.offset}
|
||||
total={data.subscribers.total} />
|
||||
{:else if $hasPageQueries}
|
||||
<EmptyFilter resource="subscribers" />
|
||||
{:else if data.search}
|
||||
<EmptySearch>
|
||||
<div class="u-text-center">
|
||||
<b>Sorry, we couldn't find '{data.search}'</b>
|
||||
<p>There are no subscribers that match your search.</p>
|
||||
</div>
|
||||
<Button
|
||||
secondary
|
||||
href={`/console/project-${$page.params.project}/messaging/topics/topic-${$page.params.topic}/subscribers`}>
|
||||
Clear Search
|
||||
</Button>
|
||||
</EmptySearch>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
on:click={() => (showAdd = true)}
|
||||
href="https://appwrite.io/docs/products/messaging/topics#subscribe-targets-to-topics"
|
||||
target="subscriber" />
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<UserTargetsModal
|
||||
title="Select subscribers"
|
||||
bind:show={showAdd}
|
||||
bind:targetsById={$targetsById}
|
||||
on:update={addTargets}>
|
||||
<svelte:fragment slot="description">
|
||||
<p class="text">
|
||||
Add subscribers to this topic by selecting the targets for directing messages. <a
|
||||
href="https://appwrite.io/docs/products/messaging/topics#subscribe-targets-to-topics"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more about subscribers</a
|
||||
>.
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
</UserTargetsModal>
|
||||
File renamed without changes.
@@ -11,11 +11,6 @@
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Overview',
|
||||
event: 'overview'
|
||||
},
|
||||
{
|
||||
href: `${path}/subscribers`,
|
||||
title: 'Subscribers',
|
||||
event: 'subscribers'
|
||||
},
|
||||
@@ -23,6 +18,11 @@
|
||||
href: `${path}/activity`,
|
||||
title: 'Activity',
|
||||
event: 'activity'
|
||||
},
|
||||
{
|
||||
href: `${path}/settings`,
|
||||
title: 'Settings',
|
||||
event: 'settings'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
import DangerZone from './dangerZone.svelte';
|
||||
import Details from './details.svelte';
|
||||
import UpdateName from './updateName.svelte';
|
||||
import UpdatePermissions from './updatePermissions.svelte';
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Details />
|
||||
<UpdateName />
|
||||
<UpdatePermissions />
|
||||
<DangerZone />
|
||||
</Container>
|
||||
+6
-16
@@ -1,20 +1,10 @@
|
||||
<script lang="ts" context="module">
|
||||
import { get } from 'svelte/store';
|
||||
let showDelete = writable(false);
|
||||
export const promptDeleteUser = (id: string) => {
|
||||
showDelete.set(true);
|
||||
goto(`/console/project-${get(project).$id}/auth/user-${id}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { CardGrid, BoxAvatar, Heading } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { writable } from 'svelte/store';
|
||||
import { topic, topicTotal } from './store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { project } from '$routes/console/project-[project]/store';
|
||||
import DeleteTopic from './deleteTopic.svelte';
|
||||
import { topic, topicTotal } from '../store';
|
||||
import DeleteTopic from '../deleteTopic.svelte';
|
||||
|
||||
let showDelete = false;
|
||||
</script>
|
||||
|
||||
<CardGrid danger>
|
||||
@@ -37,9 +27,9 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary on:click={() => ($showDelete = true)} event="delete_messaging_provider"
|
||||
<Button secondary on:click={() => (showDelete = true)} event="delete_messaging_topic"
|
||||
>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
<DeleteTopic bind:showDelete={$showDelete} />
|
||||
<DeleteTopic bind:showDelete />
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { topic, topicTotal } from './store';
|
||||
import { topic, topicTotal } from '../store';
|
||||
</script>
|
||||
|
||||
<CardGrid>
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { onMount } from 'svelte';
|
||||
import { topic } from './store';
|
||||
import { topic } from '../store';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { trackEvent, Submit, trackError } from '$lib/actions/analytics';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { Roles } from '$lib/components/permissions';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { topic } from './store';
|
||||
import { topic } from '../store';
|
||||
|
||||
const topicId = $page.params.topic;
|
||||
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import {
|
||||
Empty,
|
||||
EmptySearch,
|
||||
SearchQuery,
|
||||
PaginationWithLimit,
|
||||
Heading,
|
||||
ViewSelector,
|
||||
EmptyFilter
|
||||
} from '$lib/components';
|
||||
import { Container } from '$lib/layout';
|
||||
import { ID, type Models } from '@appwrite.io/console';
|
||||
import type { PageData } from './$types';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import Table from './table.svelte';
|
||||
import { targetsById } from '../../../store';
|
||||
import UserTargetsModal from '../../../userTargetsModal.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { Filters, hasPageQueries } from '$lib/components/filters';
|
||||
import { columns } from './store';
|
||||
import { View } from '$lib/helpers/load';
|
||||
|
||||
export let data: PageData;
|
||||
let showAdd = false;
|
||||
let subscribersByTargetId: Record<string, Models.Subscriber> = {};
|
||||
|
||||
onMount(() => {
|
||||
$targetsById = {};
|
||||
for (const subscriber of $page.data.subscribers.subscribers) {
|
||||
const { target } = subscriber;
|
||||
$targetsById[target.$id] = target;
|
||||
subscribersByTargetId[target.$id] = subscriber;
|
||||
}
|
||||
});
|
||||
|
||||
async function addTargets(event: CustomEvent<Record<string, Models.Target>>) {
|
||||
showAdd = false;
|
||||
$targetsById = event.detail;
|
||||
|
||||
const targetIds = Object.keys($targetsById).filter(
|
||||
(targetId) => !(targetId in subscribersByTargetId)
|
||||
);
|
||||
const promises = targetIds.map(async (targetId) => {
|
||||
const subscriber = await sdk.forProject.messaging.createSubscriber(
|
||||
$page.params.topic,
|
||||
ID.unique(),
|
||||
targetId
|
||||
);
|
||||
subscribersByTargetId[targetId] = subscriber;
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Added ${targetIds.length} subscriber${
|
||||
targetIds.length > 1 ? 's' : ''
|
||||
} to topic`
|
||||
});
|
||||
trackEvent(Submit.MessagingTopicSubscriberAdd);
|
||||
await invalidate(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
trackError(error, Submit.MessagingTopicSubscriberAdd);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-flex-vertical">
|
||||
<div class="u-flex u-main-space-between">
|
||||
<Heading tag="h2" size="5">Subscribers</Heading>
|
||||
<div class="is-only-mobile">
|
||||
<Button on:click={() => (showAdd = true)} event="create_subscriber">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add subscriber</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: fix width of search input in mobile -->
|
||||
<SearchQuery
|
||||
search={data.search}
|
||||
placeholder="Search by subscriber ID, target ID, user ID, or type">
|
||||
<div class="u-flex u-gap-16 is-not-mobile">
|
||||
<Filters query={data.query} {columns} />
|
||||
<ViewSelector
|
||||
view={View.Table}
|
||||
{columns}
|
||||
hideView
|
||||
allowNoColumns
|
||||
showColsTextMobile />
|
||||
<Button on:click={() => (showAdd = true)} event="create_subscriber">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Add subscriber</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={View.Table}
|
||||
{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.subscribers.total}
|
||||
<Table {data} />
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Subscribers"
|
||||
limit={data.limit}
|
||||
offset={data.offset}
|
||||
total={data.subscribers.total} />
|
||||
{:else if $hasPageQueries}
|
||||
<EmptyFilter resource="subscribers" />
|
||||
{:else if data.search}
|
||||
<EmptySearch>
|
||||
<div class="u-text-center">
|
||||
<b>Sorry, we couldn't find '{data.search}'</b>
|
||||
<p>There are no subscribers that match your search.</p>
|
||||
</div>
|
||||
<Button
|
||||
secondary
|
||||
href={`/console/project-${$page.params.project}/messaging/topics/topic-${$page.params.topic}/subscribers`}>
|
||||
Clear Search
|
||||
</Button>
|
||||
</EmptySearch>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
on:click={() => (showAdd = true)}
|
||||
href="https://appwrite.io/docs/products/messaging/topics#subscribe-targets-to-topics"
|
||||
target="subscriber" />
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<UserTargetsModal
|
||||
title="Select subscribers"
|
||||
bind:show={showAdd}
|
||||
bind:targetsById={$targetsById}
|
||||
on:update={addTargets}>
|
||||
<svelte:fragment slot="description">
|
||||
<p class="text">
|
||||
Add subscribers to this topic by selecting the targets for directing messages. <a
|
||||
href="https://appwrite.io/docs/products/messaging/topics#subscribe-targets-to-topics"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more about subscribers</a
|
||||
>.
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
</UserTargetsModal>
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const columns = writable<Column[]>([
|
||||
{ id: '$id', title: 'Subscriber ID', type: 'string', show: true, width: 140 },
|
||||
{ id: 'userName', title: 'Name', type: 'string', show: true, filter: false, width: 100 },
|
||||
{ id: 'targetId', title: 'Target ID', type: 'string', show: true, width: 140 },
|
||||
{ id: 'target', title: 'Target', type: 'string', show: true, filter: false, width: 140 },
|
||||
{ id: 'type', title: 'Type', type: 'string', show: true, width: 80 },
|
||||
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 100 }
|
||||
]);
|
||||
+7
-6
@@ -18,15 +18,16 @@
|
||||
} from '$lib/elements/table';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import type { PageData } from './$types';
|
||||
import { columns } from './store';
|
||||
import ProviderType from '../../../providerType.svelte';
|
||||
import ProviderType from '../../providerType.svelte';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { project } from '$routes/console/project-[project]/store';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { page } from '$app/stores';
|
||||
import { targetsById } from '../../../store';
|
||||
import { targetsById } from '../../store';
|
||||
import { MessagingProviderType, type Models } from '@appwrite.io/console';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
|
||||
export let columns: Column[];
|
||||
export let data: PageData;
|
||||
|
||||
let subscribers: Record<string, Models.Subscriber> = {};
|
||||
@@ -84,7 +85,7 @@
|
||||
<TableCellHeadCheck
|
||||
bind:selected={selectedIds}
|
||||
pageItemsIds={data.subscribers.subscribers.map((d) => d.$id)} />
|
||||
{#each $columns as column}
|
||||
{#each columns as column}
|
||||
{#if column.show}
|
||||
<TableCellHead width={column.width}>{column.title}</TableCellHead>
|
||||
{/if}
|
||||
@@ -97,10 +98,10 @@
|
||||
href={`${base}/console/project-${$project.$id}/auth/user-${subscriber.target.userId}`}>
|
||||
<TableCellCheck bind:selectedIds id={subscriber.$id} />
|
||||
|
||||
{#each $columns as column}
|
||||
{#each columns as column}
|
||||
{#if column.show}
|
||||
{#if column.id === '$id'}
|
||||
{#key $columns}
|
||||
{#key column.id}
|
||||
<TableCell title="Subscriber ID">
|
||||
<Id value={subscriber.$id}>
|
||||
{subscriber.$id}
|
||||
Reference in new issue
Block a user