mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' of github.com:appwrite/console into feat-credit-flow
This commit is contained in:
commit
2a6a5e97ee
20 files changed
+970
-719
No files matched your search
Generated
+660
-651
File diff suppressed because it is too large.
Load diff
@@ -20,7 +20,7 @@
|
||||
|
||||
<ul
|
||||
class="grid-box common-section u-margin-block-start-32"
|
||||
style={`--grid-gap:1.5rem; --grid-item-size:${total > 3 ? '22rem' : '25rem'};`}
|
||||
style={`--grid-gap:1.5rem; --grid-item-size-small-screens: 18rem; --grid-item-size:${total > 3 ? '22rem' : '25rem'};`}
|
||||
data-private>
|
||||
<slot />
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
export let fullWidth = false;
|
||||
export let wrapperFullWidth = false;
|
||||
export let position: 'relative' | 'static' = 'relative';
|
||||
export let noMaxWidthList = false;
|
||||
</script>
|
||||
|
||||
<Drop
|
||||
@@ -35,6 +36,7 @@
|
||||
}`}>
|
||||
{#if $$slots.list}
|
||||
<section
|
||||
class:u-max-width-none={noMaxWidthList}
|
||||
class:u-overflow-y-auto={scrollable}
|
||||
class:u-max-height-200={scrollable}
|
||||
class="drop-section">
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Button, InputNumber } from '$lib/elements/forms';
|
||||
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
|
||||
import InputText from '$lib/elements/forms/inputText.svelte';
|
||||
import {
|
||||
Button,
|
||||
InputNumber,
|
||||
InputSelect,
|
||||
InputText,
|
||||
InputTags,
|
||||
FormList,
|
||||
InputSelectCheckbox
|
||||
} from '$lib/elements/forms';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { tags, type Operator, queries } from './store';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { tags, type Operator, queries, type TagValue } from './store';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
export let columnId: string | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
clear: void;
|
||||
apply: { applied: number };
|
||||
}>();
|
||||
|
||||
let columnId: string | null = null;
|
||||
$: column = $columns.find((c) => c.id === columnId) as Column;
|
||||
let arrayValues: string[] = [];
|
||||
|
||||
dispatch('apply', { applied: $tags.length });
|
||||
|
||||
@@ -36,7 +44,7 @@
|
||||
toTag: (attribute, input) => `**${attribute}** greater than **${input}**`,
|
||||
types: ['integer', 'double', 'datetime']
|
||||
},
|
||||
'greater than or equal to': {
|
||||
'greater than or equal': {
|
||||
toQuery: (attr, input) => Query.greaterThanEqual(attr, Number(input)),
|
||||
toTag: (attribute, input) => `**${attribute}** greater than or equal to **${input}**`,
|
||||
types: ['integer', 'double', 'datetime']
|
||||
@@ -46,7 +54,7 @@
|
||||
toTag: (attribute, input) => `**${attribute}** less than **${input}**`,
|
||||
types: ['integer', 'double', 'datetime']
|
||||
},
|
||||
'less than or equal to': {
|
||||
'less than or equal': {
|
||||
toQuery: Query.lessThanEqual,
|
||||
toTag: (attribute, input) => `**${attribute}** less than or equal to **${input}**`,
|
||||
types: ['integer', 'double', 'datetime']
|
||||
@@ -56,22 +64,36 @@
|
||||
toTag: (attribute, input) => `**${attribute}** equal to **${input}**`,
|
||||
types: ['string', 'integer', 'double', 'boolean']
|
||||
},
|
||||
'not equal to': {
|
||||
'not equal': {
|
||||
toQuery: Query.notEqual,
|
||||
toTag: (attribute, input) => `**${attribute}** not equal to **${input}**`,
|
||||
types: ['string', 'integer', 'double', 'boolean']
|
||||
},
|
||||
'not null': {
|
||||
'is not null': {
|
||||
toQuery: Query.isNotNull,
|
||||
toTag: (attribute) => `**${attribute}** is not null`,
|
||||
types: ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'],
|
||||
hideInput: true
|
||||
},
|
||||
null: {
|
||||
'is null': {
|
||||
toQuery: Query.isNull,
|
||||
toTag: (attribute) => `**${attribute}** is null`,
|
||||
types: ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'],
|
||||
hideInput: true
|
||||
},
|
||||
contains: {
|
||||
toQuery: Query.contains,
|
||||
toTag: (attribute, input) => {
|
||||
if (Array.isArray(input) && input.length > 2) {
|
||||
return {
|
||||
value: input,
|
||||
tag: `**${attribute}** contains **${formatArray(input)}** `
|
||||
};
|
||||
} else {
|
||||
return `**${attribute}** contains **${input}**`;
|
||||
}
|
||||
},
|
||||
types: ['string', 'integer', 'double', 'boolean', 'datetime', 'enum']
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,23 +115,48 @@
|
||||
/* eslint @typescript-eslint/no-explicit-any: 'off' */
|
||||
let value: any = null;
|
||||
|
||||
$: {
|
||||
columnId;
|
||||
value = null;
|
||||
}
|
||||
onMount(() => {
|
||||
value = column?.array ? [] : null;
|
||||
});
|
||||
|
||||
// This Map is keyed by tags, and has a query as the value
|
||||
function addFilter() {
|
||||
if (!column || !operator) return;
|
||||
|
||||
queries.addFilter({ column, operator, value: value ?? '' });
|
||||
value = null;
|
||||
if (column.array) {
|
||||
queries.addFilter({ column, operator, value: arrayValues });
|
||||
columnId = null;
|
||||
arrayValues = [];
|
||||
} else {
|
||||
queries.addFilter({ column, operator, value: value ?? '' });
|
||||
columnId = null;
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function tagFormat(node: HTMLElement) {
|
||||
node.innerHTML = node.innerHTML.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
|
||||
}
|
||||
|
||||
function formatArray(array: string[]) {
|
||||
if (!array?.length) return;
|
||||
if (array.length > 2) {
|
||||
return `${array[0]} or ${array.length - 1} others`;
|
||||
} else {
|
||||
return array.join(' or ');
|
||||
}
|
||||
}
|
||||
|
||||
function isTypeTagValue(obj: any): obj is TagValue {
|
||||
if (typeof obj === 'string') return false;
|
||||
return (
|
||||
obj &&
|
||||
typeof obj.tag === 'string' &&
|
||||
(typeof obj.value === 'string' ||
|
||||
typeof obj.value === 'number' ||
|
||||
Array.isArray(obj.value))
|
||||
);
|
||||
}
|
||||
|
||||
$: isDisabled = !operator;
|
||||
</script>
|
||||
|
||||
@@ -134,23 +181,55 @@
|
||||
bind:value={operatorKey} />
|
||||
</ul>
|
||||
{#if column && operator && !operator?.hideInput}
|
||||
<ul class="u-margin-block-start-8">
|
||||
{#if column.type === 'integer' || column.type === 'double'}
|
||||
<InputNumber id="value" bind:value placeholder="Enter value" />
|
||||
{:else if column.type === 'boolean'}
|
||||
<InputSelect
|
||||
id="value"
|
||||
placeholder="Select a value"
|
||||
required={true}
|
||||
options={[
|
||||
{ label: 'True', value: true },
|
||||
{ label: 'False', value: false }
|
||||
].filter(Boolean)}
|
||||
bind:value />
|
||||
{:else}
|
||||
<InputText id="value" bind:value placeholder="Enter value" />
|
||||
{/if}
|
||||
</ul>
|
||||
{#if column?.array}
|
||||
<FormList class="u-margin-block-start-8">
|
||||
{#if column.format === 'enum'}
|
||||
<InputSelectCheckbox
|
||||
name="value"
|
||||
bind:tags={arrayValues}
|
||||
placeholder="Select value"
|
||||
options={column?.elements?.map((value) => ({
|
||||
label: value,
|
||||
value,
|
||||
checked: arrayValues.includes(value)
|
||||
}))}>
|
||||
</InputSelectCheckbox>
|
||||
{:else}
|
||||
<InputTags
|
||||
label="values"
|
||||
showLabel={false}
|
||||
id="value"
|
||||
bind:tags={arrayValues}
|
||||
placeholder="Enter values" />
|
||||
{/if}
|
||||
</FormList>
|
||||
{:else}
|
||||
<ul class="u-margin-block-start-8">
|
||||
{#if column.format === 'enum'}
|
||||
<InputSelect
|
||||
id="value"
|
||||
bind:value
|
||||
placeholder="Select value"
|
||||
options={column?.elements?.map((value) => ({ label: value, value }))}
|
||||
label="Value"
|
||||
showLabel={false} />
|
||||
{:else if column.type === 'integer' || column.type === 'double'}
|
||||
<InputNumber id="value" bind:value placeholder="Enter value" />
|
||||
{:else if column.type === 'boolean'}
|
||||
<InputSelect
|
||||
id="value"
|
||||
placeholder="Select a value"
|
||||
required={true}
|
||||
options={[
|
||||
{ label: 'True', value: true },
|
||||
{ label: 'False', value: false }
|
||||
].filter(Boolean)}
|
||||
bind:value />
|
||||
{:else}
|
||||
<InputText id="value" bind:value placeholder="Enter value" />
|
||||
{/if}
|
||||
</ul>
|
||||
{/if}
|
||||
{/if}
|
||||
<Button text disabled={isDisabled} class="u-margin-block-start-4" submit>
|
||||
<i class="icon-plus" />
|
||||
@@ -160,16 +239,32 @@
|
||||
|
||||
<ul class="u-flex u-flex-wrap u-cross-center u-gap-8 u-margin-block-start-16 tags">
|
||||
{#each $tags as tag (tag)}
|
||||
<button
|
||||
class="tag"
|
||||
on:click={() => {
|
||||
queries.removeFilter(tag);
|
||||
}}>
|
||||
<span class="text" use:tagFormat>
|
||||
{tag}
|
||||
</span>
|
||||
<i class="icon-x" />
|
||||
</button>
|
||||
{#if isTypeTagValue(tag)}
|
||||
<button
|
||||
use:tooltip={{
|
||||
content: tag?.value?.toString()
|
||||
}}
|
||||
class="tag"
|
||||
on:click={() => {
|
||||
queries.removeFilter(tag);
|
||||
}}>
|
||||
<span class="text" use:tagFormat>
|
||||
{tag.tag}
|
||||
</span>
|
||||
<i class="icon-x" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="tag"
|
||||
on:click={() => {
|
||||
queries.removeFilter(tag);
|
||||
}}>
|
||||
<span class="text" use:tagFormat>
|
||||
{tag}
|
||||
</span>
|
||||
<i class="icon-x" />
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
|
||||
export let query = '[]';
|
||||
export let columns: Writable<Column[]>;
|
||||
export let disabled = false;
|
||||
export let fullWidthMobile = false;
|
||||
|
||||
const parsedQueries = queryParamToMap(query);
|
||||
queries.set(parsedQueries);
|
||||
|
||||
let selectedColumn: string | null = null;
|
||||
// We need to separate them so we don't trigger Drop's handlers
|
||||
let showFiltersDesktop = false;
|
||||
let showFiltersMobile = false;
|
||||
@@ -25,11 +27,20 @@
|
||||
showFiltersDesktop = false;
|
||||
showFiltersMobile = false;
|
||||
});
|
||||
|
||||
function clearAll() {
|
||||
selectedColumn = null;
|
||||
queries.clearAll();
|
||||
}
|
||||
|
||||
$: if (!showFiltersDesktop && !showFiltersMobile) {
|
||||
selectedColumn = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="is-not-mobile">
|
||||
<Drop bind:show={showFiltersDesktop} noArrow>
|
||||
<Button secondary on:click={() => (showFiltersDesktop = !showFiltersDesktop)}>
|
||||
<Button secondary on:click={() => (showFiltersDesktop = !showFiltersDesktop)} {disabled}>
|
||||
<i class="icon-filter u-opacity-50" />
|
||||
Filters
|
||||
{#if applied > 0}
|
||||
@@ -42,12 +53,13 @@
|
||||
<div class="dropped card">
|
||||
<p>Apply filter rules to refine the table view</p>
|
||||
<Content
|
||||
bind:columnId={selectedColumn}
|
||||
{columns}
|
||||
on:apply={(e) => (applied = e.detail.applied)}
|
||||
on:clear={() => (applied = 0)} />
|
||||
<hr />
|
||||
<div class="u-flex u-margin-block-start-16 u-main-end u-gap-8">
|
||||
<Button text on:click={queries.clearAll}>Clear all</Button>
|
||||
<Button text on:click={clearAll}>Clear all</Button>
|
||||
<Button on:click={queries.apply} disabled={!$queriesAreDirty}>Apply</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,7 +88,7 @@
|
||||
on:apply={(e) => (applied = e.detail.applied)}
|
||||
on:clear={() => (applied = 0)} />
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={queries.clearAll}>Clear all</Button>
|
||||
<Button text on:click={clearAll}>Clear all</Button>
|
||||
<Button on:click={queries.apply} disabled={!$queriesAreDirty}>Apply</Button
|
||||
></svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
@@ -5,14 +5,19 @@ import { page } from '$app/stores';
|
||||
import deepEqual from 'deep-equal';
|
||||
import type { Column, ColumnType } from '$lib/helpers/types';
|
||||
|
||||
export type TagValue = {
|
||||
tag: string;
|
||||
value: string | number | string[];
|
||||
};
|
||||
|
||||
export type Operator = {
|
||||
toTag: (attribute: string, input?: string | number) => string;
|
||||
toQuery: (attribute: string, input?: string | number) => string;
|
||||
toTag: (attribute: string, input?: string | number | string[]) => string | TagValue;
|
||||
toQuery: (attribute: string, input?: string | number | string[]) => string;
|
||||
types: ColumnType[];
|
||||
hideInput?: boolean;
|
||||
};
|
||||
|
||||
export function mapToQueryParams(map: Map<string, string>) {
|
||||
export function mapToQueryParams(map: Map<string | TagValue, string>) {
|
||||
return encodeURIComponent(JSON.stringify(Array.from(map.entries())));
|
||||
}
|
||||
|
||||
@@ -22,13 +27,13 @@ export function queryParamToMap(queryParam: string) {
|
||||
return new Map(queries);
|
||||
}
|
||||
|
||||
function initQueries(initialValue = new Map<string, string>()) {
|
||||
function initQueries(initialValue = new Map<string | TagValue, string>()) {
|
||||
const queries = writable(initialValue);
|
||||
|
||||
type AddFilterArgs = {
|
||||
operator: Operator;
|
||||
column: Column;
|
||||
value: string | number;
|
||||
value: string | number | string[];
|
||||
};
|
||||
|
||||
function addFilter({ column, operator, value }: AddFilterArgs) {
|
||||
@@ -38,7 +43,7 @@ function initQueries(initialValue = new Map<string, string>()) {
|
||||
});
|
||||
}
|
||||
|
||||
function removeFilter(tag: string) {
|
||||
function removeFilter(tag: string | TagValue) {
|
||||
queries.update((map) => {
|
||||
map.delete(tag);
|
||||
return map;
|
||||
|
||||
@@ -31,4 +31,5 @@ export { default as InputDate } from './inputDate.svelte';
|
||||
export { default as InputDateRange } from './inputDateRange.svelte';
|
||||
export { default as InputTime } from './inputTime.svelte';
|
||||
export { default as InputDigits } from './inputDigits.svelte';
|
||||
export { default as InputSelectCheckbox } from './inputSelectCheckbox.svelte';
|
||||
export { default as InputFilePicker } from './inputFilePicker.svelte';
|
||||
@@ -0,0 +1,88 @@
|
||||
<script lang="ts">
|
||||
import { DropList } from '$lib/components';
|
||||
import { SelectSearchCheckbox } from '..';
|
||||
|
||||
type Option = {
|
||||
value: string;
|
||||
label: string;
|
||||
checked: boolean;
|
||||
};
|
||||
|
||||
export let name: string;
|
||||
export let tags: string[] = [];
|
||||
export let placeholder: string;
|
||||
export let options: Option[] = [];
|
||||
|
||||
let search = '';
|
||||
let input: HTMLInputElement;
|
||||
let show = false;
|
||||
|
||||
function addValue(option: Option) {
|
||||
let tag = option.value.trim();
|
||||
if (tag.length === 0 || tags.includes(tag)) return;
|
||||
|
||||
tags = [...tags, tag];
|
||||
option.checked = true;
|
||||
clearSearch();
|
||||
}
|
||||
|
||||
function removeValue(option: Option) {
|
||||
tags = tags.filter((tag) => tag !== option.value);
|
||||
option.checked = false;
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
search = '';
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
$: filteredOptions = search
|
||||
? options.filter((option) => option.label.toLowerCase().includes(search.toLowerCase()))
|
||||
: options;
|
||||
|
||||
//TODO: Debounce search and open the droplist when the user starts typing
|
||||
</script>
|
||||
|
||||
<DropList bind:show noStyle noArrow scrollable placement="bottom-end" fixed noMaxWidthList>
|
||||
<button
|
||||
class="tags-input u-position-relative u-cursor-pointer"
|
||||
type="button"
|
||||
on:click={() => (show = !show)}>
|
||||
<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>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<input
|
||||
class="tags-input-text u-cursor-text"
|
||||
{placeholder}
|
||||
bind:value={search}
|
||||
bind:this={input} />
|
||||
<span
|
||||
class:icon-cheveron-up={show}
|
||||
class:icon-cheveron-down={!show}
|
||||
class="u-position-absolute u-inset-block-start-4 u-inset-inline-end-12"
|
||||
aria-hidden="true"></span>
|
||||
</button>
|
||||
|
||||
<svelte:fragment slot="list">
|
||||
{#each filteredOptions as option (option.value + option.checked)}
|
||||
<SelectSearchCheckbox
|
||||
on:click={() => (option?.checked ? removeValue(option) : addValue(option))}
|
||||
bind:value={option.checked}>
|
||||
{option.label}
|
||||
</SelectSearchCheckbox>
|
||||
{:else}
|
||||
<li class="drop-list-item">
|
||||
<span class="text">There are no {name} that match your search</span>
|
||||
</li>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as Pill } from './pill.svelte';
|
||||
export { default as SelectSearchItem } from './selectSearchItem.svelte';
|
||||
export { default as Flag } from './flag.svelte';
|
||||
export { default as SelectSearchCheckbox } from './selectSearchCheckbox.svelte';
|
||||
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
// export let data: string[] = [];
|
||||
export let value = false;
|
||||
</script>
|
||||
|
||||
<li class="drop-list-item">
|
||||
<button
|
||||
class="drop-button u-flex u-cross-center u-gap-12"
|
||||
on:click|preventDefault
|
||||
type="button">
|
||||
<input type="checkbox" class="is-small" bind:checked={value} />
|
||||
<slot />
|
||||
</button>
|
||||
</li>
|
||||
@@ -13,7 +13,15 @@ export function isHTMLInputElement(el: unknown): el is HTMLInputElement {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type Prettify<T> = T & {};
|
||||
|
||||
const columnTypes = ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'] as const;
|
||||
const columnTypes = [
|
||||
'string',
|
||||
'integer',
|
||||
'double',
|
||||
'boolean',
|
||||
'datetime',
|
||||
'relationship',
|
||||
'enum'
|
||||
] as const;
|
||||
export type ColumnType = (typeof columnTypes)[number];
|
||||
export type Column = {
|
||||
id: string;
|
||||
@@ -28,6 +36,9 @@ export type Column = {
|
||||
* Set to false to disable filtering for this column
|
||||
*/
|
||||
filter?: boolean;
|
||||
array?: boolean;
|
||||
format?: string;
|
||||
elements?: string[];
|
||||
};
|
||||
|
||||
export function isValueOfStringEnum<T extends Record<string, string>>(
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<footer class="main-footer u-cross-center">
|
||||
<div class="main-footer-start">
|
||||
<ul class="inline-links is-no-padding-first-and-last u-x-small">
|
||||
<ul class="inline-links is-no-padding-first-and-last u-x-small u-flex-wrap u-row-gap-16">
|
||||
<li class="inline-links-item" style="line-height: 1.02;">
|
||||
<div class="u-flex u-cross-center u-gap-8">
|
||||
{#if isCloud}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
} catch (e) {
|
||||
error = true;
|
||||
trackError(e.message, Submit.DomainCreate);
|
||||
throw new Error(e.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
import { project } from './project-[project]/store';
|
||||
import { feedback } from '$lib/stores/feedback';
|
||||
import { VARS, hasStripePublicKey, isCloud } from '$lib/system';
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
import { stripe } from '$lib/stores/stripe';
|
||||
import MobileSupportModal from './wizard/support/mobileSupportModal.svelte';
|
||||
import { showSupportModal } from './wizard/support/store';
|
||||
@@ -236,6 +235,8 @@
|
||||
}, INTERVAL);
|
||||
|
||||
if (isCloud && hasStripePublicKey) {
|
||||
const loadStripe = (await import('@stripe/stripe-js')).loadStripe;
|
||||
|
||||
$stripe = await loadStripe(VARS.STRIPE_PUBLIC_KEY);
|
||||
await checkForMissingPaymentMethod();
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<PlanSummary />
|
||||
<PaymentHistory />
|
||||
<PaymentMethods />
|
||||
<BillingAddress billingAddress={data.billingAddress} />
|
||||
<BillingAddress billingAddress={data?.billingAddress} />
|
||||
<TaxId />
|
||||
<BudgetCap />
|
||||
{#if $organization?.billingPlan !== BillingPlan.STARTER && !!$organization?.billingBudget}
|
||||
|
||||
@@ -15,10 +15,14 @@ export const load: PageLoad = async ({ parent, depends }) => {
|
||||
let billingAddress: Address = null;
|
||||
const billingAddressId = (organization as Organization)?.billingAddressId;
|
||||
if (billingAddressId) {
|
||||
billingAddress = await sdk.forConsole.billing.getOrganizationBillingAddress(
|
||||
organization.$id,
|
||||
billingAddressId
|
||||
);
|
||||
try {
|
||||
billingAddress = await sdk.forConsole.billing.getOrganizationBillingAddress(
|
||||
organization.$id,
|
||||
billingAddressId
|
||||
);
|
||||
} catch {
|
||||
billingAddress = null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
+5
-2
@@ -28,7 +28,10 @@
|
||||
id: attribute.key,
|
||||
title: attribute.key,
|
||||
type: attribute.type as ColumnType,
|
||||
show: selected?.includes(attribute.key) ?? true
|
||||
show: selected?.includes(attribute.key) ?? true,
|
||||
array: attribute?.array,
|
||||
format: 'format' in attribute && attribute?.format === 'enum' ? attribute.format : null,
|
||||
elements: 'elements' in attribute ? attribute.elements : null
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -54,7 +57,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Filters query={data.query} {columns} />
|
||||
<Filters query={data.query} {columns} disabled={!(hasAttributes && hasValidAttributes)} />
|
||||
|
||||
<div class="u-flex u-main-end u-gap-16">
|
||||
<ViewSelector
|
||||
|
||||
+5
-1
@@ -91,7 +91,11 @@
|
||||
id: attribute.key,
|
||||
title: attribute.key,
|
||||
type: attribute.type as ColumnType,
|
||||
show: selected?.includes(attribute.key) ?? true
|
||||
show: selected?.includes(attribute.key) ?? true,
|
||||
array: attribute?.array,
|
||||
format:
|
||||
'format' in attribute && attribute?.format === 'enum' ? attribute.format : null,
|
||||
elements: 'elements' in attribute ? attribute.elements : null
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
`functions.${$page.params.function}.deployments.${$page.params.deployment}.update`
|
||||
)
|
||||
) {
|
||||
logs = message.payload.buildLogs;
|
||||
logs = message.payload['logs'];
|
||||
if (message.payload.status === 'ready') {
|
||||
invalidate(Dependencies.DEPLOYMENT);
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@
|
||||
label="Allowed extensions"
|
||||
placeholder="Allowed file extensions (mp4, jpg, pdf, etc.)"
|
||||
bind:tags={extensions} />
|
||||
<li class="u-flex u-gap-12 u-margin-block-start-8">
|
||||
<li class="u-flex u-gap-12 u-margin-block-start-8 with-scroll">
|
||||
{#each suggestedExtensions as ext}
|
||||
<Pill
|
||||
selected={extensions.includes(ext)}
|
||||
|
||||
Reference in new issue
Block a user