mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
various fixes
This commit is contained in:
+2
-2
@@ -21,9 +21,9 @@
|
||||
"dependencies": {
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@ae1eb53",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f29290",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332",
|
||||
"@appwrite.io/pink-legacy": "^1.0.1",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@35f29290",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@2ff4e332",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
|
||||
Generated
+14
-14
@@ -15,14 +15,14 @@ importers:
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
'@appwrite.io/pink-icons-svelte':
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f29290
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f29290(svelte@4.2.19)
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332(svelte@4.2.19)
|
||||
'@appwrite.io/pink-legacy':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@35f29290
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@35f29290(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@2ff4e332
|
||||
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@2ff4e332(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -215,14 +215,14 @@ packages:
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@ae1eb53}
|
||||
version: 1.2.1
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f29290':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f29290}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332}
|
||||
version: 1.0.0-next.7
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f2929064b9d126a377b6a3e85f59198c5901a2':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f2929064b9d126a377b6a3e85f59198c5901a2}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332a9fec20d8ed842170634ede486bda8df':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332a9fec20d8ed842170634ede486bda8df}
|
||||
version: 1.0.0-next.7
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -236,8 +236,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.1':
|
||||
resolution: {integrity: sha512-e5IZx2drqSxgl2WXvmr0eLfg/WiOCzuqQE8oksD+9GoFBkcL9HWvXul5GjFni3RBAN9XFRL3nUExGk4T0H8sFw==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@35f29290':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@35f29290}
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@2ff4e332':
|
||||
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@2ff4e332}
|
||||
version: 1.0.0-next.85
|
||||
peerDependencies:
|
||||
react-dom: ^18.0.0
|
||||
@@ -4099,11 +4099,11 @@ snapshots:
|
||||
|
||||
'@appwrite.io/console@https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@ae1eb53': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f29290(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332(svelte@4.2.19)':
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f2929064b9d126a377b6a3e85f59198c5901a2(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332a9fec20d8ed842170634ede486bda8df(svelte@4.2.19)':
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
@@ -4116,9 +4116,9 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@35f29290(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@2ff4e332(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@35f2929064b9d126a377b6a3e85f59198c5901a2(svelte@4.2.19)
|
||||
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@2ff4e332a9fec20d8ed842170634ede486bda8df(svelte@4.2.19)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.3(svelte@4.2.19))(svelte@4.2.19)
|
||||
'@melt-ui/svelte': 0.86.3(svelte@4.2.19)
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
|
||||
{#if total > 2}
|
||||
<Avatar {size}>
|
||||
+{total - 2}
|
||||
<span style:font-size="10px">
|
||||
+{total - 2}
|
||||
</span>
|
||||
</Avatar>
|
||||
{/if}
|
||||
</AvatarGroup>
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
<div class="u-flex-vertical u-gap-16">
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
color: hsl(var(--color-neutral-50));
|
||||
line-height: 1.25rem;
|
||||
&:first-child {
|
||||
color: hsl(var(--color-neutral-70));
|
||||
}
|
||||
}
|
||||
|
||||
:global(.theme-dark) div {
|
||||
color: hsl(var(--color-neutral-20));
|
||||
|
||||
&:first-child {
|
||||
color: hsl(var(--color-neutral-10));
|
||||
}
|
||||
max-width: 360px;
|
||||
text-wrap: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,58 +5,62 @@
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import { Badge, Layout, Link, Tag, Typography } from '@appwrite.io/pink-svelte';
|
||||
</script>
|
||||
|
||||
<Base>
|
||||
{#if isCloud}
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE}
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
<p>
|
||||
<span class="u-bold">Roles</span>
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<span class="inline-tag u-normal u-x-small">Pro plan</span>
|
||||
{/if}
|
||||
</p>
|
||||
<p>Owner, Developer, Editor, Analyst and Billing.</p>
|
||||
</div>
|
||||
<p>
|
||||
<Button link external href="https://appwrite.io/docs/advanced/platform/roles"
|
||||
>Learn more</Button> about roles.
|
||||
</p>
|
||||
{:else}
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
<p>
|
||||
<span class="u-bold">Roles</span>
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<span class="inline-tag u-normal u-x-small">Pro plan</span>
|
||||
{/if}
|
||||
</p>
|
||||
<p>
|
||||
<Layout.Stack>
|
||||
{#if isCloud}
|
||||
{#if $organization?.billingPlan !== BillingPlan.FREE}
|
||||
<Typography.Text variant="m-600">Roles</Typography.Text>
|
||||
<Typography.Text>Owner, Developer, Editor, Analyst and Billing.</Typography.Text>
|
||||
<Typography.Text>
|
||||
<Link.Anchor
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://appwrite.io/docs/advanced/platform/roles"
|
||||
>Learn more</Link.Anchor>
|
||||
</Typography.Text>
|
||||
{:else}
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Typography.Text variant="m-600">
|
||||
<span class="u-bold">Roles</span>
|
||||
</Typography.Text>
|
||||
<Badge variant="secondary" size="xs" content="Pro plan" />
|
||||
</Layout.Stack>
|
||||
<Typography.Text>
|
||||
Upgrade to Pro to assign new roles to members such as Owner, Developer, Editor
|
||||
or Analyst.
|
||||
</Typography.Text>
|
||||
|
||||
<p class="u-flex u-main-end u-cross-center u-gap-4">
|
||||
<Button
|
||||
size="s"
|
||||
text
|
||||
external
|
||||
href="https://appwrite.io/docs/advanced/platform/roles">Learn more</Button>
|
||||
<Button size="s" secondary external href={$upgradeURL}>Upgrade plan</Button>
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Typography.Text variant="m-600">
|
||||
<span class="u-bold">Roles</span>
|
||||
</Typography.Text>
|
||||
<Badge variant="secondary" size="xs" content="Cloud" />
|
||||
</Layout.Stack>
|
||||
<Typography.Text>
|
||||
Upgrade to Cloud to assign new roles to members or ask us about our enterprise self
|
||||
hosted offering.
|
||||
</Typography.Text>
|
||||
<p class="u-flex u-main-end u-cross-center u-gap-4">
|
||||
<Button text external href="https://appwrite.io/docs/advanced/platform/roles"
|
||||
>Learn more</Button>
|
||||
<Button secondary external href={$upgradeURL}>Upgrade plan</Button>
|
||||
<Button
|
||||
size="s"
|
||||
text
|
||||
external
|
||||
href="https://appwrite.io/docs/advanced/platform/roles">Learn more</Button>
|
||||
<Button size="s" secondary external href={$upgradeURL}>Upgrade to Cloud</Button>
|
||||
</p>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
<p>
|
||||
<span class="u-bold">Roles</span>
|
||||
<span class="inline-tag u-normal u-x-small">Appwrite Cloud</span>
|
||||
</p>
|
||||
<p>
|
||||
Upgrade to Cloud to assign new roles to members or ask us about our entriprise self
|
||||
hosted offering.
|
||||
</p>
|
||||
</div>
|
||||
<p class="u-flex u-main-end u-cross-center u-gap-4">
|
||||
<Button text external href="https://appwrite.io/docs/advanced/platform/roles"
|
||||
>Learn more</Button>
|
||||
<Button secondary external href={$upgradeURL}>Upgrade to Cloud</Button>
|
||||
</p>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Base>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
export let selected = false;
|
||||
export let href: string = null;
|
||||
export let event: string = null;
|
||||
export let noscroll = false;
|
||||
export let root: { variant: Variant; stretch: boolean } = {
|
||||
variant: 'primary',
|
||||
stretch: false
|
||||
@@ -87,6 +88,7 @@
|
||||
|
||||
{#if href}
|
||||
<Tabs.Item.Link
|
||||
{noscroll}
|
||||
{root}
|
||||
{href}
|
||||
bind:active={selected}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{isSearchable}
|
||||
{required}
|
||||
helper={error}
|
||||
state={error ? 'error' : 'default'}
|
||||
on:invalid={handleInvalid}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
import { Heading, Trim } from '$lib/components';
|
||||
import { Trim } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
|
||||
@@ -25,16 +25,25 @@
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { loading } from '$routes/store';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { ID, Region } from '@appwrite.io/console';
|
||||
import { ID, Region, PlatformType } from '@appwrite.io/console';
|
||||
import { openImportWizard } from '../project-[project]/settings/migrations/(import)';
|
||||
import { readOnly } from '$lib/stores/billing';
|
||||
import type { RegionList } from '$lib/sdk/billing';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, type ComponentType } from 'svelte';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { canWriteProjects } from '$lib/stores/roles';
|
||||
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
|
||||
import { Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Badge, Icon } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconAndroid,
|
||||
IconApple,
|
||||
IconCode,
|
||||
IconFlutter,
|
||||
IconGlobe,
|
||||
IconPlus,
|
||||
IconReact,
|
||||
IconUnity
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { getPlatformInfo } from '$lib/helpers/platform';
|
||||
|
||||
export let data;
|
||||
@@ -64,6 +73,26 @@
|
||||
if (isCloud) wizard.start(Create);
|
||||
else showCreate = true;
|
||||
}
|
||||
|
||||
function getIconForPlatform(platform: string): ComponentType {
|
||||
switch (platform) {
|
||||
case 'web':
|
||||
return IconCode;
|
||||
case 'flutter':
|
||||
return IconFlutter;
|
||||
case 'apple':
|
||||
return IconApple;
|
||||
case 'android':
|
||||
return IconAndroid;
|
||||
case 'react-native':
|
||||
return IconReact;
|
||||
case 'unity':
|
||||
return IconUnity;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$: $registerCommands([
|
||||
{
|
||||
label: 'Create project',
|
||||
@@ -170,10 +199,10 @@
|
||||
|
||||
{#each platforms as platform, i}
|
||||
{#if i < 3}
|
||||
<Pill>
|
||||
<span class={`icon-${platform.icon}`} aria-hidden="true" />
|
||||
{platform.name}
|
||||
</Pill>
|
||||
{@const icon = getIconForPlatform(platform.icon)}
|
||||
<Badge variant="secondary" content={platform.name}>
|
||||
<Icon {icon} size="s" slot="start" />
|
||||
</Badge>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if platforms?.length > 3}
|
||||
|
||||
@@ -59,24 +59,24 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Invite member" {error} size="big" bind:show={showCreate} onSubmit={create}>
|
||||
<FormList>
|
||||
<InputEmail
|
||||
<Modal title="Invite member" {error} bind:show={showCreate} onSubmit={create}>
|
||||
<InputEmail
|
||||
required
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Enter email"
|
||||
autofocus={true}
|
||||
bind:value={email} />
|
||||
<InputText id="member-name" label="Name" placeholder="Enter name" bind:value={name} />
|
||||
{#if isCloud}
|
||||
<InputSelect
|
||||
required
|
||||
id="email"
|
||||
label="Email"
|
||||
placeholder="Enter email"
|
||||
autofocus={true}
|
||||
bind:value={email} />
|
||||
<InputText
|
||||
id="member-name"
|
||||
label="Name (optional)"
|
||||
placeholder="Enter name"
|
||||
bind:value={name} />
|
||||
{#if isCloud}
|
||||
<InputSelect popover={Roles} id="role" label="Role" options={roles} bind:value={role} />
|
||||
{/if}
|
||||
</FormList>
|
||||
popover={Roles}
|
||||
id="role"
|
||||
label="Role"
|
||||
options={roles}
|
||||
bind:value={role} />
|
||||
{/if}
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
|
||||
<Button submit submissionLoader>Send invite</Button>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { ID, Region } from '@appwrite.io/console';
|
||||
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Icon, Layout, Tag } from '@appwrite.io/pink-svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let show = false;
|
||||
@@ -49,21 +51,27 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal title="Create project" {error} onSubmit={create} size="big" bind:show>
|
||||
<FormList>
|
||||
<Modal title="Create project" {error} onSubmit={create} bind:show>
|
||||
<Layout.Stack gap="l">
|
||||
<InputText id="name" label="Name" bind:value={name} required autofocus={true} />
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" /><span class="text">
|
||||
Project ID
|
||||
</span>
|
||||
</Pill>
|
||||
</div>
|
||||
<span>
|
||||
<Tag size="s" on:click={() => (showCustomId = !showCustomId)}>
|
||||
<Icon icon={IconPencil} slot="start" size="s" />
|
||||
Project ID
|
||||
</Tag>
|
||||
</span>
|
||||
{:else}
|
||||
<CustomId autofocus bind:show={showCustomId} name="Project" isProject bind:id />
|
||||
<CustomId
|
||||
autofocus
|
||||
bind:show={showCustomId}
|
||||
name="Project"
|
||||
isProject
|
||||
bind:id
|
||||
fullWidth />
|
||||
{/if}
|
||||
</FormList>
|
||||
</Layout.Stack>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button submit {disabled}>Create</Button>
|
||||
|
||||
@@ -14,9 +14,24 @@
|
||||
import { isOwner } from '$lib/stores/roles';
|
||||
import Edit from './edit.svelte';
|
||||
import { getRoleLabel } from '$lib/stores/billing';
|
||||
import { Drop } from '$lib/components';
|
||||
import { IconDotsHorizontal, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Layout, Table, Badge, Button, Icon } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
IconInfo,
|
||||
IconPencil,
|
||||
IconPlus,
|
||||
IconRefresh,
|
||||
IconTrash
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import {
|
||||
Layout,
|
||||
Table,
|
||||
Badge,
|
||||
Button,
|
||||
Icon,
|
||||
Typography,
|
||||
Popover,
|
||||
ActionMenu
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import Upgrade from '$lib/components/roles/upgrade.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -25,7 +40,6 @@
|
||||
let showDelete = false;
|
||||
let showEdit = false;
|
||||
let showDropdown = [];
|
||||
let showPopover = false;
|
||||
|
||||
const resend = async (member: Models.Membership) => {
|
||||
try {
|
||||
@@ -54,7 +68,8 @@
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Typography.Title>Members</Typography.Title>
|
||||
<ConsoleButton on:mousedown={() => newMemberModal.set(true)} event="create_user" size="s">
|
||||
<Icon size="s" icon={IconPlus} slot="start" />
|
||||
<span class="text">Invite</span>
|
||||
@@ -65,28 +80,16 @@
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell>Name</Table.Header.Cell>
|
||||
<Table.Header.Cell>Email</Table.Header.Cell>
|
||||
<Table.Header.Cell
|
||||
>Roles
|
||||
<Drop isPopover bind:show={showPopover} display="inline-block">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (showPopover = !showPopover)}
|
||||
aria-label="input tooltip">
|
||||
<span
|
||||
class="icon-info"
|
||||
aria-hidden="true"
|
||||
style="font-size: var(--icon-size-small)" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<div
|
||||
class="dropped card u-max-width-300 u-break-word"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={Upgrade} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
<Table.Header.Cell>
|
||||
<Layout.Stack direction="row" gap="xxs" alignItems="center">
|
||||
Roles
|
||||
<Popover let:toggle>
|
||||
<Button.Button icon variant="compact" size="s" on:click={toggle}>
|
||||
<Icon size="s" icon={IconInfo} />
|
||||
</Button.Button>
|
||||
<svelte:component this={Upgrade} slot="tooltip" />
|
||||
</Popover>
|
||||
</Layout.Stack>
|
||||
</Table.Header.Cell>
|
||||
<Table.Header.Cell>2FA</Table.Header.Cell>
|
||||
{#if $isOwner}
|
||||
@@ -102,7 +105,7 @@
|
||||
{member.userName ? member.userName : 'n/a'}
|
||||
</span>
|
||||
{#if member.invited && !member.joined}
|
||||
<Badge type="warning" variant="secondary" content="Pending" />
|
||||
<Badge type="warning" variant="secondary" content="Pending" size="xs" />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
@@ -121,49 +124,41 @@
|
||||
</Table.Cell>
|
||||
{#if $isOwner}
|
||||
<Table.Cell>
|
||||
<DropList bind:show={showDropdown[index]} placement="bottom-end" noArrow>
|
||||
<Button.Button
|
||||
icon
|
||||
variant="text"
|
||||
size="s"
|
||||
aria-label="More options"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
showDropdown[index] = !showDropdown[index];
|
||||
}}>
|
||||
<Icon icon={IconDotsHorizontal} size="m" />
|
||||
<Popover let:toggle padding="none" placement="bottom-end">
|
||||
<Button.Button icon variant="ghost" size="s" on:click={toggle}>
|
||||
<Icon size="s" icon={IconDotsHorizontal} />
|
||||
</Button.Button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem
|
||||
icon="pencil"
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showEdit = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Edit role
|
||||
</DropListItem>
|
||||
{#if member.invited && !member.joined}
|
||||
<DropListItem
|
||||
icon="refresh"
|
||||
<div style:min-width="232px" slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconPencil}
|
||||
on:click={() => {
|
||||
resend(member);
|
||||
selectedMember = member;
|
||||
showEdit = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Resend
|
||||
</DropListItem>
|
||||
{/if}
|
||||
<DropListItem
|
||||
icon="trash"
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showDelete = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Remove
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
Edit role
|
||||
</ActionMenu.Item.Button>
|
||||
{#if member.invited && !member.joined}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconRefresh}
|
||||
on:click={() => {
|
||||
resend(member);
|
||||
}}>
|
||||
Resend
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
<ActionMenu.Item.Button
|
||||
trailingIcon={IconTrash}
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showDelete = true;
|
||||
}}>
|
||||
Remove
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</div>
|
||||
</Popover>
|
||||
</Table.Cell>
|
||||
{/if}
|
||||
</Table.Row>
|
||||
|
||||
@@ -53,14 +53,13 @@
|
||||
<CardGrid>
|
||||
<svelte:fragment slot="title">Name</svelte:fragment>
|
||||
<svelte:fragment slot="aside">
|
||||
<ul>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="Enter name"
|
||||
autocomplete={false}
|
||||
bind:value={name} />
|
||||
</ul>
|
||||
<InputText
|
||||
required
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="Enter name"
|
||||
autocomplete={false}
|
||||
bind:value={name} />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
|
||||
+94
-107
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Alert, Modal, SecondaryTabs, SecondaryTabsItem } from '$lib/components';
|
||||
import { Button, FormList, InputText } from '$lib/elements/forms';
|
||||
import { Modal, SecondaryTabs, SecondaryTabsItem } from '$lib/components';
|
||||
import { Button, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { members, organization, organizationList } from '$lib/stores/organization';
|
||||
@@ -22,6 +22,7 @@
|
||||
} from '$lib/elements/table';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { tierToPlan } from '$lib/stores/billing';
|
||||
import { Table, Tabs, Alert } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let showDelete = false;
|
||||
export let invoices: InvoiceList;
|
||||
@@ -102,116 +103,102 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-height-dialog">
|
||||
<Modal title="Delete organization" onSubmit={deleteOrg} bind:show={showDelete} bind:error>
|
||||
{#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>
|
||||
{#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>
|
||||
<Modal title="Delete organization" onSubmit={deleteOrg} bind:show={showDelete} bind:error>
|
||||
{#if upcomingInvoice}
|
||||
<Alert.Inline
|
||||
status="warning"
|
||||
title={`You have a pending ${formatCurrency(upcomingInvoice.grossAmount)} invoice for your
|
||||
${tierToPlan(upcomingInvoice.plan).name} plan`}>
|
||||
By proceeding, your invoice will be processed within the hour. Upon successful payment,
|
||||
your organization will be deleted.
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
|
||||
<p slot="description">
|
||||
{#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>
|
||||
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>
|
||||
|
||||
<FormList>
|
||||
<InputText
|
||||
label={`Confirm the organization name to continue`}
|
||||
placeholder="Enter {$organization.name} to continue"
|
||||
id="organization-name"
|
||||
required
|
||||
bind:value={organizationName} />
|
||||
</FormList>
|
||||
{#if $projects.total > 0}
|
||||
<Tabs.Root let:root stretch>
|
||||
{#each tabs as { name, label, total }}
|
||||
<Tabs.Item.Button
|
||||
{root}
|
||||
on:click={() => (selectedTab = name)}
|
||||
active={selectedTab === name}>
|
||||
{label.desktop} ({total})
|
||||
</Tabs.Item.Button>
|
||||
{/each}
|
||||
</Tabs.Root>
|
||||
|
||||
<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>
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
{#each tabData.headers as header}
|
||||
<Table.Header.Cell width={columnWidth + 'px'}>{header}</Table.Header.Cell>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
{#each tabData.rows as row}
|
||||
<Table.Row>
|
||||
{#each row.cells as cell}
|
||||
<Table.Cell>{cell}</Table.Cell>
|
||||
{/each}
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
<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}
|
||||
<InputText
|
||||
label={`Confirm the organization name to continue`}
|
||||
placeholder="Enter {$organization.name} to continue"
|
||||
id="organization-name"
|
||||
required
|
||||
bind:value={organizationName} />
|
||||
<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>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
import { periodToDates } from '$lib/layout/usage.svelte';
|
||||
import { canWriteProjects } from '$lib/stores/roles';
|
||||
import { hasOnboardingDismissed } from '$lib/helpers/onboarding';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Card, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
@@ -89,143 +89,166 @@
|
||||
|
||||
{#if $project}
|
||||
<Container overlapCover>
|
||||
{#if $usage}
|
||||
{@const storage = humanFileSize($usage.filesStorageTotal ?? 0)}
|
||||
<section class="common-section">
|
||||
<div class="grid-dashboard-1s-2m-6l">
|
||||
<div class="card is-2-columns-medium-screen is-3-columns-large-screen">
|
||||
<Bandwidth {period} on:change={(e) => changePeriod(e.detail)} />
|
||||
<!-- TODO: fix with nicer solution -->
|
||||
<div class="flex-container">
|
||||
{#if $usage}
|
||||
{@const storage = humanFileSize($usage.filesStorageTotal ?? 0)}
|
||||
<section class="common-section">
|
||||
<div class="grid-dashboard-1s-2m-6l" style:gap="1rem">
|
||||
<Card.Base
|
||||
class="is-2-columns-medium-screen is-3-columns-large-screen"
|
||||
padding="s">
|
||||
<Bandwidth {period} on:change={(e) => changePeriod(e.detail)} />
|
||||
</Card.Base>
|
||||
<Card.Base
|
||||
class="is-2-columns-medium-screen is-3-columns-large-screen"
|
||||
padding="s">
|
||||
<Requests {period} on:change={(e) => changePeriod(e.detail)} />
|
||||
</Card.Base>
|
||||
<Card.Link
|
||||
padding="s"
|
||||
href={`${base}/project-${projectId}/databases`}
|
||||
class="is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-database" aria-hidden="true" />
|
||||
<span class="text">Database</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<Typography.Title>
|
||||
{formatNum($usage.documentsTotal ?? 0)}
|
||||
</Typography.Title>
|
||||
<Typography.Text>Documents</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<Typography.Text
|
||||
>Databases: {formatNum(
|
||||
$usage.databasesTotal ?? 0
|
||||
)}</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Link>
|
||||
<Card.Link
|
||||
padding="s"
|
||||
href={`${base}/project-${projectId}/storage`}
|
||||
class="is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-folder" aria-hidden="true" />
|
||||
<span class="text">Storage</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<Typography.Title>
|
||||
{storage.value}
|
||||
<span class="body-text-2">{storage.unit}</span>
|
||||
</Typography.Title>
|
||||
<Typography.Text>Storage</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<Typography.Text>
|
||||
Buckets: {formatNum($usage.bucketsTotal ?? 0)}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Link>
|
||||
<Card.Link
|
||||
padding="s"
|
||||
href={`${base}/project-${projectId}/auth`}
|
||||
class="is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-user-group" aria-hidden="true" />
|
||||
<span class="text">Auth</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<Typography.Title>
|
||||
{formatNum($usage.usersTotal ?? 0)}
|
||||
</Typography.Title>
|
||||
<Typography.Text>Users</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Link>
|
||||
<Card.Link
|
||||
padding="s"
|
||||
href={`${base}/project-${projectId}/functions`}
|
||||
class="is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-lightning-bolt" aria-hidden="true" />
|
||||
<span class="text">Functions</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<Typography.Title>
|
||||
{formatNum($usage.executionsTotal ?? 0)}
|
||||
</Typography.Title>
|
||||
<Typography.Text>Executions</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<div class="text" />
|
||||
</div>
|
||||
</div>
|
||||
</Card.Link>
|
||||
<Card.Base
|
||||
padding="s"
|
||||
class="is-2-columns-medium-screen is-2-columns-large-screen is-2-rows-large-screen is-location-row-2-end-large-screen">
|
||||
<Realtime />
|
||||
</Card.Base>
|
||||
</div>
|
||||
<div class="card is-2-columns-medium-screen is-3-columns-large-screen">
|
||||
<Requests {period} on:change={(e) => changePeriod(e.detail)} />
|
||||
</div>
|
||||
<a
|
||||
href={`${base}/project-${projectId}/databases`}
|
||||
class="card is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-database" aria-hidden="true" />
|
||||
<span class="text">Database</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{formatNum($usage.documentsTotal ?? 0)}
|
||||
</div>
|
||||
<div>Documents</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<div class="text">
|
||||
Databases: {formatNum($usage.databasesTotal ?? 0)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href={`${base}/project-${projectId}/storage`}
|
||||
class="card is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-folder" aria-hidden="true" />
|
||||
<span class="text">Storage</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{storage.value}
|
||||
<span class="body-text-2">{storage.unit}</span>
|
||||
</div>
|
||||
<div>Storage</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<div class="text">
|
||||
Buckets: {formatNum($usage.bucketsTotal ?? 0)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href={`${base}/project-${projectId}/auth`}
|
||||
class="card is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-user-group" aria-hidden="true" />
|
||||
<span class="text">Auth</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{formatNum($usage.usersTotal ?? 0)}
|
||||
</div>
|
||||
<div>Users</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href={`${base}/project-${projectId}/functions`}
|
||||
class="card is-2-columns-large-screen">
|
||||
<div class="grid-item-1">
|
||||
<div class="grid-item-1-start-start">
|
||||
<div class="eyebrow-heading-3">
|
||||
<span class="icon-lightning-bolt" aria-hidden="true" />
|
||||
<span class="text">Functions</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-start-end" />
|
||||
|
||||
<div class="grid-item-1-end-start">
|
||||
<div class="heading-level-4">
|
||||
{formatNum($usage.executionsTotal ?? 0)}
|
||||
</div>
|
||||
<div>Executions</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-item-1-end-end">
|
||||
<div class="text" />
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div
|
||||
class="card is-2-columns-medium-screen is-2-columns-large-screen is-2-rows-large-screen is-location-row-2-end-large-screen">
|
||||
<Realtime />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<Layout.Stack gap="xl">
|
||||
<Heading tag="h2" size="5" id="integrations">Integrations</Heading>
|
||||
<Layout.Stack gap="xl" direction="row" justifyContent="space-between">
|
||||
<Tabs>
|
||||
<Tab
|
||||
href={`${path}/platforms`}
|
||||
selected={$page.url.pathname === `${path}/platforms`}
|
||||
event="platforms">Platforms</Tab>
|
||||
<Tab
|
||||
href={`${path}/keys`}
|
||||
selected={$page.url.pathname === `${path}/keys`}
|
||||
event="keys">API keys</Tab>
|
||||
</Tabs>
|
||||
{#if $action}
|
||||
<svelte:component this={$action} />
|
||||
{/if}
|
||||
<Layout.Stack gap="xl">
|
||||
<Heading tag="h2" size="5" id="integrations">Integrations</Heading>
|
||||
<Layout.Stack gap="xl" direction="row" justifyContent="space-between">
|
||||
<Tabs>
|
||||
<Tab
|
||||
noscroll
|
||||
href={`${path}/platforms`}
|
||||
selected={$page.url.pathname === `${path}/platforms`}
|
||||
event="platforms">Platforms</Tab>
|
||||
<Tab
|
||||
noscroll
|
||||
href={`${path}/keys`}
|
||||
selected={$page.url.pathname === `${path}/keys`}
|
||||
event="keys">API keys</Tab>
|
||||
</Tabs>
|
||||
{#if $action}
|
||||
<svelte:component this={$action} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Container>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.flex-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-12);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { Card, DropList, DropListItem } from '$lib/components';
|
||||
import { Card } from '$lib/components';
|
||||
import { humanFileSize } from '$lib/helpers/sizeConvertion';
|
||||
import { totalMetrics } from './+layout.svelte';
|
||||
import { usage } from './store';
|
||||
import type { UsagePeriods } from '$lib/layout';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { BarChart } from '$lib/charts';
|
||||
import { Popover, Typography, Icon, ActionMenu, Link, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { IconChevronDown } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let period: UsagePeriods;
|
||||
|
||||
let showPeriod = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: network = $usage?.network as unknown as Array<{
|
||||
@@ -19,32 +19,35 @@
|
||||
}>;
|
||||
|
||||
$: bandwidth = humanFileSize(totalMetrics($usage?.network));
|
||||
|
||||
$: if (period) {
|
||||
showPeriod = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="u-flex u-gap-16 u-main-space-between">
|
||||
<Layout.Stack justifyContent="space-between" direction="row" alignItems="flex-start">
|
||||
<div>
|
||||
<div class="heading-level-4">
|
||||
<Typography.Title>
|
||||
{bandwidth.value}
|
||||
<span class="body-text-2">{bandwidth.unit}</span>
|
||||
</div>
|
||||
<div>Bandwidth</div>
|
||||
</Typography.Title>
|
||||
<Typography.Text>Bandwidth</Typography.Text>
|
||||
</div>
|
||||
<DropList bind:show={showPeriod} placement="bottom-start" childStart noArrow>
|
||||
<button class="transparent-button" on:click={() => (showPeriod = !showPeriod)}>
|
||||
<span class="text">{period}</span>
|
||||
<span class="icon-cheveron-down" aria-hidden="true" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem on:click={() => dispatch('change', '24h')}>24h</DropListItem>
|
||||
<DropListItem on:click={() => dispatch('change', '30d')}>30d</DropListItem>
|
||||
<DropListItem on:click={() => dispatch('change', '90d')}>90d</DropListItem>
|
||||
<Popover let:toggle padding="none">
|
||||
<Link.Button on:click={toggle} variant="quiet">
|
||||
<Layout.Stack direction="row" gap="none">
|
||||
<span>{period}</span>
|
||||
<Icon icon={IconChevronDown} />
|
||||
</Layout.Stack>
|
||||
</Link.Button>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button on:click={() => dispatch('change', '24h')}
|
||||
>24h</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button on:click={() => dispatch('change', '30d')}
|
||||
>30d</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button on:click={() => dispatch('change', '90d')}
|
||||
>90d</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</div>
|
||||
</Popover>
|
||||
</Layout.Stack>
|
||||
{#if bandwidth.value !== '0'}
|
||||
<div style="height: 12rem;">
|
||||
<BarChart
|
||||
|
||||
@@ -58,8 +58,9 @@
|
||||
import type { PageData } from './$types';
|
||||
import { canWritePlatforms } from '$lib/stores/roles';
|
||||
import { setOverviewAction } from '../context';
|
||||
import { Table } from '@appwrite.io/pink-svelte';
|
||||
import { ActionMenu, Card, Empty, Popover, Table } from '@appwrite.io/pink-svelte';
|
||||
import Action from './action.svelte';
|
||||
import { IconAndroid, IconApple, IconFlutter, IconReact } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -121,49 +122,44 @@
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{:else}
|
||||
<article class="card u-grid u-cross-center u-width-full-line common-section">
|
||||
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
|
||||
{#if $app.themeInUse === 'dark'}
|
||||
<img src={SearchDark} alt="empty" aria-hidden="true" />
|
||||
{:else}
|
||||
<img src={SearchLight} alt="empty" aria-hidden="true" />
|
||||
{/if}
|
||||
<slot>
|
||||
<div class="u-text-center">
|
||||
<Heading size="7" tag="h4">Create a platform to get started.</Heading>
|
||||
<p class="body-text-2 u-bold u-margin-block-start-4">
|
||||
Need a hand? Learn more in our documentation.
|
||||
</p>
|
||||
</div>
|
||||
<div class="u-flex u-gap-16 u-main-center">
|
||||
<Button external href="https://appwrite.io/docs/sdks" text>
|
||||
Documentation
|
||||
</Button>
|
||||
<DropList bind:show={showDropdownEmpty} placement="bottom-start">
|
||||
{#if $canWritePlatforms}
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => (showDropdownEmpty = !showDropdownEmpty)}>
|
||||
<span class="text">Add platform</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem on:click={() => addPlatform(Platform.Flutter)}>
|
||||
<Card.Base padding="none">
|
||||
<Empty
|
||||
title="Add a platform to get started."
|
||||
description="Need a hand? Learn more in our documentation.">
|
||||
<svelte:fragment slot="actions">
|
||||
<Button external href="https://appwrite.io/docs/sdks" text>Documentation</Button>
|
||||
<Popover let:toggle padding="none" placement="bottom-end">
|
||||
{#if $canWritePlatforms}
|
||||
<Button secondary on:click={toggle}>
|
||||
<span class="text">Add platform</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<div style:min-width="232px" slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.Flutter)}
|
||||
leadingIcon={IconFlutter}>
|
||||
Flutter
|
||||
</DropListItem>
|
||||
<DropListItem on:click={() => addPlatform(Platform.Android)}>
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.Android)}
|
||||
leadingIcon={IconAndroid}>
|
||||
Android
|
||||
</DropListItem>
|
||||
<DropListItem on:click={() => addPlatform(Platform.Apple)}>
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.Apple)}
|
||||
leadingIcon={IconApple}>
|
||||
Apple
|
||||
</DropListItem>
|
||||
<DropListItem on:click={() => addPlatform(Platform.ReactNative)}>
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.ReactNative)}
|
||||
leadingIcon={IconReact}>
|
||||
React Native
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</article>
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</div>
|
||||
</Popover>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
{/if}
|
||||
|
||||
@@ -3,25 +3,50 @@
|
||||
import DropListItem from '$lib/components/dropListItem.svelte';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import { canWritePlatforms } from '$lib/stores/roles';
|
||||
import { Icon } from '@appwrite.io/pink-svelte';
|
||||
import { ActionMenu, Icon, Popover } from '@appwrite.io/pink-svelte';
|
||||
import { addPlatform, Platform } from './+page.svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import {
|
||||
IconAndroid,
|
||||
IconApple,
|
||||
IconFlutter,
|
||||
IconPlus,
|
||||
IconReact
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
let showDropdown = false;
|
||||
</script>
|
||||
|
||||
{#if $canWritePlatforms}
|
||||
<DropList bind:show={showDropdown} placement="bottom-start">
|
||||
<Button on:click={() => (showDropdown = !showDropdown)}>
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Add platform
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem on:click={() => addPlatform(Platform.Flutter)}>Flutter app</DropListItem>
|
||||
<DropListItem on:click={() => addPlatform(Platform.Android)}>Android app</DropListItem>
|
||||
<DropListItem on:click={() => addPlatform(Platform.Apple)}>Apple app</DropListItem>
|
||||
<DropListItem on:click={() => addPlatform(Platform.ReactNative)}
|
||||
>React Native app</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
<Popover let:toggle padding="none" placement="bottom-end">
|
||||
{#if $canWritePlatforms}
|
||||
<Button on:click={toggle}>
|
||||
<Icon icon={IconPlus} slot="start" />
|
||||
<span class="text">Add platform</span>
|
||||
</Button>
|
||||
{/if}
|
||||
<div style:min-width="232px" slot="tooltip">
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.Flutter)}
|
||||
leadingIcon={IconFlutter}>
|
||||
Flutter
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.Android)}
|
||||
leadingIcon={IconAndroid}>
|
||||
Android
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.Apple)}
|
||||
leadingIcon={IconApple}>
|
||||
Apple
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => addPlatform(Platform.ReactNative)}
|
||||
leadingIcon={IconReact}>
|
||||
React Native
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { BarChart } from '$lib/charts';
|
||||
import { Card } from '$lib/components';
|
||||
import { formatNum } from '$lib/helpers/string';
|
||||
import { Link, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { stats } from '../store';
|
||||
|
||||
$: projectId = $page.params.project;
|
||||
@@ -34,12 +35,11 @@
|
||||
class="icon-chart-square-bar text-large"
|
||||
aria-hidden="true"
|
||||
style="font-size: 32px;" />
|
||||
<p class="u-bold">No data to show</p>
|
||||
<a
|
||||
class="link"
|
||||
<Typography.Text variant="m-600">No data to show</Typography.Text>
|
||||
<Link.Anchor
|
||||
href="https://appwrite.io/docs/apis/realtime"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">Get started with Realtime</a>
|
||||
rel="noopener noreferrer">Get started with Realtime</Link.Anchor>
|
||||
</div>
|
||||
</Card>
|
||||
{/if}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { LineChart } from '$lib/charts';
|
||||
import { formatNum } from '$lib/helpers/string';
|
||||
import { ActionMenu, Icon, Layout, Link, Popover, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { IconChevronDown } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let period: UsagePeriods;
|
||||
|
||||
@@ -23,25 +25,31 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="u-flex u-gap-16 u-main-space-between">
|
||||
<Layout.Stack justifyContent="space-between" direction="row" alignItems="flex-start">
|
||||
<div>
|
||||
<div class="heading-level-4">
|
||||
<Typography.Title>
|
||||
{formatNum(totalMetrics($usage?.requests))}
|
||||
</div>
|
||||
<div>Requests</div>
|
||||
</Typography.Title>
|
||||
<Typography.Text>Requests</Typography.Text>
|
||||
</div>
|
||||
<DropList bind:show={showPeriod} placement="bottom-start" childStart noArrow>
|
||||
<button class="transparent-button" on:click={() => (showPeriod = !showPeriod)}>
|
||||
<span class="text">{period}</span>
|
||||
<span class="icon-cheveron-down" aria-hidden="true" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem on:click={() => dispatch('change', '24h')}>24h</DropListItem>
|
||||
<DropListItem on:click={() => dispatch('change', '30d')}>30d</DropListItem>
|
||||
<DropListItem on:click={() => dispatch('change', '90d')}>90d</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</div>
|
||||
<Popover let:toggle padding="none">
|
||||
<Link.Button on:click={toggle} variant="quiet">
|
||||
<Layout.Stack direction="row" gap="none">
|
||||
<span>{period}</span>
|
||||
<Icon icon={IconChevronDown} />
|
||||
</Layout.Stack>
|
||||
</Link.Button>
|
||||
<ActionMenu.Root slot="tooltip">
|
||||
<ActionMenu.Item.Button on:click={() => dispatch('change', '24h')}
|
||||
>24h</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button on:click={() => dispatch('change', '30d')}
|
||||
>30d</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button on:click={() => dispatch('change', '90d')}
|
||||
>90d</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</Popover>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if totalMetrics($usage?.requests) !== 0}
|
||||
<div style="height: 12rem;">
|
||||
<LineChart
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"color-border-neutral-stronger": "var(--neutral-500)",
|
||||
"color-fgcolor-on-warning-weak": "var(--web-orange-700)",
|
||||
"color-border-neutral-strongest": "var(--neutral-900)",
|
||||
"color-border-focus": "var(--neutral-500)",
|
||||
"color-border-focus": "var(--neutral-300)",
|
||||
"color-border-accent": "var(--brand-pink-500)",
|
||||
"color-border-accent-secondary": "var(--brand-pink-600)",
|
||||
"color-border-success": "var(--web-green-500)",
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"color-border-neutral-stronger": "var(--neutral-500)",
|
||||
"color-fgcolor-on-warning-weak": "var(--web-orange-700)",
|
||||
"color-border-neutral-strongest": "var(--neutral-900)",
|
||||
"color-border-focus": "var(--neutral-500)",
|
||||
"color-border-focus": "var(--neutral-300)",
|
||||
"color-border-accent": "var(--brand-pink-500)",
|
||||
"color-border-accent-secondary": "var(--brand-pink-600)",
|
||||
"color-border-success": "var(--web-green-500)",
|
||||
|
||||
Reference in New Issue
Block a user