Skip to content

Commit

Permalink
Merge pull request #1302 from ItzNotABug/org-deletion-new-flow
Browse files Browse the repository at this point in the history
New Organization Deletion Flow
  • Loading branch information
ernstmul authored Oct 3, 2024
2 parents ab67b7f + a66e46d commit 6748b14
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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) {
Expand Down Expand Up @@ -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)}.
</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.
{#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>
{/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>

0 comments on commit 6748b14

Please sign in to comment.