mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix table for users
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
<article class="card">
|
||||
<article class="card common-section">
|
||||
<slot />
|
||||
</article>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
aria-label="Close Modal"
|
||||
title="Close Modal"
|
||||
on:click={closeModal}>
|
||||
<span class="icon-cancel" aria-hidden="true" />
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</header>
|
||||
<div class="modal-content">
|
||||
|
||||
@@ -7,3 +7,9 @@
|
||||
<slot />
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<style>
|
||||
th {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,3 +4,16 @@ export const toLocaleDate = (timestamp: number) => {
|
||||
|
||||
return date.toLocaleDateString('en', options);
|
||||
};
|
||||
|
||||
export const toLocaleDateTime = (timestamp: number) => {
|
||||
const date = new Date(timestamp * 1000);
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
};
|
||||
|
||||
return date.toLocaleDateString('en', options);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import type { Models } from 'src/sdk';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import { base } from '$app/paths';
|
||||
import { usersList } from './store';
|
||||
@@ -26,13 +26,14 @@
|
||||
let showCreate = false;
|
||||
let search = '';
|
||||
let offset: number = null;
|
||||
const limit = 5;
|
||||
|
||||
const limit = 5;
|
||||
const project = $page.params.project;
|
||||
const getAvatar = (name: string) => sdkForProject.avatars.getInitials(name, 30, 30).toString();
|
||||
const userCreated = async (event: CustomEvent<Models.User<Record<string, unknown>>>) => {
|
||||
await goto(`${base}/console/${project}/users/user/${event.detail.$id}`);
|
||||
};
|
||||
|
||||
$: usersList.load(search, limit, offset);
|
||||
$: if (search) offset = 0;
|
||||
$: {
|
||||
@@ -65,7 +66,8 @@
|
||||
<TableCellHead width={30} />
|
||||
<TableCellHead>Name</TableCellHead>
|
||||
<TableCellHead>E-Mail</TableCellHead>
|
||||
<TableCellHead>Status</TableCellHead>
|
||||
<TableCellHead width={100}>Status</TableCellHead>
|
||||
<TableCellHead width={100}>ID</TableCellHead>
|
||||
<TableCellHead>Joined</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -88,12 +90,19 @@
|
||||
</TableCellLink>
|
||||
<TableCellText title="E-Mail">{user.email}</TableCellText>
|
||||
<TableCellText title="Status">
|
||||
<Pill success={user.emailVerification}>
|
||||
{user.emailVerification ? 'Verified' : 'Unverified'}
|
||||
</Pill>
|
||||
{#if user.status}
|
||||
<Pill success={user.emailVerification}>
|
||||
{user.emailVerification ? 'Verified' : 'Unverified'}
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill danger>Blocked</Pill>
|
||||
{/if}
|
||||
</TableCellText>
|
||||
<TableCellText title="ID">
|
||||
<Pill>User ID <i class="icon-duplicate" /></Pill>
|
||||
</TableCellText>
|
||||
<TableCellText title="Joined">
|
||||
{toLocaleDate(user.registration)}
|
||||
{toLocaleDateTime(user.registration)}
|
||||
</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
|
||||
@@ -8,7 +8,12 @@ export type UsersList = {
|
||||
response?: Models.UserList<Record<string, unknown>>;
|
||||
};
|
||||
|
||||
function createUserStore() {
|
||||
export type TeamsList = {
|
||||
loading: boolean;
|
||||
response?: Models.TeamList;
|
||||
};
|
||||
|
||||
function createUsersListStore() {
|
||||
const { subscribe, set } = writable<UsersList>({
|
||||
loading: true,
|
||||
response: browser ? JSON.parse(sessionStorage.getItem('users')) : null
|
||||
@@ -38,8 +43,40 @@ function createUserStore() {
|
||||
};
|
||||
}
|
||||
|
||||
export const usersList = createUserStore();
|
||||
function createTeamsListStore() {
|
||||
const { subscribe, set } = writable<TeamsList>({
|
||||
loading: true,
|
||||
response: browser ? JSON.parse(sessionStorage.getItem('teams')) : null
|
||||
});
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
set,
|
||||
load: async (search: string, limit: number, offset: number) => {
|
||||
try {
|
||||
const response = await sdkForProject.teams.list(
|
||||
search,
|
||||
limit,
|
||||
offset,
|
||||
undefined,
|
||||
undefined,
|
||||
'DESC'
|
||||
);
|
||||
set({
|
||||
loading: false,
|
||||
response
|
||||
});
|
||||
} catch (error) {
|
||||
//TODO: take care what happens here
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const usersList = createUsersListStore();
|
||||
export const teamsList = createTeamsListStore();
|
||||
|
||||
if (browser) {
|
||||
usersList.subscribe((n) => sessionStorage?.setItem('users', JSON.stringify(n.response ?? '')));
|
||||
teamsList.subscribe((n) => sessionStorage?.setItem('teams', JSON.stringify(n.response ?? '')));
|
||||
}
|
||||
|
||||
@@ -11,14 +11,16 @@
|
||||
TableCellText,
|
||||
TableCell
|
||||
} from '$lib/elements/table';
|
||||
import { Button, InputSearch } from '$lib/elements/forms';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Empty, Pagination } from '$lib/components';
|
||||
import Create from './_createTeam.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import type { Models } from 'src/sdk';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { Container } from '$lib/layout';
|
||||
import { base } from '$app/paths';
|
||||
import { teamsList } from './store';
|
||||
import { onMount } from 'svelte';
|
||||
import type { Models } from 'src/sdk';
|
||||
|
||||
let search = '';
|
||||
let showCreate = false;
|
||||
@@ -30,68 +32,77 @@
|
||||
const teamCreated = async (event: CustomEvent<Models.Team>) => {
|
||||
await goto(`${base}/console/${project}/users/team/${event.detail.$id}`);
|
||||
};
|
||||
$: request = sdkForProject.teams.list(search, limit, offset);
|
||||
$: teamsList.load(search, limit, offset);
|
||||
$: if (search) offset = 0;
|
||||
$: {
|
||||
//TODO: refactor this into something maintainable without the use of goto
|
||||
if (offset !== null) {
|
||||
$page.url.searchParams.set('offset', offset.toString());
|
||||
goto(`?${$page.url.searchParams.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
offset = +$page.url.searchParams.get('offset') ?? 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<div class="u-flex u-space-between common-section">
|
||||
<InputSearch bind:value={search} />
|
||||
<div class="u-flex u-gap-12 common-section u-main-space-between">
|
||||
<div class="input-text-wrapper u-stretch" style="max-width: 500px">
|
||||
<input type="search" class="input-text" bind:value={search} />
|
||||
<span class="icon-search" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<Button on:click={() => (showCreate = true)}>
|
||||
<span class="icon-plus" aria-hidden="true" /> <span class="text">Create Team</span>
|
||||
</Button>
|
||||
</div>
|
||||
{#if $teamsList?.response?.total}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCellHead width={30} />
|
||||
<TableCellHead>Name</TableCellHead>
|
||||
<TableCellHead>Members</TableCellHead>
|
||||
<TableCellHead>Created</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each $teamsList.response.teams as team}
|
||||
<TableRow>
|
||||
<TableCell onlyDesktop>
|
||||
<div class="image">
|
||||
<img
|
||||
class="avatar"
|
||||
width="30"
|
||||
height="30"
|
||||
src={getAvatar(team.name)}
|
||||
alt={team.name} />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCellLink
|
||||
title="ID"
|
||||
href={`${base}/console/${project}/users/teams/${team.$id}`}>
|
||||
{team.name}
|
||||
</TableCellLink>
|
||||
<TableCellText title="Members">{team.total}</TableCellText>
|
||||
<TableCellText title="Members"
|
||||
>{toLocaleDate(team.dateCreated)}</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
{#await request}
|
||||
<div aria-busy="true" />
|
||||
{:then response}
|
||||
{#if response.total}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCellHead width={30} />
|
||||
<TableCellHead>Name</TableCellHead>
|
||||
<TableCellHead>Members</TableCellHead>
|
||||
<TableCellHead>Created</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each response.teams as team}
|
||||
<TableRow>
|
||||
<TableCell onlyDesktop>
|
||||
<div class="image">
|
||||
<img
|
||||
class="avatar"
|
||||
width="30"
|
||||
height="30"
|
||||
src={getAvatar(team.name)}
|
||||
alt={team.name} />
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCellLink
|
||||
title="ID"
|
||||
href={`${base}/console/${project}/users/teams/${team.$id}`}>
|
||||
{team.name}
|
||||
</TableCellLink>
|
||||
<TableCellText title="Members">{team.total}</TableCellText>
|
||||
<TableCellText title="Members"
|
||||
>{toLocaleDate(team.dateCreated)}</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Pagination {limit} bind:offset sum={response.total} />
|
||||
{:else if search}
|
||||
<Empty>
|
||||
<svelte:fragment slot="header"
|
||||
>No results found for <b>{search}</b></svelte:fragment>
|
||||
</Empty>
|
||||
{:else}
|
||||
<Empty>
|
||||
<svelte:fragment slot="header">No Teams Found</svelte:fragment>
|
||||
Create your first team to get started.
|
||||
</Empty>
|
||||
{/if}
|
||||
{/await}
|
||||
<Pagination {limit} bind:offset sum={$teamsList.response.total} />
|
||||
{:else if search}
|
||||
<Empty>
|
||||
<svelte:fragment slot="header">No results found for <b>{search}</b></svelte:fragment>
|
||||
</Empty>
|
||||
{:else}
|
||||
<Empty>
|
||||
<svelte:fragment slot="header">No Teams Found</svelte:fragment>
|
||||
Create your first team to get started.
|
||||
</Empty>
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
<Create bind:showCreate on:created={teamCreated} />
|
||||
|
||||
Reference in New Issue
Block a user