mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
merge main into fix-table-content-truncated.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { members, organization } from '$lib/stores/organization';
|
||||
import { projects } from '../store';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -42,6 +43,8 @@
|
||||
}
|
||||
|
||||
$: avatars = $members.memberships.map((team) => team.userName);
|
||||
$: orgProjects = `${$projects.total} ${$projects.total === 1 ? 'project' : 'projects'}`;
|
||||
$: orgMembers = `${$organization.total} ${$organization.total === 1 ? 'member' : 'members'}`;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -89,7 +92,7 @@
|
||||
<svelte:fragment slot="title">
|
||||
<h6 class="u-bold u-trim-1" data-private>{$organization.name}</h6>
|
||||
</svelte:fragment>
|
||||
<p>{$organization.total} members</p>
|
||||
<p>{orgMembers}, {orgProjects}</p>
|
||||
</BoxAvatar>
|
||||
</svelte:fragment>
|
||||
|
||||
|
||||
+178
-28
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Alert, Modal, SecondaryTabs, SecondaryTabsItem } from '$lib/components';
|
||||
import { Button, FormList, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { organization, organizationList } from '$lib/stores/organization';
|
||||
import { members, organization, organizationList } from '$lib/stores/organization';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -12,11 +12,28 @@
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { isCloud } from '$lib/system';
|
||||
import type { InvoiceList } from '$lib/sdk/billing';
|
||||
import {
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { tierToPlan } from '$lib/stores/billing';
|
||||
|
||||
export let showDelete = false;
|
||||
export let invoices: InvoiceList;
|
||||
let error: string = null;
|
||||
|
||||
let selectedTab = 'projects';
|
||||
let organizationName: string = null;
|
||||
|
||||
/* enable overflow-x */
|
||||
const columnWidth = 120;
|
||||
const columnWidthSmall = columnWidth / 4;
|
||||
|
||||
async function deleteOrg() {
|
||||
try {
|
||||
if (isCloud) {
|
||||
@@ -46,34 +63,167 @@
|
||||
}
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'projects',
|
||||
label: { desktop: 'Projects', mobile: 'Projects' },
|
||||
total: $projects.total
|
||||
},
|
||||
{
|
||||
name: 'members',
|
||||
label: { desktop: 'Total Members', mobile: 'Members' },
|
||||
total: $members.total
|
||||
}
|
||||
];
|
||||
|
||||
$: tabData =
|
||||
selectedTab === 'projects'
|
||||
? {
|
||||
headers: ['Project', '', 'Last Updated'],
|
||||
rows: $projects.projects.map((project) => ({
|
||||
cells: [project.name, '', toLocaleDate(project.$updatedAt)]
|
||||
}))
|
||||
}
|
||||
: {
|
||||
headers: ['Member', '', 'Last Activity'],
|
||||
rows: $members.memberships.map((member) => ({
|
||||
cells: [member.userName, '', toLocaleDate(member.$updatedAt)]
|
||||
}))
|
||||
};
|
||||
|
||||
$: upcomingInvoice = invoices?.invoices.find((i) => i.status === 'upcoming' && i.amount > 0);
|
||||
|
||||
$: if (!showDelete) {
|
||||
// reset on close.
|
||||
organizationName = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete organization"
|
||||
onSubmit={deleteOrg}
|
||||
bind:show={showDelete}
|
||||
bind:error
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
{#if upcomingInvoice}
|
||||
<p data-private>
|
||||
The organization <b>{$organization.name}</b> will be flagged for deletion.
|
||||
</p>
|
||||
<div class="max-height-dialog">
|
||||
<Modal
|
||||
title="Delete organization"
|
||||
onSubmit={deleteOrg}
|
||||
bind:show={showDelete}
|
||||
bind:error
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
{#if upcomingInvoice}
|
||||
<Alert type="warning">
|
||||
<span slot="title">
|
||||
You have a pending {formatCurrency(upcomingInvoice.grossAmount)} invoice for your
|
||||
{tierToPlan(upcomingInvoice.plan).name} plan
|
||||
</span>
|
||||
<p>
|
||||
By proceeding, your invoice will be processed within the hour. Upon successful
|
||||
payment, your organization will be deleted.
|
||||
</p>
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
<p data-private>
|
||||
All existing projects will be paused and the organization will be deleted once your
|
||||
upcoming invoice is processed on {toLocaleDate($organization.billingNextInvoiceDate)}.
|
||||
{#if $projects.total > 0}
|
||||
The following projects and all data associated with <b>{$organization.name}</b> will
|
||||
be permanently deleted. <b>This action is irreversible</b>.
|
||||
{:else}
|
||||
All data associated with <b>{$organization.name}</b> will be permanently deleted.
|
||||
<b>This action is irreversible</b>.
|
||||
{/if}
|
||||
</p>
|
||||
{:else}
|
||||
<p data-private>
|
||||
Are you sure you want to delete <b>{$organization.name}</b>? All projects ({$projects.total})
|
||||
and data associated with this organization will be deleted. This action is irreversible.
|
||||
</p>
|
||||
{/if}
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
<Button secondary submit>Delete</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
{#if $projects.total > 0}
|
||||
<div class="box is-only-desktop">
|
||||
<SecondaryTabs large stretch class="u-sep-block-end u-padding-8">
|
||||
{#each tabs as { name, label, total }}
|
||||
<SecondaryTabsItem
|
||||
center
|
||||
fullWidth
|
||||
disabled={selectedTab === name}
|
||||
on:click={() => (selectedTab = name)}>
|
||||
{label.desktop} ({total})
|
||||
</SecondaryTabsItem>
|
||||
{/each}
|
||||
</SecondaryTabs>
|
||||
|
||||
<TableScroll dense noMargin>
|
||||
<TableHeader>
|
||||
{#each tabData.headers as header}
|
||||
<TableCellHead width={columnWidth}>{header}</TableCellHead>
|
||||
{/each}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each tabData.rows as row}
|
||||
<TableRow>
|
||||
{#each row.cells as cell}
|
||||
<TableCell width={columnWidth}>{cell}</TableCell>
|
||||
{/each}
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
</div>
|
||||
<div class="box is-not-desktop">
|
||||
<SecondaryTabs large stretch class="u-sep-block-end u-padding-8">
|
||||
{#each tabs as { name, label, total }}
|
||||
<SecondaryTabsItem
|
||||
center
|
||||
fullWidth
|
||||
disabled={selectedTab === name}
|
||||
on:click={() => (selectedTab = name)}>
|
||||
{label.mobile} ({total})
|
||||
</SecondaryTabsItem>
|
||||
{/each}
|
||||
</SecondaryTabs>
|
||||
|
||||
<TableScroll dense noMargin>
|
||||
<TableHeader>
|
||||
{#each tabData.headers as header, index}
|
||||
<TableCellHead width={index === 1 ? columnWidthSmall : columnWidth}
|
||||
>{header}</TableCellHead>
|
||||
{/each}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each tabData.rows as row}
|
||||
<TableRow>
|
||||
{#each row.cells as cell, index}
|
||||
<TableCell width={index === 1 ? columnWidthSmall : columnWidth}
|
||||
>{cell}</TableCell>
|
||||
{/each}
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<FormList>
|
||||
<InputText
|
||||
label={`Confirm the organization name to continue`}
|
||||
placeholder="Enter {$organization.name} to continue"
|
||||
id="organization-name"
|
||||
required
|
||||
bind:value={organizationName} />
|
||||
</FormList>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
<Button
|
||||
secondary
|
||||
submit
|
||||
disabled={!error && (!organizationName || organizationName !== $organization.name)}>
|
||||
Delete
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
padding: unset;
|
||||
border-radius: var(--border-radius-medium, 8px);
|
||||
}
|
||||
|
||||
:global(.max-height-dialog dialog) {
|
||||
max-height: 650px;
|
||||
}
|
||||
</style>
|
||||
|
||||
+11
-2
@@ -30,6 +30,7 @@
|
||||
import { attributes, collection, columns } from './store';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
import type { ColumnType } from '$lib/helpers/types';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -169,6 +170,8 @@
|
||||
<TableCellHead eyebrow={false}>{column.title}</TableCellHead>
|
||||
{/if}
|
||||
{/each}
|
||||
<TableCellHead eyebrow={false}>Created</TableCellHead>
|
||||
<TableCellHead eyebrow={false}>Updated</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each data.documents.documents as document}
|
||||
@@ -250,6 +253,12 @@
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
<TableCellText>
|
||||
{toLocaleDateTime(document.$createdAt)}
|
||||
</TableCellText>
|
||||
<TableCellText>
|
||||
{toLocaleDateTime(document.$updatedAt)}
|
||||
</TableCellText>
|
||||
</TableRowLink>
|
||||
{/each}
|
||||
</TableBody>
|
||||
@@ -338,8 +347,8 @@
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)} disabled={deleting}>Cancel</Button>
|
||||
<Button secondary submit disabled={deleting || (relAttributes?.length && !checked)}
|
||||
>Delete
|
||||
<Button secondary submit disabled={deleting || (relAttributes?.length && !checked)}>
|
||||
Delete
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user