mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'feat-pink-v2' into feat-new-onboarding-flow
# Conflicts: # package.json # pnpm-lock.yaml # src/lib/layout/navigation.svelte
This commit is contained in:
+2
-2
@@ -22,7 +22,7 @@
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@9193",
|
||||
"@appwrite.io/pink": "0.25.0",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@5824678",
|
||||
"@appwrite.io/pink-icons-svelte": "1.0.0-next.7",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@250",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@melt-ui/pp": "^0.3.2",
|
||||
"@melt-ui/svelte": "^0.86.2",
|
||||
"@melt-ui/svelte": "^0.83.0",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.8.1",
|
||||
|
||||
@@ -338,5 +338,6 @@ export enum Submit {
|
||||
SiteUpdateBuildSettings = 'submit_site_update_build_settings',
|
||||
SiteUpdateSinglePageApplication = 'submit_site_update_single_page_application',
|
||||
SiteConnectRepo = 'submit_site_connect_repo',
|
||||
SiteRedeploy = 'submit_site_redeploy'
|
||||
SiteRedeploy = 'submit_site_redeploy',
|
||||
SiteDisconnectRepo = 'submit_site_disconnect_repo'
|
||||
}
|
||||
|
||||
@@ -68,12 +68,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<FakeModal
|
||||
bind:show
|
||||
title="Add payment method"
|
||||
bind:error
|
||||
onSubmit={handleSubmit}
|
||||
headerDivider={false}>
|
||||
<FakeModal bind:show title="Add payment method" bind:error onSubmit={handleSubmit}>
|
||||
<FormList gap={16}>
|
||||
<slot />
|
||||
<InputText
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
$: isFree = org.billingPlan === BillingPlan.FREE;
|
||||
</script>
|
||||
|
||||
<Modal bind:show size="big" headerDivider={false} title="Usage rates">
|
||||
<Modal bind:show size="big" title="Usage rates">
|
||||
{#if isFree}
|
||||
Usage on the {$plansInfo?.get(BillingPlan.FREE).name} plan is limited for the following resources.
|
||||
Next billing period: {toLocaleDate(nextDate)}.
|
||||
|
||||
@@ -37,13 +37,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show
|
||||
title="Add credits"
|
||||
headerDivider={false}
|
||||
onSubmit={addCoupon}
|
||||
size="big"
|
||||
bind:error>
|
||||
<Modal bind:show title="Add credits" onSubmit={addCoupon} size="big" bind:error>
|
||||
Credits will be applied automatically to your next invoice.
|
||||
|
||||
<FormList>
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
export let isTile = false;
|
||||
export let isDashed = false;
|
||||
export let isButton = false;
|
||||
export let danger = false;
|
||||
export let href: string = null;
|
||||
let classes = '';
|
||||
export { classes as class };
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Divider, Layout, Card } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let danger = false;
|
||||
export let hideOverflow = false;
|
||||
export let hideFooter = false;
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
import { copy } from '$lib/helpers/copy';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
export let single = false;
|
||||
export let isCard = true;
|
||||
export let noMedia = false;
|
||||
export let target: string = null;
|
||||
export let href: string = null;
|
||||
export let marginTop = false;
|
||||
|
||||
@@ -5,20 +5,10 @@
|
||||
|
||||
type Props = ComponentProps<Selector>;
|
||||
|
||||
export let name: string;
|
||||
export let group: string;
|
||||
export let value: string | number | boolean;
|
||||
export let disabled = false;
|
||||
export let icon: string = null;
|
||||
export let imageIcon: string = null;
|
||||
export let fullHeight = true;
|
||||
export let borderRadius: 'xsmall' | 'small' | 'medium' | 'large' = 'small';
|
||||
export let backgroundColor: string = null;
|
||||
export let backgroundColorHover: string = null;
|
||||
export let tooltipText: string = null;
|
||||
export let tooltipShow = false;
|
||||
export let src: string = null;
|
||||
export let alt: string = null;
|
||||
|
||||
// Pink v2
|
||||
export let radius: Props['radius'] = 's';
|
||||
@@ -27,6 +17,9 @@
|
||||
export let variant: Props['variant'] = 'primary';
|
||||
//temporarily unefined
|
||||
export let title: Props['title'] = undefined;
|
||||
export let disabled = false;
|
||||
export let src: string = null;
|
||||
export let alt: string = null;
|
||||
|
||||
// TODO: remove after label card migration
|
||||
let slotTitle: HTMLSpanElement;
|
||||
@@ -35,12 +28,13 @@
|
||||
<Tooltip disabled={!tooltipText || !tooltipShow} inline={false}>
|
||||
<div>
|
||||
<Card.Selector
|
||||
{padding}
|
||||
{src}
|
||||
{imageRadius}
|
||||
{alt}
|
||||
{padding}
|
||||
{imageRadius}
|
||||
{variant}
|
||||
{value}
|
||||
{radius}
|
||||
{disabled}
|
||||
title={title ?? slotTitle?.innerText}
|
||||
bind:group>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Button, FormItem, FormList, InputDigits, InputText } from '$lib/elements/forms';
|
||||
import { Button, FormList, InputDigits, InputText } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
@@ -92,59 +92,49 @@
|
||||
bind:this={inputDigitFields} />
|
||||
{/if}
|
||||
{#if showVerifyButton}
|
||||
<FormItem>
|
||||
<Button fullWidth submit {disabled}>Verify</Button>
|
||||
</FormItem>
|
||||
<Button fullWidth submit {disabled}>Verify</Button>
|
||||
{/if}
|
||||
{#if enabledFactors.length > 1}
|
||||
<span class="with-separators eyebrow-heading-3">or</span>
|
||||
<div class="u-flex-vertical u-gap-8">
|
||||
{#if factors.totp && challengeType != null && challengeType != AuthenticationFactor.Totp}
|
||||
<FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Totp)}>
|
||||
<span class="icon-device-mobile u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Authenticator app</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Totp)}>
|
||||
<span class="icon-device-mobile u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Authenticator app</span>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if factors.email && challengeType != AuthenticationFactor.Email}
|
||||
<FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Email)}>
|
||||
<span class="icon-mail u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Email verification</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Email)}>
|
||||
<span class="icon-mail u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Email verification</span>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if factors.phone && challengeType != AuthenticationFactor.Phone}
|
||||
<FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Phone)}>
|
||||
<span class="icon-chat-alt u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Phone verification</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
<Button
|
||||
secondary
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Phone)}>
|
||||
<span class="icon-chat-alt u-font-size-20" aria-hidden="true" />
|
||||
<span class="text">Phone verification</span>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if factors.recoveryCode && challengeType != AuthenticationFactor.Recoverycode}
|
||||
<FormItem>
|
||||
<Button
|
||||
text
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Recoverycode)}>
|
||||
<span class="text">Use recovery code</span>
|
||||
</Button>
|
||||
</FormItem>
|
||||
<Button
|
||||
text
|
||||
fullWidth
|
||||
{disabled}
|
||||
on:click={() => createChallenge(AuthenticationFactor.Recoverycode)}>
|
||||
<span class="text">Use recovery code</span>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
import { disableCommands } from '$lib/commandCenter';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import { Layout, Modal } from '@appwrite.io/pink-svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let show = false;
|
||||
export let icon: string = null;
|
||||
export let state: 'success' | 'warning' | 'error' | 'info' = null;
|
||||
export let error: string = null;
|
||||
export let closable = true;
|
||||
export let headerDivider = true;
|
||||
export let open = false;
|
||||
export let dismissible = true;
|
||||
export let onSubmit: (e: SubmitEvent) => Promise<void> | void = function () {
|
||||
return;
|
||||
};
|
||||
@@ -19,11 +17,22 @@
|
||||
export let hideFooter = false;
|
||||
|
||||
let alert: HTMLElement;
|
||||
let formComponent: Form;
|
||||
|
||||
beforeNavigate(() => {
|
||||
show = false;
|
||||
});
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
if (show) {
|
||||
formComponent.triggerSubmit();
|
||||
trackEvent('click_submit_form', { from: 'enter' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: $disableCommands(show);
|
||||
|
||||
$: if (error) {
|
||||
@@ -31,8 +40,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form isModal {onSubmit}>
|
||||
<Modal {title} bind:open={show} {hideFooter}>
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<Form isModal {onSubmit} bind:this={formComponent}>
|
||||
<Modal {title} bind:open={show} {hideFooter} {dismissible}>
|
||||
<svelte:fragment slot="description">
|
||||
<slot name="description" />
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Button, FormItem, FormList, Helper, InputText } from '$lib/elements/forms';
|
||||
import { Button, FormList, Helper, InputText } from '$lib/elements/forms';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Modal } from '..';
|
||||
import type { Writable } from 'svelte/store';
|
||||
@@ -31,16 +31,14 @@
|
||||
role.
|
||||
</span>
|
||||
<FormList>
|
||||
<FormItem>
|
||||
<InputText
|
||||
id="custom-permission"
|
||||
label="Role"
|
||||
placeholder="user:[USER_ID] or team:[TEAM_ID]/[ROLE]"
|
||||
bind:value />
|
||||
<Helper type="neutral">
|
||||
A permission should be formatted as: user:[USER_ID] or team:[TEAM_ID]/[ROLE]¸
|
||||
</Helper>
|
||||
</FormItem>
|
||||
<InputText
|
||||
id="custom-permission"
|
||||
label="Role"
|
||||
placeholder="user:[USER_ID] or team:[TEAM_ID]/[ROLE]"
|
||||
bind:value />
|
||||
<Helper type="neutral">
|
||||
A permission should be formatted as: user:[USER_ID] or team:[TEAM_ID]/[ROLE]¸
|
||||
</Helper>
|
||||
</FormList>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Button, FormItem, FormList, Helper, InputText } from '$lib/elements/forms';
|
||||
import { Button, FormList, Helper, InputText } from '$lib/elements/forms';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Modal } from '..';
|
||||
import type { Writable } from 'svelte/store';
|
||||
@@ -37,11 +37,9 @@
|
||||
Labels allow you to grant access to users with the specified label.
|
||||
</span>
|
||||
<FormList>
|
||||
<FormItem>
|
||||
<InputText id="label" label="Label" placeholder="Enter label" bind:value />
|
||||
<Helper type={isError ? 'warning' : 'neutral'}
|
||||
>Only alphanumeric characters are allowed.</Helper>
|
||||
</FormItem>
|
||||
<InputText id="label" label="Label" placeholder="Enter label" bind:value />
|
||||
<Helper type={isError ? 'warning' : 'neutral'}
|
||||
>Only alphanumeric characters are allowed.</Helper>
|
||||
</FormList>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { onMount } from 'svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { Input, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
import { IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let search = '';
|
||||
@@ -13,8 +13,6 @@
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let autofocus = false;
|
||||
export let isWithEndButton = true;
|
||||
export let fullWidth = false;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let timer: ReturnType<typeof setTimeout>;
|
||||
@@ -60,15 +58,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<div class={fullWidth ? 'u-width-full-line' : 'u-flex-basis-50-percent'}>
|
||||
<Input.Text {placeholder} {disabled} {required} type="search" bind:value={search}>
|
||||
<svelte:fragment slot="end">
|
||||
{#if search}
|
||||
<Input.Action icon={IconX} on:click={() => (search = '')} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Input.Text>
|
||||
</div>
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
<div style:max-width="260px" style:width="100%">
|
||||
<Input.Text {placeholder} {disabled} {required} type="search" bind:value={search}>
|
||||
<svelte:fragment slot="end">
|
||||
{#if search}
|
||||
<Input.Action icon={IconX} on:click={() => (search = '')} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Input.Text>
|
||||
</div>
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { throttle } from '$lib/helpers/functions';
|
||||
import { onMount } from 'svelte';
|
||||
import { Tooltip } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let alternativeTrim = false;
|
||||
let showTooltip = false;
|
||||
let container: HTMLSpanElement | null;
|
||||
|
||||
onMount(onResize);
|
||||
|
||||
function onResize() {
|
||||
showTooltip = container.offsetWidth < container.scrollWidth;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={throttle(onResize, 250)} />
|
||||
|
||||
<span class={`text ${alternativeTrim ? 'u-trim-1' : 'u-trim'}`} bind:this={container}>
|
||||
<Tooltip inline={false}
|
||||
><span class="text u-trim"><slot /></span><span slot="tooltip"
|
||||
>{container?.innerText ?? undefined}</span
|
||||
></Tooltip>
|
||||
</span>
|
||||
<Tooltip maxWidth="auto" inline={false}>
|
||||
<span class={`text ${alternativeTrim ? 'u-trim-1' : 'u-trim'}`} bind:this={container}>
|
||||
<slot />
|
||||
</span>
|
||||
<span slot="tooltip">{container?.innerText ?? undefined} </span>
|
||||
</Tooltip>
|
||||
|
||||
@@ -73,40 +73,37 @@
|
||||
}, 0);
|
||||
</script>
|
||||
|
||||
<div class="grid-header-col-4">
|
||||
{#if !hideColumns && view === View.Table}
|
||||
{#if $columns?.length}
|
||||
<DropList bind:show={showSelectColumns} scrollable wrapperFullWidth={fullWidthMobile}>
|
||||
<Button
|
||||
size="s"
|
||||
secondary
|
||||
on:click={() => (showSelectColumns = !showSelectColumns)}
|
||||
{fullWidthMobile}>
|
||||
<span
|
||||
class="icon-view-boards u-opacity-50"
|
||||
aria-hidden="true"
|
||||
aria-label="columns" />
|
||||
{#if !hideText}
|
||||
<span class="text {showColsTextMobile ? '' : 'is-only-desktop'}"
|
||||
>Columns</span>
|
||||
{/if}
|
||||
<span class="inline-tag">{selectedColumnsNumber}</span>
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
{#each $columns as column}
|
||||
<InputChoice
|
||||
id={column.id}
|
||||
label={column.title}
|
||||
bind:value={column.show}
|
||||
disabled={allowNoColumns
|
||||
? false
|
||||
: selectedColumnsNumber <= 1 && column.show} />
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{/if}
|
||||
{#if !hideColumns && view === View.Table}
|
||||
{#if $columns?.length}
|
||||
<DropList bind:show={showSelectColumns} scrollable wrapperFullWidth={fullWidthMobile}>
|
||||
<Button
|
||||
size="s"
|
||||
secondary
|
||||
on:click={() => (showSelectColumns = !showSelectColumns)}
|
||||
{fullWidthMobile}>
|
||||
<span
|
||||
class="icon-view-boards u-opacity-50"
|
||||
aria-hidden="true"
|
||||
aria-label="columns" />
|
||||
{#if !hideText}
|
||||
<span class="text {showColsTextMobile ? '' : 'is-only-desktop'}">Columns</span>
|
||||
{/if}
|
||||
<span class="inline-tag">{selectedColumnsNumber}</span>
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
{#each $columns as column}
|
||||
<InputChoice
|
||||
id={column.id}
|
||||
label={column.title}
|
||||
bind:value={column.show}
|
||||
disabled={allowNoColumns
|
||||
? false
|
||||
: selectedColumnsNumber <= 1 && column.show} />
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !hideView}
|
||||
<div class="grid-header-col-3 toggle-button">
|
||||
|
||||
@@ -6,14 +6,12 @@
|
||||
import { Button } from '@appwrite.io/pink-svelte';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
type Props = ComponentProps<Button.Button>;
|
||||
type Props = ComponentProps<Button.Button | Button.Anchor>;
|
||||
|
||||
export let submit = false;
|
||||
export let secondary = false;
|
||||
export let text = false;
|
||||
export let danger = false;
|
||||
export let icon = false;
|
||||
export let link = false;
|
||||
export let size: Props['size'] = 's';
|
||||
export let disabled = false;
|
||||
export let external = false;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<script context="module" lang="ts">
|
||||
export type FormItemTag = 'li' | 'div';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let tag: FormItemTag = 'li';
|
||||
export let fullWidth = false;
|
||||
export let isMultiple = false;
|
||||
export let stackOnMobile = false;
|
||||
export let style: string = '';
|
||||
let classes: string = '';
|
||||
export { classes as class };
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
@@ -1,8 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let fullWidth = false;
|
||||
export let alignEnd = false;
|
||||
</script>
|
||||
|
||||
<div class="form-item-part" class:u-cross-child-end={alignEnd} class:u-stretch={fullWidth}>
|
||||
<slot />
|
||||
</div>
|
||||
@@ -1,10 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let isCommonSection = false;
|
||||
export let gap = null;
|
||||
let classes = '';
|
||||
export { classes as class };
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="l">
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
export { default as Form } from './form.svelte';
|
||||
export { default as FormItem } from './formItem.svelte';
|
||||
export { default as FormItemPart } from './formItemPart.svelte';
|
||||
export { default as FormList } from './formList.svelte';
|
||||
export { default as Button } from './button.svelte';
|
||||
export { default as InputDomain } from './inputDomain.svelte';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Selector } from '@appwrite.io/pink-svelte';
|
||||
import { Helper } from '.';
|
||||
import type { FormItemTag } from './formItem.svelte';
|
||||
|
||||
interface $$Props extends Partial<HTMLLabelElement> {
|
||||
id: string;
|
||||
@@ -11,7 +10,6 @@
|
||||
disabled?: boolean;
|
||||
element?: HTMLInputElement | undefined;
|
||||
indeterminate?: boolean;
|
||||
wrapperTag?: FormItemTag;
|
||||
size?: 's' | 'm';
|
||||
}
|
||||
|
||||
@@ -21,7 +19,6 @@
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let element: HTMLInputElement | undefined = undefined;
|
||||
export let wrapperTag: FormItemTag = 'li';
|
||||
export let size: $$Props['size'] = 's';
|
||||
let error: string;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { tooltip as tooltipAction } from '$lib/actions/tooltip';
|
||||
import { Layout, Selector, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { Helper } from '.';
|
||||
|
||||
@@ -37,7 +36,6 @@
|
||||
{id}
|
||||
{disabled}
|
||||
{required}
|
||||
bind:value
|
||||
bind:checked={value}
|
||||
on:change
|
||||
on:invalid={handleInvalid} />
|
||||
@@ -47,7 +45,6 @@
|
||||
{disabled}
|
||||
size="s"
|
||||
{required}
|
||||
bind:value
|
||||
bind:checked={value}
|
||||
on:invalid={handleInvalid}
|
||||
on:change />
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let label: string = null;
|
||||
export let id: string;
|
||||
export let name = id;
|
||||
export let value: string = null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { FormItem, FormItemPart, Helper, Label } from '.';
|
||||
import { Helper, Label } from '.';
|
||||
import NullCheckbox from './nullCheckbox.svelte';
|
||||
|
||||
export let label: string;
|
||||
@@ -17,7 +17,6 @@
|
||||
export let autofocus = false;
|
||||
export let autocomplete = false;
|
||||
export let fullWidth = false;
|
||||
export let isMultiple = false;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
@@ -55,45 +54,42 @@
|
||||
}
|
||||
|
||||
$: isNullable = nullable && !required;
|
||||
$: wrapper = isMultiple ? FormItemPart : FormItem;
|
||||
</script>
|
||||
|
||||
<svelte:component this={wrapper} {fullWidth}>
|
||||
<Label {required} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}
|
||||
</Label>
|
||||
<Label {required} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
step=".001"
|
||||
{min}
|
||||
{max}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="date"
|
||||
style={disabled ? '' : 'cursor: pointer;'}
|
||||
class="input-text"
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid}
|
||||
on:click={function () {
|
||||
this.showPicker();
|
||||
}}
|
||||
style:--amount-of-buttons={isNullable ? 2.75 : 1}
|
||||
style:--button-size={isNullable ? '2rem' : '1rem'} />
|
||||
{#if isNullable}
|
||||
<ul
|
||||
class="buttons-list u-cross-center u-gap-8 u-position-absolute u-inset-block-start-8 u-inset-block-end-8 u-inset-inline-end-12">
|
||||
<li class="buttons-list-item">
|
||||
<NullCheckbox checked={value === null} on:change={handleNullChange} />
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
step=".001"
|
||||
{min}
|
||||
{max}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="date"
|
||||
style={disabled ? '' : 'cursor: pointer;'}
|
||||
class="input-text"
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid}
|
||||
on:click={function () {
|
||||
this.showPicker();
|
||||
}}
|
||||
style:--amount-of-buttons={isNullable ? 2.75 : 1}
|
||||
style:--button-size={isNullable ? '2rem' : '1rem'} />
|
||||
{#if isNullable}
|
||||
<ul
|
||||
class="buttons-list u-cross-center u-gap-8 u-position-absolute u-inset-block-start-8 u-inset-block-end-8 u-inset-inline-end-12">
|
||||
<li class="buttons-list-item">
|
||||
<NullCheckbox checked={value === null} on:change={handleNullChange} />
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</svelte:component>
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import { Helper, Label } from '.';
|
||||
import NullCheckbox from './nullCheckbox.svelte';
|
||||
|
||||
export let label: string;
|
||||
@@ -59,37 +59,35 @@
|
||||
$: isNullable = nullable && !required;
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<Label {required} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}
|
||||
</Label>
|
||||
<Label {required} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{value}
|
||||
{step}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="datetime-local"
|
||||
class="input-text"
|
||||
bind:this={element}
|
||||
on:input={handleInput}
|
||||
on:invalid={handleInvalid}
|
||||
style:--amount-of-buttons={isNullable ? 2.75 : 1}
|
||||
style:--button-size={isNullable ? '2rem' : '1rem'} />
|
||||
{#if isNullable}
|
||||
<ul
|
||||
class="buttons-list u-cross-center u-gap-8 u-position-absolute u-inset-block-start-8 u-inset-block-end-8 u-inset-inline-end-12">
|
||||
<li class="buttons-list-item">
|
||||
<NullCheckbox checked={value === null} on:change={handleNullChange} />
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{value}
|
||||
{step}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="datetime-local"
|
||||
class="input-text"
|
||||
bind:this={element}
|
||||
on:input={handleInput}
|
||||
on:invalid={handleInvalid}
|
||||
style:--amount-of-buttons={isNullable ? 2.75 : 1}
|
||||
style:--button-size={isNullable ? '2rem' : '1rem'} />
|
||||
{#if isNullable}
|
||||
<ul
|
||||
class="buttons-list u-cross-center u-gap-8 u-position-absolute u-inset-block-start-8 u-inset-block-end-8 u-inset-inline-end-12">
|
||||
<li class="buttons-list-item">
|
||||
<NullCheckbox checked={value === null} on:change={handleNullChange} />
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</FormItem>
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { FormItem } from '.';
|
||||
import { createPinInput, melt } from '@melt-ui/svelte';
|
||||
|
||||
export let length: number = 6;
|
||||
@@ -66,24 +65,22 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<FormItem {fullWidth}>
|
||||
<ol class="u-flex u-main-center" use:melt={$root} bind:this={element}>
|
||||
{#each Array.from({ length }) as _}
|
||||
<li>
|
||||
<input
|
||||
type="number"
|
||||
class="verification-code-input u-bold u-remove-input-number-buttons"
|
||||
inputmode="numeric"
|
||||
maxlength="1"
|
||||
style:border-radius="var(--border-radius-small)"
|
||||
use:melt={$input()}
|
||||
{required}
|
||||
{readonly}
|
||||
{disabled} />
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
</FormItem>
|
||||
<ol class="u-flex u-main-center" use:melt={$root} bind:this={element}>
|
||||
{#each Array.from({ length }) as _}
|
||||
<li>
|
||||
<input
|
||||
type="number"
|
||||
class="verification-code-input u-bold u-remove-input-number-buttons"
|
||||
inputmode="numeric"
|
||||
maxlength="1"
|
||||
style:border-radius="var(--border-radius-small)"
|
||||
use:melt={$input()}
|
||||
{required}
|
||||
{readonly}
|
||||
{disabled} />
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
|
||||
<style lang="scss">
|
||||
@use '@appwrite.io/pink/src/abstract/variables/devices';
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent, onMount } from 'svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let label: string = null;
|
||||
export let id: string;
|
||||
export let name = id;
|
||||
export let value = '';
|
||||
@@ -14,10 +12,6 @@
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let autocomplete = false;
|
||||
export let maxlength: number = null;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
export let fullWidth = false;
|
||||
|
||||
// https://www.geeksforgeeks.org/how-to-validate-a-domain-name-using-regular-expression/
|
||||
const pattern = String.raw`(?!-)[A-Za-z0-9\-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,18}`;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent } from 'svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let label: string = null;
|
||||
export let id: string;
|
||||
export let value = '';
|
||||
export let placeholder = '';
|
||||
@@ -15,10 +12,6 @@
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let autocomplete = false;
|
||||
export let tooltip: string = null;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
export let fullWidth = false;
|
||||
|
||||
let error: string;
|
||||
|
||||
@@ -47,6 +40,7 @@
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{required}
|
||||
{readonly}
|
||||
{label}
|
||||
{nullable}
|
||||
on:input
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string | undefined = undefined;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let label: string = null;
|
||||
export let id: string;
|
||||
export let name = id;
|
||||
export let value: number = null;
|
||||
@@ -16,8 +14,6 @@
|
||||
export let min: number = null;
|
||||
export let max: number = null;
|
||||
export let step: number | 'any' = 1;
|
||||
export let isMultiple = false;
|
||||
export let fullWidth = false;
|
||||
|
||||
let error: string;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent, onMount } from 'svelte';
|
||||
import { FormItem, FormItemPart, Helper, Label } from '.';
|
||||
import { Helper, Label } from '.';
|
||||
import { Drop } from '$lib/components';
|
||||
|
||||
export let label: string = undefined;
|
||||
@@ -21,7 +21,6 @@
|
||||
export let autocomplete = false;
|
||||
export let fullWidth = false;
|
||||
export let tooltip: string = null;
|
||||
export let isMultiple = false;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
|
||||
@@ -58,63 +57,59 @@
|
||||
type $$Events = {
|
||||
input: Event & { target: HTMLInputElement };
|
||||
};
|
||||
|
||||
$: wrapper = isMultiple ? FormItemPart : FormItem;
|
||||
</script>
|
||||
|
||||
<svelte:component this={wrapper} {fullWidth}>
|
||||
{#if label}
|
||||
<Label {required} {hideRequired} {tooltip} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show display="inline-block">
|
||||
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (show = !show)}
|
||||
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-250"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
</Label>
|
||||
{/if}
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{name}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{maxlength}
|
||||
{pattern}
|
||||
type="text"
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
class="input-text"
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
{#if $$slots.options}
|
||||
<div class="options-list">
|
||||
<slot name="options" />
|
||||
</div>
|
||||
{#if label}
|
||||
<Label {required} {hideRequired} {tooltip} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show display="inline-block">
|
||||
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (show = !show)}
|
||||
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-250"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning" class="u-line-height-1">{error}</Helper>
|
||||
</Label>
|
||||
{/if}
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{name}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{maxlength}
|
||||
{pattern}
|
||||
type="text"
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
class="input-text"
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
{#if $$slots.options}
|
||||
<div class="options-list">
|
||||
<slot name="options" />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:component>
|
||||
<slot />
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning" class="u-line-height-1">{error}</Helper>
|
||||
{/if}
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent } from 'svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let id: string;
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let value = '';
|
||||
export let placeholder = '';
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let autofocus = false;
|
||||
export let meter = false;
|
||||
export let autocomplete = false;
|
||||
export let showPasswordButton = false;
|
||||
export let minlength = 8;
|
||||
export let maxlength: number = null;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
export let fullWidth = false;
|
||||
|
||||
let error: string;
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { SvelteComponent, onMount } from 'svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
import type { States } from '@appwrite.io/pink-svelte/dist/input/types';
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let label: string = null;
|
||||
export let id: string;
|
||||
export let name: string = id;
|
||||
export let value = '';
|
||||
@@ -17,9 +14,6 @@
|
||||
export let minlength: number = null;
|
||||
export let maxlength: number = 15;
|
||||
export let nullable = false;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
export let fullWidth = false;
|
||||
|
||||
const pattern = String.raw`^\+[1-9]\d{1,14}$`;
|
||||
|
||||
@@ -57,6 +51,7 @@
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{required}
|
||||
{readonly}
|
||||
{label}
|
||||
{nullable}
|
||||
{pattern}
|
||||
|
||||
@@ -7,31 +7,13 @@
|
||||
type Props = ComponentProps<Radio>;
|
||||
|
||||
export let label: string = null;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let group: string;
|
||||
export let value: string;
|
||||
export let name: string;
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let size: Props['size'] = 'medium';
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (element.validity.valueMissing) {
|
||||
error = 'This field is required';
|
||||
return;
|
||||
}
|
||||
error = element.validationMessage;
|
||||
};
|
||||
|
||||
$: if (value) {
|
||||
error = null;
|
||||
}
|
||||
export let size: Props['size'] = 'm';
|
||||
</script>
|
||||
|
||||
<Selector.Radio
|
||||
@@ -43,4 +25,5 @@
|
||||
{label}
|
||||
{size}
|
||||
bind:group
|
||||
on:invalid={handleInvalid} />
|
||||
on:invalid
|
||||
on:change />
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let autofocus = false;
|
||||
export let isWithEndButton = true;
|
||||
export let style: string = '';
|
||||
export let label: string = '';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let id: string;
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let label: string = '';
|
||||
export let value = '';
|
||||
export let placeholder = '';
|
||||
</script>
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
import type { FormItemTag } from './formItem.svelte';
|
||||
import type { SvelteComponent } from 'svelte';
|
||||
|
||||
export let id: string;
|
||||
export let label: string | undefined = undefined;
|
||||
export let ariaLabel = label;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let value: string | number | boolean | null;
|
||||
export let placeholder = '';
|
||||
export let required = false;
|
||||
export let hideRequired = false;
|
||||
export let disabled = false;
|
||||
export let wrapperTag: FormItemTag = 'li';
|
||||
export let options: {
|
||||
value: string | boolean | number | null;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}[];
|
||||
export let isMultiple = false;
|
||||
export let fullWidth = false;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
|
||||
let element: HTMLSelectElement;
|
||||
let error: string;
|
||||
|
||||
let show: boolean = false;
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
@@ -1,29 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Selector } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let id: string;
|
||||
export let label: string;
|
||||
export let value = false;
|
||||
export let disabled = false;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
error = element.validationMessage;
|
||||
};
|
||||
|
||||
$: if (value) {
|
||||
error = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Selector.Switch
|
||||
{label}
|
||||
{disabled}
|
||||
bind:checked={value}
|
||||
helper={error}
|
||||
state={error ? 'error' : 'default'}
|
||||
on:invalid={handleInvalid} />
|
||||
<Selector.Switch {id} {label} {disabled} bind:checked={value} on:invalid on:change />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { last } from '$lib/helpers/array';
|
||||
import { onMount, SvelteComponent } from 'svelte';
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import { Helper, Label } from '.';
|
||||
import { Drop } from '$lib/components';
|
||||
|
||||
export let id: string;
|
||||
@@ -86,72 +86,67 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<input
|
||||
class="u-hide"
|
||||
bind:this={hiddenEl}
|
||||
value={tags.join(',')}
|
||||
{required}
|
||||
on:invalid={handleInvalid} />
|
||||
<Label {required} {tooltip} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show display="inline-block">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => (show = !show)}
|
||||
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-250"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<div class="tags-input">
|
||||
<div class="tags">
|
||||
<ul class="tags-list">
|
||||
{#each tags as tag}
|
||||
<li class="tags-item">
|
||||
<div class="input-tag">
|
||||
<span class="tag-text">{tag}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="input-tag-delete-button"
|
||||
aria-label={`delete ${tag} tag`}
|
||||
on:click={() => removeValue(tag)}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<input
|
||||
{id}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{readonly}
|
||||
type="text"
|
||||
class="tags-input-text"
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:keydown={handleInput}
|
||||
on:blur={addValue} />
|
||||
</div>
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
<input
|
||||
class="u-hide"
|
||||
bind:this={hiddenEl}
|
||||
value={tags.join(',')}
|
||||
{required}
|
||||
on:invalid={handleInvalid} />
|
||||
<Label {required} {tooltip} hide={!showLabel} for={id}>
|
||||
{label}{#if popover}
|
||||
<Drop isPopover bind:show display="inline-block">
|
||||
<button type="button" on:click={() => (show = !show)} 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-250"
|
||||
style:--card-border-radius="var(--border-radius-small)"
|
||||
style:--p-card-padding=".75rem"
|
||||
style:box-shadow="var(--shadow-large)">
|
||||
<svelte:component this={popover} {...popoverProps} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
{/if}
|
||||
</FormItem>
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper">
|
||||
<div class="tags-input">
|
||||
<div class="tags">
|
||||
<ul class="tags-list">
|
||||
{#each tags as tag}
|
||||
<li class="tags-item">
|
||||
<div class="input-tag">
|
||||
<span class="tag-text">{tag}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="input-tag-delete-button"
|
||||
aria-label={`delete ${tag} tag`}
|
||||
on:click={() => removeValue(tag)}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<input
|
||||
{id}
|
||||
{placeholder}
|
||||
{disabled}
|
||||
{readonly}
|
||||
type="text"
|
||||
class="tags-input-text"
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:keydown={handleInput}
|
||||
on:blur={addValue} />
|
||||
</div>
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
|
||||
@@ -1,37 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
import { SvelteComponent, onMount, tick } from 'svelte';
|
||||
export let label: string = undefined;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let name: string = id;
|
||||
export let value = '';
|
||||
export let placeholder = '';
|
||||
export let required = false;
|
||||
export let hideRequired = false;
|
||||
export let nullable = false;
|
||||
export let disabled = false;
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let autocomplete = false;
|
||||
export let fullWidth = false;
|
||||
export let maxlength: number = null;
|
||||
export let tooltip: string = null;
|
||||
export let isMultiple = false;
|
||||
export let popover: typeof SvelteComponent<unknown> = null;
|
||||
export let popoverProps: Record<string, unknown> = {};
|
||||
|
||||
let error: string;
|
||||
|
||||
// TODO: re-enable
|
||||
// onMount(async () => {
|
||||
// if (element && autofocus) {
|
||||
// await tick();
|
||||
// element.focus();
|
||||
// }
|
||||
// });
|
||||
|
||||
const handleInvalid = (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import NullCheckbox from './nullCheckbox.svelte';
|
||||
import TextCounter from './textCounter.svelte';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let name: string = id;
|
||||
export let value = '';
|
||||
@@ -18,8 +12,6 @@
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let maxlength: number = null;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let tooltip: string = null;
|
||||
|
||||
let error: string;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { FormItem, FormItemPart, Helper, Label } from '.';
|
||||
import { Helper, Label } from '.';
|
||||
|
||||
export let label: string;
|
||||
export let showLabel = true;
|
||||
@@ -14,8 +14,6 @@
|
||||
export let readonly = false;
|
||||
export let autofocus = false;
|
||||
export let autocomplete = false;
|
||||
export let fullWidth = false;
|
||||
export let isMultiple = false;
|
||||
export let step: number | 'any' = 60;
|
||||
|
||||
let element: HTMLInputElement;
|
||||
@@ -41,35 +39,32 @@
|
||||
$: if (value) {
|
||||
error = null;
|
||||
}
|
||||
$: wrapper = isMultiple ? FormItemPart : FormItem;
|
||||
</script>
|
||||
|
||||
<svelte:component this={wrapper} {fullWidth}>
|
||||
<Label {required} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}
|
||||
</Label>
|
||||
<Label {required} {optionalText} hide={!showLabel} for={id}>
|
||||
{label}
|
||||
</Label>
|
||||
|
||||
<div class="input-text-wrapper" style="--amount-of-buttons:1; --button-size: 1rem">
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{min}
|
||||
{max}
|
||||
{step}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="time"
|
||||
class="input-text"
|
||||
style={disabled ? '' : 'cursor: pointer;'}
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid}
|
||||
on:click={function () {
|
||||
this.showPicker();
|
||||
}} />
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</svelte:component>
|
||||
<div class="input-text-wrapper" style="--amount-of-buttons:1; --button-size: 1rem">
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{required}
|
||||
{min}
|
||||
{max}
|
||||
{step}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
type="time"
|
||||
class="input-text"
|
||||
style={disabled ? '' : 'cursor: pointer;'}
|
||||
bind:value
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid}
|
||||
on:click={function () {
|
||||
this.showPicker();
|
||||
}} />
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let label: string;
|
||||
export let optionalText: string | undefined = undefined;
|
||||
export let showLabel = true;
|
||||
export let id: string;
|
||||
export let name = id;
|
||||
export let value = '';
|
||||
|
||||
@@ -6,23 +6,11 @@
|
||||
PaginationWithLimit,
|
||||
Trim
|
||||
} from '$lib/components';
|
||||
import {
|
||||
TableBody,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableCellHead,
|
||||
TableCell,
|
||||
TableCellText,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { Container, ContainerHeader } from '$lib/layout';
|
||||
import { hoursToDays, toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { tierToPlan, type PlanServices } from '$lib/stores/billing';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { type PlanServices } from '$lib/stores/billing';
|
||||
import { Layout, Table } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let logs: Models.LogList;
|
||||
export let offset = 0;
|
||||
@@ -38,61 +26,53 @@
|
||||
<Heading tag="h2" size="5">Activity</Heading>
|
||||
{/if}
|
||||
{#if logs.total}
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={100}>User</TableCellHead>
|
||||
<TableCellHead width={100}>Event</TableCellHead>
|
||||
<TableCellHead width={80}>Location</TableCellHead>
|
||||
<TableCellHead width={90}>IP</TableCellHead>
|
||||
<TableCellHead width={140}>Date</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody service="logs" total={isCloud ? Infinity : 0}>
|
||||
<svelte:fragment slot="limit" let:limit let:upgradeMethod>
|
||||
<p class="text">
|
||||
Logs are retained in rolling {hoursToDays(limit)} intervals with the
|
||||
{tierToPlan($organization.billingPlan).name}
|
||||
plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<Button link on:click={upgradeMethod}>Upgrade</Button> to increase your log
|
||||
retention for a longer period.
|
||||
{/if}
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
{#each logs.logs as log}
|
||||
<TableRow>
|
||||
<TableCell title="Client">
|
||||
<div class="u-flex u-cross-center u-gap-12">
|
||||
{#if log.userEmail}
|
||||
{#if log.userName}
|
||||
<AvatarInitials size={32} name={log.userName} />
|
||||
<Trim>{log.userName}</Trim>
|
||||
{:else}
|
||||
<AvatarInitials size={32} name={log.userEmail} />
|
||||
<Trim>{log.userEmail}</Trim>
|
||||
{/if}
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell>User</Table.Header.Cell>
|
||||
<Table.Header.Cell>Event</Table.Header.Cell>
|
||||
<Table.Header.Cell>Location</Table.Header.Cell>
|
||||
<Table.Header.Cell>IP</Table.Header.Cell>
|
||||
<Table.Header.Cell>Date</Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each logs.logs as log}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
{#if log.userEmail}
|
||||
{#if log.userName}
|
||||
<AvatarInitials size={32} name={log.userName} />
|
||||
<Trim>{log.userName}</Trim>
|
||||
{:else}
|
||||
<div class="avatar is-size-small">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
<span class="text u-trim">{log.userName ?? 'Anonymous'}</span>
|
||||
<AvatarInitials size={32} name={log.userEmail} />
|
||||
<Trim>{log.userEmail}</Trim>
|
||||
{/if}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCellText title="Event">{log.event}</TableCellText>
|
||||
|
||||
<TableCellText title="Location">
|
||||
{#if log.countryCode !== '--'}
|
||||
{log.countryName}
|
||||
{:else}
|
||||
Unknown
|
||||
<div class="avatar is-size-small">
|
||||
<span class="icon-anonymous" aria-hidden="true" />
|
||||
</div>
|
||||
<span class="text u-trim">{log.userName ?? 'Anonymous'}</span>
|
||||
{/if}
|
||||
</TableCellText>
|
||||
<TableCellText title="IP">{log.ip}</TableCellText>
|
||||
<TableCellText title="Date">{toLocaleDateTime(log.time)}</TableCellText>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{log.event}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{#if log.countryCode !== '--'}
|
||||
{log.countryName}
|
||||
{:else}
|
||||
Unknown
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{log.ip}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{toLocaleDateTime(log.time)}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
|
||||
<PaginationWithLimit name="Logs" {limit} {offset} total={logs.total} />
|
||||
{:else}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let isFlex = true;
|
||||
export let title: string;
|
||||
export let titleTag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' = 'h2';
|
||||
export let titleSize: '1' | '2' | '3' | '4' | '5' | '6' | '7' = '5';
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { Tooltip } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let selectedRequest = 'parameters';
|
||||
@@ -347,7 +346,7 @@
|
||||
Logs are retained in rolling {hoursToDays(limit)} intervals
|
||||
with the {tier} plan.
|
||||
{#if $organization.billingPlan === BillingPlan.FREE}
|
||||
<Button link href={$upgradeURL}>Upgrade</Button> to increase
|
||||
<Button href={$upgradeURL}>Upgrade</Button> to increase
|
||||
your log retention for a longer period.
|
||||
{/if}
|
||||
</Alert>
|
||||
|
||||
@@ -13,14 +13,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Exit Process"
|
||||
bind:show
|
||||
onSubmit={handleSubmit}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
size="small"
|
||||
headerDivider={false}>
|
||||
<Modal title="Exit Process" bind:show onSubmit={handleSubmit}>
|
||||
<p class="text">
|
||||
<slot />
|
||||
</p>
|
||||
|
||||
@@ -32,13 +32,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete domain"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteDomain}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete domain" bind:show={showDelete} onSubmit={deleteDomain}>
|
||||
{#if selectedDomain}
|
||||
<p data-private>
|
||||
Are you sure you want to delete <b>{selectedDomain.domain}</b>? You will no longer be
|
||||
|
||||
+108
-114
@@ -21,6 +21,7 @@
|
||||
import Retry from './wizard/retry.svelte';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { canWriteRules } from '$lib/stores/roles';
|
||||
import { Layout, Table, Link } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let rules: Models.ProxyRuleList;
|
||||
export let type: ResourceType;
|
||||
@@ -63,127 +64,120 @@
|
||||
{/if}
|
||||
</div>
|
||||
{#if rules.total}
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={150}>Name</TableCellHead>
|
||||
<TableCellHead width={120}>Verification Status</TableCellHead>
|
||||
<TableCellHead width={180}>Certificate Status</TableCellHead>
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell>Name</Table.Header.Cell>
|
||||
<Table.Header.Cell>Verification Status</Table.Header.Cell>
|
||||
<Table.Header.Cell>Certificate Status</Table.Header.Cell>
|
||||
{#if $canWriteRules}
|
||||
<TableCellHead width={40} />
|
||||
<Table.Header.Cell width="40" />
|
||||
{/if}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each rules.rules as domain, i}
|
||||
<TableRow>
|
||||
<TableCellLink
|
||||
title="Domain"
|
||||
href={`https://${domain.domain}`}
|
||||
external
|
||||
noStyle>
|
||||
<span class="u-flex u-gap-4 u-cross-center">
|
||||
<Trim>
|
||||
<span class="link">{domain.domain}</span>
|
||||
</Trim>
|
||||
<span class="icon-external-link" aria-hidden="true" />
|
||||
</span>
|
||||
</TableCellLink>
|
||||
<TableCell title="Verification">
|
||||
{#if domain.status === 'created'}
|
||||
<div class="u-flex u-gap-16 u-cross-center">
|
||||
<Pill danger>
|
||||
<span
|
||||
class="icon-exclamation-circle u-color-text-danger"
|
||||
aria-hidden="true" />
|
||||
<span class="u-text">failed</span>
|
||||
</Pill>
|
||||
<button type="button" on:click={() => openRetry(domain)}>
|
||||
<span class="link">Retry</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
</svelte:fragment>
|
||||
{#each rules.rules as domain, index}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Link.Anchor href={`https://${domain.domain}`} target="_blank">
|
||||
{domain.domain}
|
||||
</Link.Anchor>
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{#if domain.status === 'created'}
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Pill danger>
|
||||
<span
|
||||
class="icon-exclamation-circle u-color-text-danger"
|
||||
aria-hidden="true" />
|
||||
<span class="u-text">failed</span>
|
||||
</Pill>
|
||||
<button type="button" on:click={() => openRetry(domain)}>
|
||||
<span class="link">Retry</span>
|
||||
</button>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Pill success>
|
||||
<span
|
||||
class="icon-check-circle u-color-text-success"
|
||||
aria-hidden="true" />
|
||||
<p class="text">verified</p>
|
||||
</Pill>
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{#if domain.status === 'unverified'}
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Pill danger>
|
||||
<span
|
||||
class="icon-exclamation-circle u-color-text-danger"
|
||||
aria-hidden="true" />
|
||||
<span class="u-text">failed</span>
|
||||
</Pill>
|
||||
<button type="button" on:click={() => openRetry(domain)}>
|
||||
<span class="link">Retry</span>
|
||||
</button>
|
||||
</Layout.Stack>
|
||||
{:else if domain.status === 'verified'}
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Pill success>
|
||||
<span
|
||||
class="icon-check-circle u-color-text-success"
|
||||
aria-hidden="true" />
|
||||
<p class="text">verified</p>
|
||||
<span>generated</span>
|
||||
</Pill>
|
||||
{/if}
|
||||
</TableCell>
|
||||
<TableCell title="Certificate">
|
||||
{#if domain.status === 'unverified'}
|
||||
<div class="u-flex u-gap-16 u-cross-center">
|
||||
<Pill danger>
|
||||
<span
|
||||
class="icon-exclamation-circle u-color-text-danger"
|
||||
aria-hidden="true" />
|
||||
<span class="u-text">failed</span>
|
||||
</Pill>
|
||||
<button type="button" on:click={() => openRetry(domain)}>
|
||||
<span class="link">Retry</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else if domain.status === 'verified'}
|
||||
<div class="u-flex u-gap-16 u-cross-center">
|
||||
<Pill success>
|
||||
<span
|
||||
class="icon-check-circle u-color-text-success"
|
||||
aria-hidden="true" />
|
||||
<span>generated</span>
|
||||
</Pill>
|
||||
{#if domain.renewAt}
|
||||
<span class="u-text-color-gray">
|
||||
Auto-renewal: {toLocaleDate(domain.renewAt)}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<Pill warning>
|
||||
<span class="icon-clock u-text-color-gray" aria-hidden="true" />
|
||||
<p class="text">blocked by verification</p>
|
||||
</Pill>
|
||||
{/if}
|
||||
</TableCell>
|
||||
{#if $canWriteRules}
|
||||
<TableCell right>
|
||||
<DropList
|
||||
bind:show={showDomainsDropdown[i]}
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<Button
|
||||
text
|
||||
icon
|
||||
ariaLabel="more options"
|
||||
on:click={() =>
|
||||
(showDomainsDropdown[i] = !showDomainsDropdown[i])}>
|
||||
<span class="icon-dots-horizontal" aria-hidden="true" />
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
{#if domain.status !== 'verified'}
|
||||
<DropListItem
|
||||
icon="refresh"
|
||||
on:click={() => openRetry(domain, i)}>
|
||||
{domain.status === 'unverified'
|
||||
? 'Retry generation'
|
||||
: 'Retry verification'}
|
||||
</DropListItem>
|
||||
{/if}
|
||||
<DropListItem
|
||||
icon="trash"
|
||||
on:click={() => {
|
||||
selectedDomain = domain;
|
||||
showDelete = true;
|
||||
showDomainsDropdown[i] = false;
|
||||
}}>
|
||||
Delete
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</TableCell>
|
||||
{#if domain.renewAt}
|
||||
<span class="u-text-color-gray">
|
||||
Auto-renewal: {toLocaleDate(domain.renewAt)}
|
||||
</span>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Pill warning>
|
||||
<span class="icon-clock u-text-color-gray" aria-hidden="true" />
|
||||
<p class="text">blocked by verification</p>
|
||||
</Pill>
|
||||
{/if}
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
</Table.Cell>
|
||||
{#if $canWriteRules}
|
||||
<Table.Cell>
|
||||
<DropList
|
||||
bind:show={showDomainsDropdown[index]}
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<Button
|
||||
text
|
||||
icon
|
||||
ariaLabel="more options"
|
||||
on:click={() =>
|
||||
(showDomainsDropdown[index] = !showDomainsDropdown[index])}>
|
||||
<span class="icon-dots-horizontal" aria-hidden="true" />
|
||||
</Button>
|
||||
<svelte:fragment slot="list">
|
||||
{#if domain.status !== 'verified'}
|
||||
<DropListItem
|
||||
icon="refresh"
|
||||
on:click={() => openRetry(domain, index)}>
|
||||
{domain.status === 'unverified'
|
||||
? 'Retry generation'
|
||||
: 'Retry verification'}
|
||||
</DropListItem>
|
||||
{/if}
|
||||
<DropListItem
|
||||
icon="trash"
|
||||
on:click={() => {
|
||||
selectedDomain = domain;
|
||||
showDelete = true;
|
||||
showDomainsDropdown[index] = false;
|
||||
}}>
|
||||
Delete
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</Table.Cell>
|
||||
{/if}
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{:else}
|
||||
<Empty
|
||||
single
|
||||
@@ -193,7 +187,7 @@
|
||||
{/if}
|
||||
|
||||
<Delete bind:showDelete bind:selectedDomain {dependency} />
|
||||
<Modal bind:show={showRetry} headerDivider={false} bind:error={retryError} size="big">
|
||||
<Modal bind:show={showRetry} bind:error={retryError}>
|
||||
<svelte:fragment slot="title">
|
||||
Retry {$domain.status === 'unverified' ? 'certificate generation' : 'verification'}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -27,14 +27,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete account"
|
||||
bind:show={showDelete}
|
||||
bind:error
|
||||
onSubmit={deleteAccount}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete account" bind:show={showDelete} bind:error onSubmit={deleteAccount}>
|
||||
<p>Are you sure you want to delete your account?</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
|
||||
@@ -38,10 +38,7 @@
|
||||
title="Delete authentication method"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteAuthenticator}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
bind:error
|
||||
headerDivider={false}>
|
||||
bind:error>
|
||||
<p>
|
||||
Are you sure you want to delete this authentication method? You will no longer be able to
|
||||
use this method to authenticate your account.
|
||||
|
||||
@@ -29,14 +29,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Regenerate recovery codes"
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
{error}
|
||||
onSubmit={submit}
|
||||
bind:show>
|
||||
<Modal title="Regenerate recovery codes" {error} onSubmit={submit} bind:show>
|
||||
<p>
|
||||
Are you sure you want to regenerate all recovery codes? All <b
|
||||
>previously generated recovery codes will become invalid.</b>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, FormItem, FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { Button, FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import type { Organization } from '$lib/stores/organization';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -101,23 +101,13 @@
|
||||
label="City or suburb"
|
||||
placeholder="Enter your city"
|
||||
required />
|
||||
<FormItem isMultiple>
|
||||
<InputText
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={state}
|
||||
id="state"
|
||||
label="State"
|
||||
placeholder="Enter your state"
|
||||
required />
|
||||
<InputText
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={zip}
|
||||
id="zip"
|
||||
label="Postal code"
|
||||
placeholder="Enter postal code" />
|
||||
</FormItem>
|
||||
<InputText
|
||||
bind:value={state}
|
||||
id="state"
|
||||
label="State"
|
||||
placeholder="Enter your state"
|
||||
required />
|
||||
<InputText bind:value={zip} id="zip" label="Postal code" placeholder="Enter postal code" />
|
||||
</FormList>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -32,13 +32,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show={showDelete}
|
||||
onSubmit={handleDelete}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
bind:error>
|
||||
<Modal bind:show={showDelete} onSubmit={handleDelete} bind:error>
|
||||
<svelte:fragment slot="title">
|
||||
{linkedOrgs.length ? 'Unable to delete billing address' : 'Delete billing address'}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -33,12 +33,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show={showDelete}
|
||||
onSubmit={handleDelete}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal bind:show={showDelete} onSubmit={handleDelete}>
|
||||
<svelte:fragment slot="title">
|
||||
{linkedOrgs.length ? 'Unable to delete payment method' : 'Delete payment method'}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, FormItem, FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { Button, FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import type { Address } from '$lib/sdk/billing';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -85,23 +85,18 @@
|
||||
label="City or suburb"
|
||||
placeholder="Enter your city"
|
||||
required />
|
||||
<FormItem isMultiple>
|
||||
<InputText
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={selectedAddress.state}
|
||||
id="state"
|
||||
label="State"
|
||||
placeholder="Enter your state"
|
||||
required />
|
||||
<InputText
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={selectedAddress.postalCode}
|
||||
id="zip"
|
||||
label="Postal code"
|
||||
placeholder="Enter postal code" />
|
||||
</FormItem>
|
||||
<InputText
|
||||
fullWidth
|
||||
bind:value={selectedAddress.state}
|
||||
id="state"
|
||||
label="State"
|
||||
placeholder="Enter your state"
|
||||
required />
|
||||
<InputText
|
||||
bind:value={selectedAddress.postalCode}
|
||||
id="zip"
|
||||
label="Postal code"
|
||||
placeholder="Enter postal code" />
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Alert, Modal } from '$lib/components';
|
||||
import { Button, FormItem, FormList, InputNumber, InputSelect } from '$lib/elements/forms';
|
||||
import { Button, FormList, InputNumber, InputSelect } from '$lib/elements/forms';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
@@ -63,13 +63,7 @@
|
||||
$: options = createMonthOptions(year);
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:error
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
bind:show
|
||||
title="Update payment method"
|
||||
headerDivider={false}>
|
||||
<Modal bind:error onSubmit={handleSubmit} bind:show title="Update payment method">
|
||||
{#if selectedPaymentMethod?.expired}
|
||||
<Alert type="error">
|
||||
<svelte:fragment slot="title">This payment method has expired</svelte:fragment>
|
||||
@@ -79,26 +73,20 @@
|
||||
Updates to this payment method will be applied to any linked organizations.
|
||||
{/if}
|
||||
<FormList>
|
||||
<FormItem isMultiple style="align-items: start;">
|
||||
<InputSelect
|
||||
isMultiple
|
||||
fullWidth
|
||||
id="month"
|
||||
label="Month"
|
||||
bind:value={month}
|
||||
{options}
|
||||
required
|
||||
placeholder="Enter expiry month" />
|
||||
<InputNumber
|
||||
isMultiple
|
||||
fullWidth
|
||||
id="year"
|
||||
label="Year"
|
||||
bind:value={year}
|
||||
min={currentYear}
|
||||
required
|
||||
placeholder="Enter expiry year" />
|
||||
</FormItem>
|
||||
<InputSelect
|
||||
id="month"
|
||||
label="Month"
|
||||
bind:value={month}
|
||||
{options}
|
||||
required
|
||||
placeholder="Enter expiry month" />
|
||||
<InputNumber
|
||||
id="year"
|
||||
label="Year"
|
||||
bind:value={year}
|
||||
min={currentYear}
|
||||
required
|
||||
placeholder="Enter expiry year" />
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:show title="Add credits" headerDivider={false} onSubmit={redeem} size="big" bind:error>
|
||||
<Modal bind:show title="Add credits" onSubmit={redeem} bind:error>
|
||||
Apply Appwrite credits to your organization.
|
||||
|
||||
<FormList>
|
||||
|
||||
@@ -55,12 +55,7 @@
|
||||
</script>
|
||||
|
||||
{#if disabled}
|
||||
<Modal
|
||||
bind:show={showDelete}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
title="Unable to delete payment method">
|
||||
<Modal bind:show={showDelete} title="Unable to delete payment method">
|
||||
<p data-private>
|
||||
The {isBackup ? 'backup' : 'default'} payment method cannot be removed as
|
||||
<b>{$organization?.name}</b>
|
||||
@@ -76,9 +71,6 @@
|
||||
bind:show={showDelete}
|
||||
bind:error
|
||||
onSubmit={isBackup ? removeBackuptMethod : removeDefaultMethod}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
title="Remove payment method">
|
||||
<p data-private>
|
||||
Are you sure you want to remove the payment method from <b>{$organization?.name}</b>?
|
||||
|
||||
@@ -29,14 +29,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show
|
||||
bind:error
|
||||
onSubmit={removeAddress}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
title="Remove billing address">
|
||||
<Modal bind:show bind:error onSubmit={removeAddress} title="Remove billing address">
|
||||
<p data-private>
|
||||
Are you sure you want to remove the billing address from <b>{$organization?.name}</b>?
|
||||
</p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Alert, Modal, RadioBoxes } from '$lib/components';
|
||||
import { Button, FormItem, FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { Button, FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -103,13 +103,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show
|
||||
bind:error
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
headerDivider={false}
|
||||
title="Replace billing address">
|
||||
<Modal bind:show bind:error onSubmit={handleSubmit} title="Replace billing address">
|
||||
<p class="text">Replace the existing billing address for your organization.</p>
|
||||
{#if addresses?.total}
|
||||
<FormList>
|
||||
@@ -141,7 +135,7 @@
|
||||
<svelte:fragment slot="new">
|
||||
<span style="padding-inline:0.25rem">Add a new billing address</span>
|
||||
</svelte:fragment>
|
||||
<FormList gap={16} class="u-margin-block-start-24">
|
||||
<FormList>
|
||||
<InputSelect
|
||||
bind:value={country}
|
||||
{options}
|
||||
@@ -166,23 +160,17 @@
|
||||
label="City or suburb"
|
||||
placeholder="Enter your city"
|
||||
required />
|
||||
<FormItem isMultiple>
|
||||
<InputText
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={state}
|
||||
id="state"
|
||||
label="State"
|
||||
placeholder="Enter your state"
|
||||
required />
|
||||
<InputText
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={postalCode}
|
||||
id="zip"
|
||||
label="Postal code"
|
||||
placeholder="Enter postal code" />
|
||||
</FormItem>
|
||||
<InputText
|
||||
bind:value={state}
|
||||
id="state"
|
||||
label="State"
|
||||
placeholder="Enter your state"
|
||||
required />
|
||||
<InputText
|
||||
bind:value={postalCode}
|
||||
id="zip"
|
||||
label="Postal code"
|
||||
placeholder="Enter postal code" />
|
||||
</FormList>
|
||||
</RadioBoxes>
|
||||
</FormList>
|
||||
|
||||
@@ -103,13 +103,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<FakeModal
|
||||
bind:show
|
||||
bind:error
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
headerDivider={false}
|
||||
title="Replace payment method">
|
||||
<FakeModal bind:show bind:error onSubmit={handleSubmit} size="big" title="Replace payment method">
|
||||
<p class="text">Replace the existing payment method for your organization.</p>
|
||||
|
||||
<PaymentBoxes
|
||||
|
||||
@@ -118,7 +118,6 @@
|
||||
state="warning"
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
headerDivider={false}
|
||||
title="Retry payment">
|
||||
<!-- TODO: format currency -->
|
||||
<p class="text">
|
||||
@@ -129,7 +128,6 @@
|
||||
|
||||
<Button
|
||||
external
|
||||
link
|
||||
href={`${endpoint}/organizations/${$page.params.organization}/invoices/${invoice.$id}/view`}>
|
||||
View invoice
|
||||
</Button>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
import PlanSelection from '$lib/components/billing/planSelection.svelte';
|
||||
import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte';
|
||||
import Default from '$lib/components/roles/default.svelte';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { BillingPlan, Dependencies, feedbackDowngradeOptions } from '$lib/constants';
|
||||
import {
|
||||
Button,
|
||||
@@ -259,7 +258,7 @@
|
||||
<Label class="label u-margin-block-start-16">Select plan</Label>
|
||||
<p class="text">
|
||||
For more details on our plans, visit our
|
||||
<Button href="https://appwrite.io/pricing" external link>pricing page</Button>.
|
||||
<Button href="https://appwrite.io/pricing" external>pricing page</Button>.
|
||||
</p>
|
||||
{#if !selfService}
|
||||
<Alert class="u-position-relative u-margin-block-start-16" type="info"
|
||||
@@ -317,7 +316,7 @@
|
||||
</FormList>
|
||||
{/if}
|
||||
{#if isDowngrade}
|
||||
<FormList class="u-margin-block-start-24">
|
||||
<FormList>
|
||||
<InputSelect
|
||||
id="reason"
|
||||
label="Reason for plan change"
|
||||
|
||||
@@ -51,12 +51,7 @@
|
||||
$: isUser = selectedMember?.userId === $user?.$id;
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteMembership}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal bind:show={showDelete} onSubmit={deleteMembership}>
|
||||
<svelte:fragment slot="title">
|
||||
{isUser ? 'Leave organization' : 'Delete member'}
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import {
|
||||
AvatarGroup,
|
||||
DropList,
|
||||
|
||||
@@ -3,15 +3,6 @@
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { AvatarInitials, DropList, DropListItem, PaginationWithLimit } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import {
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { Container, ContainerHeader } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { newMemberModal, organization } from '$lib/stores/organization';
|
||||
@@ -24,6 +15,8 @@
|
||||
import Edit from './edit.svelte';
|
||||
import { getRoleLabel } from '$lib/stores/billing';
|
||||
import { Drop } from '$lib/components';
|
||||
import { Layout, Table } from '@appwrite.io/pink-svelte';
|
||||
|
||||
import Upgrade from '$lib/components/roles/upgrade.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
@@ -68,127 +61,119 @@
|
||||
buttonText={$isOwner ? 'Invite' : ''}
|
||||
buttonMethod={() => newMemberModal.set(true)}
|
||||
showAlert={false} />
|
||||
|
||||
<TableScroll>
|
||||
<TableHeader>
|
||||
<TableCellHead width={160}>Name</TableCellHead>
|
||||
<TableCellHead width={120}>Email</TableCellHead>
|
||||
<div style:--p-col-width={120} class="table-thead-col" role="columnheader">
|
||||
<span class="u-flex u-cross-baseline">
|
||||
<span class="eyebrow-heading-3"> Role </span>
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<TableCellHead width={90}>2FA</TableCellHead>
|
||||
{#if $isOwner}
|
||||
<TableCellHead width={30} />
|
||||
{/if}
|
||||
</TableHeader>
|
||||
<TableBody
|
||||
service="members"
|
||||
total={data.organizationMembers.total}
|
||||
event="members_list">
|
||||
{#each data.organizationMembers.memberships as member, index}
|
||||
<TableRow>
|
||||
<TableCell title="Name">
|
||||
<div class="u-flex u-gap-12 u-cross-center">
|
||||
<AvatarInitials size={40} name={member.userName} />
|
||||
<span class="text u-trim">
|
||||
{member.userName ? member.userName : 'n/a'}
|
||||
</span>
|
||||
{#if member.invited && !member.joined}
|
||||
<Pill warning>Pending</Pill>
|
||||
{/if}
|
||||
<Table.Root>
|
||||
<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>
|
||||
</TableCell>
|
||||
<TableCellText title="Email">{member.userEmail}</TableCellText>
|
||||
<TableCellText title="Role"
|
||||
>{member.roles
|
||||
.map((role) => getRoleLabel(role))
|
||||
.join(', ')}</TableCellText>
|
||||
<TableCellText title="2FA">
|
||||
<Pill success={member.mfa}>
|
||||
{#if member.mfa}
|
||||
<span
|
||||
class="icon-check-circle u-color-text-success"
|
||||
aria-hidden="true" />
|
||||
<p class="text">enabled</p>
|
||||
{:else}
|
||||
<p class="text">disabled</p>
|
||||
{/if}
|
||||
</Pill>
|
||||
</TableCellText>
|
||||
{#if $isOwner}
|
||||
<TableCell showOverflow right>
|
||||
<DropList
|
||||
bind:show={showDropdown[index]}
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="More options"
|
||||
on:click|preventDefault={() => {
|
||||
showDropdown[index] = !showDropdown[index];
|
||||
</svelte:fragment>
|
||||
</Drop>
|
||||
</Table.Header.Cell>
|
||||
<Table.Header.Cell>2FA</Table.Header.Cell>
|
||||
{#if $isOwner}
|
||||
<Table.Header.Cell />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{#each data.organizationMembers.memberships as member, index}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<AvatarInitials size={40} name={member.userName} />
|
||||
<span class="u-trim">
|
||||
{member.userName ? member.userName : 'n/a'}
|
||||
</span>
|
||||
{#if member.invited && !member.joined}
|
||||
<Pill warning>Pending</Pill>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{member.userEmail}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{member.roles.map((role) => getRoleLabel(role)).join(', ')}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Pill success={member.mfa}>
|
||||
{#if member.mfa}
|
||||
<span
|
||||
class="icon-check-circle u-color-text-success"
|
||||
aria-hidden="true" />
|
||||
<p class="text">enabled</p>
|
||||
{:else}
|
||||
<p class="text">disabled</p>
|
||||
{/if}
|
||||
</Pill>
|
||||
</Table.Cell>
|
||||
{#if $isOwner}
|
||||
<Table.Cell>
|
||||
<DropList
|
||||
bind:show={showDropdown[index]}
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="More options"
|
||||
on:click|preventDefault={() => {
|
||||
showDropdown[index] = !showDropdown[index];
|
||||
}}>
|
||||
<span class="icon-dots-horizontal" aria-hidden="true" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
<DropListItem
|
||||
icon="pencil"
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showEdit = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
<span class="icon-dots-horizontal" aria-hidden="true" />
|
||||
</button>
|
||||
<svelte:fragment slot="list">
|
||||
Edit role
|
||||
</DropListItem>
|
||||
{#if member.invited && !member.joined}
|
||||
<DropListItem
|
||||
icon="pencil"
|
||||
icon="refresh"
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showEdit = true;
|
||||
resend(member);
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Edit role
|
||||
Resend
|
||||
</DropListItem>
|
||||
{#if member.invited && !member.joined}
|
||||
<DropListItem
|
||||
icon="refresh"
|
||||
on:click={() => {
|
||||
resend(member);
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Resend
|
||||
</DropListItem>
|
||||
{/if}
|
||||
<DropListItem
|
||||
icon="trash"
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showDelete = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Remove
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</TableCell>
|
||||
{/if}
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</TableScroll>
|
||||
{/if}
|
||||
<DropListItem
|
||||
icon="trash"
|
||||
on:click={() => {
|
||||
selectedMember = member;
|
||||
showDelete = true;
|
||||
showDropdown[index] = false;
|
||||
}}>
|
||||
Remove
|
||||
</DropListItem>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
</Table.Cell>
|
||||
{/if}
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Members"
|
||||
|
||||
@@ -93,13 +93,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:error
|
||||
bind:show
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
title="Request BAA"
|
||||
headerDivider={false}>
|
||||
<Modal bind:error bind:show onSubmit={handleSubmit} title="Request BAA">
|
||||
<FormList>
|
||||
<InputEmail label="Email" placeholder="Enter email" id="email" bind:value={email} />
|
||||
<InputSelect
|
||||
|
||||
@@ -95,13 +95,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:error
|
||||
bind:show
|
||||
onSubmit={handleSubmit}
|
||||
size="big"
|
||||
headerDivider={false}
|
||||
title="Request SOC-2">
|
||||
<Modal bind:error bind:show onSubmit={handleSubmit} title="Request SOC-2">
|
||||
<FormList>
|
||||
<InputEmail label="Email" placeholder="Enter email" id="email" bind:value={email} />
|
||||
<InputSelect
|
||||
|
||||
+1
-8
@@ -103,14 +103,7 @@
|
||||
</script>
|
||||
|
||||
<div class="max-height-dialog">
|
||||
<Modal
|
||||
title="Delete organization"
|
||||
onSubmit={deleteOrg}
|
||||
bind:show={showDelete}
|
||||
bind:error
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete organization" onSubmit={deleteOrg} bind:show={showDelete} bind:error>
|
||||
{#if upcomingInvoice}
|
||||
<Alert type="warning">
|
||||
<span slot="title">
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
canSeeDatabases,
|
||||
canSeeFunctions,
|
||||
canSeeMessages,
|
||||
canWriteProjects
|
||||
canWriteProjects,
|
||||
canWriteSites
|
||||
} from '$lib/stores/roles';
|
||||
|
||||
onMount(() => {
|
||||
@@ -88,6 +89,15 @@
|
||||
group: 'navigation',
|
||||
disabled: !$canWriteProjects
|
||||
},
|
||||
{
|
||||
label: 'Go to Sites',
|
||||
callback: () => {
|
||||
goto(`${base}/project-${$project.$id}/sites`);
|
||||
},
|
||||
keys: ['g', 'i'],
|
||||
group: 'navigation',
|
||||
disabled: !$canWriteSites
|
||||
},
|
||||
{
|
||||
label: 'Go to Overview',
|
||||
callback: () => {
|
||||
|
||||
@@ -35,24 +35,18 @@
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<ContainerHeader title="Users" isFlex={false} total={data.users.total} let:isButtonDisabled>
|
||||
<SearchQuery search={data.search} placeholder="Search by name, email, phone, or ID">
|
||||
{#if $canWriteUsers}
|
||||
<Tooltip disabled={!isButtonDisabled}>
|
||||
<div>
|
||||
<Button
|
||||
on:mousedown={() => ($showCreateUser = true)}
|
||||
event="create_user"
|
||||
disabled={isButtonDisabled}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create user</span>
|
||||
</Button>
|
||||
</div>
|
||||
<span slot="tooltip">Upgrade to add more users</span>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</SearchQuery>
|
||||
</ContainerHeader>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery search={data.search} placeholder="Search by name, email, phone, or ID" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button on:mousedown={() => ($showCreateUser = true)} event="create_user" size="s">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create user</span>
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if data.users.total}
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { InputPhone, InputOTP } from '$lib/elements/forms';
|
||||
import { Button, Form, FormItem, FormItemPart } from '$lib/elements/forms';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../../store';
|
||||
import { upgradeURL } from '$lib/stores/billing';
|
||||
@@ -17,7 +17,6 @@
|
||||
import { app } from '$lib/stores/app';
|
||||
import Empty from '$lib/components/empty.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { Tooltip } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let numbers: Models.MockNumber[] = $project?.authMockNumbers ?? [];
|
||||
@@ -150,73 +149,67 @@
|
||||
{:else if numbers?.length > 0}
|
||||
<ul class="form-list u-gap-8">
|
||||
{#each numbers as number, index}
|
||||
<FormItem isMultiple>
|
||||
<InputPhone
|
||||
id={`key-${index}`}
|
||||
bind:value={number.phone}
|
||||
fullWidth
|
||||
placeholder="Enter phone number"
|
||||
label="Phone number"
|
||||
showLabel={index === 0}
|
||||
minlength={9}
|
||||
maxlength={16}
|
||||
required>
|
||||
<button
|
||||
slot="options"
|
||||
on:click={() => (number.phone = generateNumber())}
|
||||
class="options-list-button"
|
||||
aria-label="regenerate text"
|
||||
type="button">
|
||||
<Tooltip>
|
||||
<span class="icon-refresh" aria-hidden="true"></span>
|
||||
<span slot="tooltip">Regenerate</span>
|
||||
</Tooltip>
|
||||
</button>
|
||||
</InputPhone>
|
||||
<InputOTP
|
||||
id={`value-${index}`}
|
||||
bind:value={number.otp}
|
||||
fullWidth
|
||||
placeholder="Enter value"
|
||||
label="Verification code"
|
||||
maxlength={6}
|
||||
pattern={'^[0-9]{6}$'}
|
||||
patternError="The value must contain 6 digits"
|
||||
showLabel={index === 0}
|
||||
required>
|
||||
<button
|
||||
slot="options"
|
||||
on:click={() => (number.otp = generateOTP())}
|
||||
class="options-list-button"
|
||||
aria-label="regenerate text"
|
||||
type="button">
|
||||
<Tooltip
|
||||
><span class="icon-refresh" aria-hidden="true"></span><span
|
||||
slot="tooltip">Regenerate</span
|
||||
></Tooltip>
|
||||
</button>
|
||||
</InputOTP>
|
||||
<FormItemPart>
|
||||
<Button
|
||||
text
|
||||
disabled={numbers.length === 0}
|
||||
class={'u-padding-4 ' +
|
||||
(index === 0 ? 'u-margin-block-start-24' : '')}
|
||||
on:click={() => {
|
||||
deletePhoneNumber(index);
|
||||
}}>
|
||||
<Tooltip
|
||||
><span class="icon-refresh" aria-hidden="true"></span><span
|
||||
slot="tooltip">Regenerate</span
|
||||
></Tooltip>
|
||||
</Button>
|
||||
</FormItemPart>
|
||||
</FormItem>
|
||||
<InputPhone
|
||||
id={`key-${index}`}
|
||||
bind:value={number.phone}
|
||||
fullWidth
|
||||
placeholder="Enter phone number"
|
||||
label="Phone number"
|
||||
showLabel={index === 0}
|
||||
minlength={9}
|
||||
maxlength={16}
|
||||
required>
|
||||
<button
|
||||
slot="options"
|
||||
on:click={() => (number.phone = generateNumber())}
|
||||
class="options-list-button"
|
||||
aria-label="regenerate text"
|
||||
type="button">
|
||||
<Tooltip>
|
||||
<span class="icon-refresh" aria-hidden="true"></span>
|
||||
<span slot="tooltip">Regenerate</span>
|
||||
</Tooltip>
|
||||
</button>
|
||||
</InputPhone>
|
||||
<InputOTP
|
||||
id={`value-${index}`}
|
||||
bind:value={number.otp}
|
||||
fullWidth
|
||||
placeholder="Enter value"
|
||||
label="Verification code"
|
||||
maxlength={6}
|
||||
pattern={'^[0-9]{6}$'}
|
||||
patternError="The value must contain 6 digits"
|
||||
showLabel={index === 0}
|
||||
required>
|
||||
<button
|
||||
slot="options"
|
||||
on:click={() => (number.otp = generateOTP())}
|
||||
class="options-list-button"
|
||||
aria-label="regenerate text"
|
||||
type="button">
|
||||
<Tooltip
|
||||
><span class="icon-refresh" aria-hidden="true"></span><span
|
||||
slot="tooltip">Regenerate</span
|
||||
></Tooltip>
|
||||
</button>
|
||||
</InputOTP>
|
||||
<Button
|
||||
text
|
||||
disabled={numbers.length === 0}
|
||||
class={'u-padding-4 ' + (index === 0 ? 'u-margin-block-start-24' : '')}
|
||||
on:click={() => {
|
||||
deletePhoneNumber(index);
|
||||
}}>
|
||||
<Tooltip
|
||||
><span class="icon-refresh" aria-hidden="true"></span><span
|
||||
slot="tooltip">Regenerate</span
|
||||
></Tooltip>
|
||||
</Button>
|
||||
{/each}
|
||||
</ul>
|
||||
{#if numbers?.length < 10}
|
||||
<Button
|
||||
noMargin
|
||||
text
|
||||
on:click={() =>
|
||||
addPhoneNumber({
|
||||
|
||||
@@ -4,15 +4,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableRowLink,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableCell
|
||||
} from '$lib/elements/table';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import {
|
||||
Empty,
|
||||
@@ -28,12 +19,11 @@
|
||||
import { base } from '$app/paths';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import type { PageData } from './$types';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { writable } from 'svelte/store';
|
||||
import { readOnly } from '$lib/stores/billing';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { canWriteTeams } from '$lib/stores/roles';
|
||||
import { Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { Tooltip, Layout, Table } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -44,55 +34,42 @@
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<ContainerHeader
|
||||
title="Teams"
|
||||
isFlex={false}
|
||||
total={data.teams.total}
|
||||
buttonDisabled={isCloud && $readOnly}
|
||||
let:isButtonDisabled>
|
||||
<SearchQuery search={data.search} placeholder="Search by name">
|
||||
{#if $canWriteTeams}
|
||||
<Tooltip disabled={!isButtonDisabled}>
|
||||
<div>
|
||||
<Button
|
||||
on:click={() => ($showCreateTeam = true)}
|
||||
event="create_team"
|
||||
disabled={isButtonDisabled}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create team</span>
|
||||
</Button>
|
||||
</div>
|
||||
<span slot="tooltip">Upgrade to add more teams</span>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</SearchQuery>
|
||||
</ContainerHeader>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery search={data.search} placeholder="Search by name" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<Button on:mousedown={() => ($showCreateTeam = true)} event="create_user" size="s">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create team</span>
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if data.teams.total}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCellHead>Name</TableCellHead>
|
||||
<TableCellHead onlyDesktop>Members</TableCellHead>
|
||||
<TableCellHead onlyDesktop>Created</TableCellHead>
|
||||
</TableHeader>
|
||||
<TableBody service="teams" total={data.teams.total}>
|
||||
{#each data.teams.teams as team}
|
||||
<TableRowLink href={`${base}/project-${project}/auth/teams/team-${team.$id}`}>
|
||||
<TableCell title="Name">
|
||||
<div class="u-flex u-gap-12 u-cross-center">
|
||||
<AvatarInitials size={32} name={team.name} />
|
||||
<span class="text u-trim">{team.name}</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCellText onlyDesktop title="Members"
|
||||
>{team.total} members</TableCellText>
|
||||
<TableCellText onlyDesktop title="Created">
|
||||
{toLocaleDateTime(team.$createdAt)}
|
||||
</TableCellText>
|
||||
</TableRowLink>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell>Name</Table.Header.Cell>
|
||||
<Table.Header.Cell>Members</Table.Header.Cell>
|
||||
<Table.Header.Cell>Created</Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each data.teams.teams as team}
|
||||
<Table.Link href={`${base}/project-${project}/auth/teams/team-${team.$id}`}>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<AvatarInitials size={32} name={team.name} />
|
||||
<span class="u-trim">{team.name}</span>
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{team.total} members
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{toLocaleDateTime(team.$createdAt)}
|
||||
</Table.Cell>
|
||||
</Table.Link>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
|
||||
<PaginationWithLimit
|
||||
name="Teams"
|
||||
|
||||
+1
-7
@@ -37,13 +37,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete Member"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteMembership}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete Member" bind:show={showDelete} onSubmit={deleteMembership}>
|
||||
<p data-private>
|
||||
Are you sure you want to delete <b>{selectedMembership.userName}</b> from '{selectedMembership.teamName}'?
|
||||
</p>
|
||||
|
||||
@@ -28,13 +28,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete team"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteTeam}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete team" bind:show={showDelete} onSubmit={deleteTeam}>
|
||||
<p data-private>
|
||||
Are you sure you want to delete <b>{team.name}</b>?
|
||||
</p>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormItem, FormItemPart, InputText } from '$lib/elements/forms';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -66,40 +66,32 @@
|
||||
<ul class="form-list">
|
||||
{#if prefs}
|
||||
{#each prefs as [key, value], index}
|
||||
<FormItem isMultiple>
|
||||
<InputText
|
||||
id={`key-${index}`}
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={key}
|
||||
placeholder="Enter key"
|
||||
label="Key"
|
||||
required />
|
||||
<InputText
|
||||
id={`key-${index}`}
|
||||
bind:value={key}
|
||||
placeholder="Enter key"
|
||||
label="Key"
|
||||
required />
|
||||
|
||||
<InputText
|
||||
id={`value-${index}`}
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value
|
||||
placeholder="Enter value"
|
||||
label="Value"
|
||||
required />
|
||||
<FormItemPart alignEnd>
|
||||
<Button
|
||||
text
|
||||
disabled={(!key || !value) && index === 0}
|
||||
on:click={() => {
|
||||
if (index === 0) {
|
||||
prefs = [['', '']];
|
||||
} else {
|
||||
prefs.splice(index, 1);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</FormItemPart>
|
||||
</FormItem>
|
||||
<InputText
|
||||
id={`value-${index}`}
|
||||
bind:value
|
||||
placeholder="Enter value"
|
||||
label="Value"
|
||||
required />
|
||||
<Button
|
||||
text
|
||||
disabled={(!key || !value) && index === 0}
|
||||
on:click={() => {
|
||||
if (index === 0) {
|
||||
prefs = [['', '']];
|
||||
} else {
|
||||
prefs.splice(index, 1);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
|
||||
@@ -44,14 +44,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
onSubmit={reset}
|
||||
size="big"
|
||||
{error}
|
||||
bind:show
|
||||
headerDivider={false}>
|
||||
<Modal onSubmit={reset} {error} bind:show>
|
||||
<svelte:fragment slot="title">Reset Email Template?</svelte:fragment>
|
||||
<p class="text">
|
||||
Are you sure you want to reset the email template?
|
||||
|
||||
@@ -39,14 +39,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
onSubmit={reset}
|
||||
size="big"
|
||||
{error}
|
||||
bind:show
|
||||
headerDivider={false}>
|
||||
<Modal onSubmit={reset} {error} bind:show>
|
||||
<svelte:fragment slot="title">Reset SMS Template?</svelte:fragment>
|
||||
<p class="text">
|
||||
Are you sure you want to reset the SMS template?
|
||||
|
||||
@@ -31,13 +31,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete all sessions"
|
||||
bind:show={showDeleteAll}
|
||||
onSubmit={deleteAllSessions}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete all sessions" bind:show={showDeleteAll} onSubmit={deleteAllSessions}>
|
||||
<p data-private>
|
||||
Are you sure you want to delete <b>all of {$user.name}'s sessions?</b>
|
||||
</p>
|
||||
|
||||
@@ -39,13 +39,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete member"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteMembership}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete member" bind:show={showDelete} onSubmit={deleteMembership}>
|
||||
{#if selectedMembership}
|
||||
<p data-private>
|
||||
Are you sure you want to delete <b>{selectedMembership.userName}</b> from '{selectedMembership.teamName}'?
|
||||
|
||||
@@ -38,10 +38,7 @@
|
||||
title="Delete authentication method"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteProvider}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
bind:error
|
||||
headerDivider={false}>
|
||||
bind:error>
|
||||
<p>Are you sure you want to delete this authentication?</p>
|
||||
<p class="text u-bold">This action is irreversible.</p>
|
||||
<svelte:fragment slot="footer">
|
||||
|
||||
@@ -31,13 +31,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete sessions"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteSession}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}>
|
||||
<Modal title="Delete sessions" bind:show={showDelete} onSubmit={deleteSession}>
|
||||
<p>Are you sure you want to delete this session?</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
|
||||
@@ -29,14 +29,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete user"
|
||||
bind:show={showDelete}
|
||||
onSubmit={deleteUser}
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
bind:error>
|
||||
<Modal title="Delete user" bind:show={showDelete} onSubmit={deleteUser} bind:error>
|
||||
<p data-private>Are you sure you want to delete <b>{$user.name}</b> from '{$project.name}'?</p>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
|
||||
|
||||
@@ -141,14 +141,7 @@
|
||||
</div>
|
||||
</FloatingActionBar>
|
||||
|
||||
<Modal
|
||||
title="Delete identity"
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
bind:show={showDelete}
|
||||
onSubmit={handleDelete}
|
||||
headerDivider={false}
|
||||
closable={!deleting}>
|
||||
<Modal title="Delete identity" bind:show={showDelete} onSubmit={handleDelete} closable={!deleting}>
|
||||
<p class="text" data-private>
|
||||
Are you sure you want to delete <b>{selectedIds.length}</b>
|
||||
{selectedIds.length > 1 ? 'identities' : 'identity'}?
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
import { page } from '$app/stores';
|
||||
import { base } from '$app/paths';
|
||||
import { AvatarInitials, EmptySearch } from '$lib/components';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableRowLink,
|
||||
TableCellHead,
|
||||
TableCellText,
|
||||
TableCell
|
||||
} from '$lib/elements/table';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Container } from '$lib/layout';
|
||||
import DeleteMembership from '../deleteMembership.svelte';
|
||||
@@ -18,6 +9,7 @@
|
||||
import type { PageData } from './$types';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import { Table, Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -29,43 +21,43 @@
|
||||
|
||||
<Container>
|
||||
{#if data.memberships.total}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableCellHead>Name</TableCellHead>
|
||||
<TableCellHead onlyDesktop>Roles</TableCellHead>
|
||||
<TableCellHead onlyDesktop>Joined</TableCellHead>
|
||||
<TableCellHead width={30} />
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each data.memberships.memberships as membership}
|
||||
<TableRowLink
|
||||
href={`${base}/project-${project}/auth/teams/team-${membership.teamId}`}>
|
||||
<TableCellText title="Name">
|
||||
<div class="u-flex u-cross-center u-gap-12">
|
||||
<AvatarInitials size={32} name={membership.teamName} />
|
||||
<span>{membership.teamName ? membership.teamName : 'n/a'}</span>
|
||||
</div>
|
||||
</TableCellText>
|
||||
<TableCellText onlyDesktop title="Roles">{membership.roles}</TableCellText>
|
||||
<TableCellText onlyDesktop title="Joined">
|
||||
{toLocaleDateTime(membership.joined)}
|
||||
</TableCellText>
|
||||
<TableCell width={30}>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="Delete item"
|
||||
on:click|preventDefault={() => {
|
||||
selectedMembership = membership;
|
||||
showDelete = true;
|
||||
trackEvent('click_delete_membership');
|
||||
}}>
|
||||
<span class="icon-trash" aria-hidden="true" />
|
||||
</button>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Table.Root>
|
||||
<svelte:fragment slot="header">
|
||||
<Table.Header.Cell>Name</Table.Header.Cell>
|
||||
<Table.Header.Cell>Roles</Table.Header.Cell>
|
||||
<Table.Header.Cell>Joined</Table.Header.Cell>
|
||||
<Table.Header.Cell />
|
||||
</svelte:fragment>
|
||||
{#each data.memberships.memberships as membership}
|
||||
<Table.Link
|
||||
href={`${base}/project-${project}/auth/teams/team-${membership.teamId}`}>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<AvatarInitials size={32} name={membership.teamName} />
|
||||
<span>{membership.teamName ? membership.teamName : 'n/a'}</span>
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{membership.roles}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
{toLocaleDateTime(membership.joined)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<button
|
||||
class="button is-only-icon is-text"
|
||||
aria-label="Delete item"
|
||||
on:click|preventDefault={() => {
|
||||
selectedMembership = membership;
|
||||
showDelete = true;
|
||||
trackEvent('click_delete_membership');
|
||||
}}>
|
||||
<span class="icon-trash" aria-hidden="true" />
|
||||
</button>
|
||||
</Table.Cell>
|
||||
</Table.Link>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{:else}
|
||||
<EmptySearch>
|
||||
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
|
||||
|
||||
@@ -143,14 +143,7 @@
|
||||
</div>
|
||||
</FloatingActionBar>
|
||||
|
||||
<Modal
|
||||
title="Delete target"
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
bind:show={showDelete}
|
||||
onSubmit={handleDelete}
|
||||
headerDivider={false}
|
||||
closable={!deleting}>
|
||||
<Modal title="Delete target" bind:show={showDelete} onSubmit={handleDelete} closable={!deleting}>
|
||||
<p class="text" data-private>
|
||||
Are you sure you want to delete <b>{selectedIds.length}</b>
|
||||
{selectedIds.length > 1 ? 'targets' : 'target'}?
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { CardGrid, Heading } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, FormItem, FormItemPart, InputText } from '$lib/elements/forms';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -59,40 +59,32 @@
|
||||
<ul class="form-list">
|
||||
{#if prefs}
|
||||
{#each prefs as [key, value], index}
|
||||
<FormItem isMultiple>
|
||||
<InputText
|
||||
id={`key-${index}`}
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value={key}
|
||||
placeholder="Enter key"
|
||||
label="Key"
|
||||
required />
|
||||
<InputText
|
||||
id={`key-${index}`}
|
||||
bind:value={key}
|
||||
placeholder="Enter key"
|
||||
label="Key"
|
||||
required />
|
||||
|
||||
<InputText
|
||||
id={`value-${index}`}
|
||||
isMultiple
|
||||
fullWidth
|
||||
bind:value
|
||||
placeholder="Enter value"
|
||||
label="Value"
|
||||
required />
|
||||
<FormItemPart alignEnd>
|
||||
<Button
|
||||
text
|
||||
disabled={(!key || !value) && index === 0}
|
||||
on:click={() => {
|
||||
if (index === 0 && prefs?.length === 1) {
|
||||
prefs = [['', '']];
|
||||
} else {
|
||||
prefs.splice(index, 1);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
</FormItemPart>
|
||||
</FormItem>
|
||||
<InputText
|
||||
id={`value-${index}`}
|
||||
bind:value
|
||||
placeholder="Enter value"
|
||||
label="Value"
|
||||
required />
|
||||
<Button
|
||||
text
|
||||
disabled={(!key || !value) && index === 0}
|
||||
on:click={() => {
|
||||
if (index === 0 && prefs?.length === 1) {
|
||||
prefs = [['', '']];
|
||||
} else {
|
||||
prefs.splice(index, 1);
|
||||
prefs = prefs;
|
||||
}
|
||||
}}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
$: if (!showCreate) {
|
||||
console.log('test');
|
||||
selectedVar = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { Empty, PaginationWithLimit, ViewSelector } from '$lib/components';
|
||||
import { Empty, PaginationWithLimit, SearchQuery, ViewSelector } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Container, ContainerHeader } from '$lib/layout';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
@@ -13,9 +13,8 @@
|
||||
import { columns } from './store';
|
||||
import Table from './table.svelte';
|
||||
import { registerCommands } from '$lib/commandCenter';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { canWriteDatabases } from '$lib/stores/roles';
|
||||
import { Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { Layout, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -44,36 +43,27 @@
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<ContainerHeader
|
||||
title="Databases"
|
||||
total={data?.databases?.total}
|
||||
on:data={(data) => {
|
||||
isCreationDisabled = data.detail.isButtonDisabled;
|
||||
}}>
|
||||
<svelte:fragment>
|
||||
<div class="u-flex u-gap-16 u-cross-center u-flex-wrap">
|
||||
<ViewSelector
|
||||
{columns}
|
||||
view={data.view}
|
||||
hideColumns={!data.databases.total}
|
||||
hideView={!data.databases.total} />
|
||||
{#if $canWriteDatabases}
|
||||
<Tooltip disabled={!isCreationDisabled}>
|
||||
<div>
|
||||
<Button
|
||||
on:click={() => (showCreate = true)}
|
||||
event="create_database"
|
||||
disabled={isCreationDisabled}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create database</span>
|
||||
</Button>
|
||||
</div>
|
||||
<span slot="tooltip">Upgrade to add more databases</span>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</ContainerHeader>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery search={data.search} placeholder="Search databases" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<ViewSelector
|
||||
{columns}
|
||||
view={data.view}
|
||||
hideColumns={!data.databases.total}
|
||||
hideView={!data.databases.total} />
|
||||
{#if $canWriteDatabases}
|
||||
<Button
|
||||
on:click={() => (showCreate = true)}
|
||||
event="create_database"
|
||||
disabled={isCreationDisabled}>
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create database</span>
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if data.databases.total}
|
||||
{#if data.view === 'grid'}
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
import { CARD_LIMIT, Dependencies } from '$lib/constants';
|
||||
import { getLimit, getPage, getView, pageToOffset, View } from '$lib/helpers/load';
|
||||
import { getLimit, getPage, getSearch, getView, pageToOffset, View } from '$lib/helpers/load';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { type Models, Query } from '@appwrite.io/console';
|
||||
import { timeFromNow } from '$lib/helpers/date';
|
||||
import type { PageLoad } from './$types';
|
||||
import type { BackupPolicy } from '$lib/sdk/backups';
|
||||
import { isSelfHosted } from '$lib/system';
|
||||
|
||||
export const load: PageLoad = async ({ url, route, depends }) => {
|
||||
depends(Dependencies.DATABASES);
|
||||
|
||||
const page = getPage(url);
|
||||
const search = getSearch(url);
|
||||
const limit = getLimit(url, route, CARD_LIMIT);
|
||||
const view = getView(url, route, View.Grid);
|
||||
const offset = pageToOffset(page, limit);
|
||||
|
||||
const { databases, policies, lastBackups } = await fetchDatabasesAndBackups(limit, offset);
|
||||
const { databases, policies, lastBackups } = await fetchDatabasesAndBackups(
|
||||
limit,
|
||||
offset,
|
||||
search
|
||||
);
|
||||
|
||||
return {
|
||||
offset,
|
||||
limit,
|
||||
view,
|
||||
search,
|
||||
policies,
|
||||
databases,
|
||||
lastBackups
|
||||
@@ -27,12 +34,11 @@ export const load: PageLoad = async ({ url, route, depends }) => {
|
||||
};
|
||||
|
||||
// TODO: @itznotabug we should improve this!
|
||||
async function fetchDatabasesAndBackups(limit: number, offset: number) {
|
||||
const databases = await sdk.forProject.databases.list([
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt')
|
||||
]);
|
||||
async function fetchDatabasesAndBackups(limit: number, offset: number, search: string) {
|
||||
const databases = await sdk.forProject.databases.list(
|
||||
[Query.limit(limit), Query.offset(offset), Query.orderDesc('$createdAt')],
|
||||
search || undefined
|
||||
);
|
||||
|
||||
const [policies, lastBackups] = await Promise.all([
|
||||
await fetchPolicies(databases),
|
||||
@@ -43,6 +49,8 @@ async function fetchDatabasesAndBackups(limit: number, offset: number) {
|
||||
}
|
||||
|
||||
async function fetchPolicies(databases: Models.DatabaseList) {
|
||||
if (isSelfHosted) return {};
|
||||
|
||||
const databasePolicies: Record<string, BackupPolicy[]> = {};
|
||||
|
||||
await Promise.all(
|
||||
@@ -68,6 +76,8 @@ async function fetchPolicies(databases: Models.DatabaseList) {
|
||||
}
|
||||
|
||||
async function fetchLastBackups(databases: Models.DatabaseList) {
|
||||
if (isSelfHosted) return {};
|
||||
|
||||
const lastBackups: Record<string, string> = {};
|
||||
|
||||
await Promise.all(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Empty, PaginationWithLimit } from '$lib/components';
|
||||
import { Empty, PaginationWithLimit, SearchQuery, ViewSelector } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Container, GridHeader } from '$lib/layout';
|
||||
import { columns, showCreate } from './store';
|
||||
@@ -7,24 +7,30 @@
|
||||
import Grid from './grid.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { canWriteCollections } from '$lib/stores/roles';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<GridHeader
|
||||
title="Collections"
|
||||
{columns}
|
||||
view={data.view}
|
||||
hideColumns={!data.collections.total}
|
||||
hideView={!data.collections.total}>
|
||||
{#if $canWriteCollections}
|
||||
<Button on:click={() => ($showCreate = true)} event="create_collection">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create collection</span>
|
||||
</Button>
|
||||
{/if}
|
||||
</GridHeader>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<SearchQuery search={data.search} placeholder="Search collections" />
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
<ViewSelector
|
||||
{columns}
|
||||
view={data.view}
|
||||
hideColumns={!data.collections.total}
|
||||
hideView={!data.collections.total} />
|
||||
{#if $canWriteCollections}
|
||||
<Button on:click={() => ($showCreate = true)} event="create_collection">
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
<span class="text">Create collection</span>
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if data.collections.total}
|
||||
{#if data.view === 'grid'}
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { getLimit, getPage, getView, pageToOffset, View } from '$lib/helpers/load';
|
||||
import { getLimit, getPage, getSearch, getView, pageToOffset, View } from '$lib/helpers/load';
|
||||
import type { PageLoad } from './$types';
|
||||
import { CARD_LIMIT, Dependencies } from '$lib/constants';
|
||||
|
||||
export const load: PageLoad = async ({ params, url, route, depends }) => {
|
||||
depends(Dependencies.COLLECTIONS);
|
||||
const page = getPage(url);
|
||||
const search = getSearch(url);
|
||||
const limit = getLimit(url, route, CARD_LIMIT);
|
||||
const view = getView(url, route, View.Grid);
|
||||
const offset = pageToOffset(page, limit);
|
||||
const collections = await sdk.forProject.databases.listCollections(params.database, [
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('')
|
||||
]);
|
||||
const collections = await sdk.forProject.databases.listCollections(
|
||||
params.database,
|
||||
[Query.limit(limit), Query.offset(offset), Query.orderDesc('')],
|
||||
search || undefined
|
||||
);
|
||||
|
||||
return {
|
||||
offset,
|
||||
limit,
|
||||
search,
|
||||
view,
|
||||
collections
|
||||
};
|
||||
|
||||
-1
@@ -231,7 +231,6 @@
|
||||
|
||||
<Modal
|
||||
title="Create backup policy"
|
||||
size="big"
|
||||
onSubmit={createPolicies}
|
||||
bind:show={$showCreatePolicy}
|
||||
bind:error={policyCreateError}>
|
||||
|
||||
+20
-17
@@ -3,6 +3,7 @@ import { CARD_LIMIT, Dependencies } from '$lib/constants';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import type { BackupArchive, BackupArchiveList, BackupPolicyList } from '$lib/sdk/backups';
|
||||
import { isCloud } from '$lib/system';
|
||||
|
||||
export const load = async ({ params, url, route, depends }) => {
|
||||
depends(Dependencies.BACKUPS);
|
||||
@@ -14,24 +15,26 @@ export const load = async ({ params, url, route, depends }) => {
|
||||
let backups: BackupArchiveList = { total: 0, archives: [] };
|
||||
let policies: BackupPolicyList = { total: 0, policies: [] };
|
||||
|
||||
try {
|
||||
[backups, policies] = await Promise.all([
|
||||
sdk.forProject.backups.listArchives([
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', params.database)
|
||||
]),
|
||||
if (isCloud) {
|
||||
try {
|
||||
[backups, policies] = await Promise.all([
|
||||
sdk.forProject.backups.listArchives([
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', params.database)
|
||||
]),
|
||||
|
||||
sdk.forProject.backups.listPolicies([
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', params.database)
|
||||
])
|
||||
]);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
sdk.forProject.backups.listPolicies([
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', params.database)
|
||||
])
|
||||
]);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const archivesByPolicy = groupArchivesByPolicy(backups.archives);
|
||||
|
||||
+1
-11
@@ -11,8 +11,6 @@
|
||||
import { parseExpression } from 'cron-parser';
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
|
||||
import EmptyDark from '$lib/images/backups/backups-dark.png';
|
||||
import EmptyLight from '$lib/images/backups/backups-light.png';
|
||||
import type { BackupPolicy, BackupPolicyList } from '$lib/sdk/backups';
|
||||
@@ -287,14 +285,7 @@
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title="Delete policy"
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
size="small"
|
||||
bind:show={showDelete}
|
||||
headerDivider={false}
|
||||
onSubmit={deletePolicy}>
|
||||
<Modal title="Delete policy" bind:show={showDelete} onSubmit={deletePolicy}>
|
||||
<FormList>
|
||||
<div class="u-flex-vertical u-gap-16">
|
||||
<p class="text" data-private>
|
||||
@@ -310,7 +301,6 @@
|
||||
<div class="input-check-box-friction">
|
||||
<InputCheckbox
|
||||
required
|
||||
size="small"
|
||||
id="delete_policy"
|
||||
bind:checked={confirmedDeletion}
|
||||
label="I understand and confirm" />
|
||||
|
||||
+14
-17
@@ -2,7 +2,6 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { InnerModal } from '$lib/components';
|
||||
import { FormItem } from '$lib/elements/forms';
|
||||
import TextCounter from '$lib/elements/forms/textCounter.svelte';
|
||||
|
||||
export let id: string;
|
||||
@@ -61,22 +60,20 @@
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<div class="form u-gap-8">
|
||||
<FormItem>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
id="id"
|
||||
placeholder="Enter ID"
|
||||
maxlength={36}
|
||||
{pattern}
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value={id}
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
<TextCounter count={id?.length ?? 0} max={36} />
|
||||
</div>
|
||||
</FormItem>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
id="id"
|
||||
placeholder="Enter ID"
|
||||
maxlength={36}
|
||||
{pattern}
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
class="input-text"
|
||||
bind:value={id}
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
<TextCounter count={id?.length ?? 0} max={36} />
|
||||
</div>
|
||||
<div
|
||||
class="u-flex u-gap-4 u-margin-block-start-8 u-small"
|
||||
class:u-color-text-warning={icon === 'exclamation'}>
|
||||
|
||||
+3
-8
@@ -11,7 +11,6 @@
|
||||
TableRow,
|
||||
TableScroll
|
||||
} from '$lib/elements/table';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import RestoreModal from './restoreModal.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { timeFromNow, toLocaleDateTime } from '$lib/helpers/date';
|
||||
@@ -275,10 +274,7 @@
|
||||
|
||||
<Modal
|
||||
title="Delete {selectedBackups.length ? 'backups' : 'backup'}"
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
bind:show={showDelete}
|
||||
headerDivider={false}
|
||||
onSubmit={deleteBackups}>
|
||||
<p class="text" data-private>
|
||||
Are you sure you want to delete
|
||||
@@ -296,7 +292,7 @@
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
<Modal headerDivider={true} title="Restore backup" bind:show={showRestore} onSubmit={restoreBackup}>
|
||||
<Modal title="Restore backup" bind:show={showRestore} onSubmit={restoreBackup}>
|
||||
<Card
|
||||
isTile
|
||||
class="restore-modal-inner-card u-width-full-line"
|
||||
@@ -324,7 +320,7 @@
|
||||
{#each restoreOptions as restoreOption}
|
||||
<div class="u-width-full-line">
|
||||
<LabelCard
|
||||
padding="small"
|
||||
padding="s"
|
||||
name="restore"
|
||||
value={restoreOption.id}
|
||||
bind:group={selectedRestoreOption}>
|
||||
@@ -352,7 +348,6 @@
|
||||
bind:value={newDatabaseInfo.name}
|
||||
autofocus
|
||||
required />
|
||||
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}
|
||||
@@ -375,7 +370,7 @@
|
||||
<div class="input-check-box-friction">
|
||||
<InputCheckbox
|
||||
required
|
||||
size="small"
|
||||
size="s"
|
||||
id="delete_policy"
|
||||
bind:checked={confirmSameDbRestore}>
|
||||
<svelte:fragment slot="description">
|
||||
|
||||
+1
-7
@@ -46,13 +46,7 @@
|
||||
isRelationship(selectedAttribute) && selectedAttribute?.twoWay && !checked;
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
title="Delete attribute"
|
||||
icon="exclamation"
|
||||
state="warning"
|
||||
headerDivider={false}
|
||||
bind:show={showDelete}
|
||||
onSubmit={handleDelete}>
|
||||
<Modal title="Delete attribute" bind:show={showDelete} onSubmit={handleDelete}>
|
||||
<p class="text" data-private>
|
||||
Are you sure you want to delete <b data-private>{selectedAttribute?.key}</b> from
|
||||
<b data-private>{$collection?.name}</b>?
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user