diff --git a/Dockerfile b/Dockerfile index 5ccdd958f..788db3b62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,7 +39,7 @@ ENV NODE_OPTIONS=--max_old_space_size=8192 RUN bun run build -FROM nginx:1.26.3-alpine +FROM nginx:1.29.5-alpine EXPOSE 80 diff --git a/bun.lock b/bun.lock index 8a28be835..3eb57c887 100644 --- a/bun.lock +++ b/bun.lock @@ -6,11 +6,11 @@ "name": "@appwrite/console", "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa", "@appwrite.io/pink-icons": "0.25.0", - "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89", + "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc", "@appwrite.io/pink-legacy": "^1.0.3", - "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c", + "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc", "@faker-js/faker": "^9.9.0", "@plausible-analytics/tracker": "^0.4.4", "@popperjs/core": "^2.11.8", @@ -107,15 +107,15 @@ "@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="], - "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4", { "dependencies": { "bignumber.js": "9.0.0", "json-bigint": "1.0.0" } }], + "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa", { "dependencies": { "bignumber.js": "9.0.0", "json-bigint": "1.0.0" } }], "@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="], - "@appwrite.io/pink-icons-svelte": ["@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89", { "peerDependencies": { "svelte": "^4.0.0" } }], + "@appwrite.io/pink-icons-svelte": ["@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc", { "peerDependencies": { "svelte": "^4.0.0" } }], "@appwrite.io/pink-legacy": ["@appwrite.io/pink-legacy@1.0.3", "", { "dependencies": { "@appwrite.io/pink-icons": "1.0.0", "the-new-css-reset": "^1.11.2" } }, "sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ=="], - "@appwrite.io/pink-svelte": ["@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c", { "dependencies": { "@appwrite.io/pink-icons-svelte": "2.0.0-RC.1", "@floating-ui/dom": "^1.6.13", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.86.6", "@tanstack/svelte-virtual": "^3.13.10", "ansicolor": "^2.0.3", "d3": "^7.9.0", "fuse.js": "^7.1.0", "pretty-bytes": "^6.1.1", "shiki": "^1.18.0", "svelte-motion": "^0.12.2", "svelte-sonner": "^0.3.28" }, "peerDependencies": { "svelte": "^4.0.0" } }], + "@appwrite.io/pink-svelte": ["@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc", { "dependencies": { "@appwrite.io/pink-icons-svelte": "2.0.0-RC.1", "@floating-ui/dom": "^1.6.13", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.86.6", "@tanstack/svelte-virtual": "^3.13.10", "ansicolor": "^2.0.3", "d3": "^7.9.0", "fuse.js": "^7.1.0", "pretty-bytes": "^6.1.1", "shiki": "^1.18.0", "svelte-motion": "^0.12.2", "svelte-sonner": "^0.3.28" }, "peerDependencies": { "svelte": "^4.0.0" } }], "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], diff --git a/package.json b/package.json index 604624d1d..913f11548 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,11 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa", "@appwrite.io/pink-icons": "0.25.0", - "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89", + "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc", "@appwrite.io/pink-legacy": "^1.0.3", - "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c", + "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc", "@faker-js/faker": "^9.9.0", "@plausible-analytics/tracker": "^0.4.4", "@popperjs/core": "^2.11.8", diff --git a/src/lib/components/billing/updateStateModal.svelte b/src/lib/components/billing/updateStateModal.svelte index db6160a7a..868f9be4c 100644 --- a/src/lib/components/billing/updateStateModal.svelte +++ b/src/lib/components/billing/updateStateModal.svelte @@ -62,12 +62,7 @@ } - + State information is required for US payment methods to apply correct taxes and meet diff --git a/src/lib/components/domains/cnameTable.svelte b/src/lib/components/domains/cnameTable.svelte index 9569ce89f..eb1dcc693 100644 --- a/src/lib/components/domains/cnameTable.svelte +++ b/src/lib/components/domains/cnameTable.svelte @@ -36,6 +36,7 @@ - import { Icon, Layout, Tag, Tooltip } from '@appwrite.io/pink-svelte'; - import { queries, tagFormat, tags } from './store'; + import { + Icon, + Layout, + Tooltip, + CompoundTagRoot, + CompoundTagChild, + Typography, + ActionMenu, + Selector + } from '@appwrite.io/pink-svelte'; + import { capitalize } from '$lib/helpers/string'; + import { queries, tags } from './store'; import { IconX } from '@appwrite.io/pink-icons-svelte'; - import { parsedTags } from './setFilters'; + import { parsedTags, type ParsedTag } from './setFilters'; import { Button } from '$lib/elements/forms'; + import type { Column } from '$lib/helpers/types'; + import { writable, type Writable } from 'svelte/store'; + import Menu from '$lib/components/menu/menu.svelte'; + import { addFilterAndApply, buildFilterCol, type FilterData } from './quickFilters'; + import QuickFilters from '$lib/components/filters/quickFilters.svelte'; + import { isSmallViewport } from '$lib/stores/viewport'; + + let { + columns = writable([]), + analyticsSource = '' + }: { columns?: Writable; analyticsSource?: string } = $props(); + + function parseTagParts(tagString: string): { text: string; operator: boolean }[] { + return tagString + .split(/\*\*(.*?)\*\*/) + .map((part, index) => { + // Even indices are outside bold (operators), odd indices are inside bold (values) + if (index % 2 === 0) { + return part + .split(/\s+/) + .filter(Boolean) + .map((t) => ({ text: t, operator: true })); + } else { + return [{ text: part, operator: false }]; + } + }) + .flat() + .filter((p) => Boolean(p.text)); + } + + function getFilterFor(title: string): FilterData | null { + if (!columns) return null; + const col = ($columns as unknown as Column[]).find((c) => c.title === title); + if (!col) return null; + const filter = buildFilterCol(col); + return filter ?? null; + } + + // Build available filter definitions from provided columns + let availableFilters = $derived( + ($columns as unknown as Column[] | undefined)?.length + ? (($columns as unknown as Column[]) + .map((c) => (c.filter !== false ? buildFilterCol(c) : null)) + .filter((f) => f && f.options) as FilterData[]) + : [] + ); + + // QuickFilters uses the same filters list + let filterCols = $derived(availableFilters); + + // Always-show placeholders are derived from available filters (no hardcoding) + // Use reactive array so runes can track changes + let hiddenPlaceholders: string[] = $state([]); + + let activeTitles = $derived( + ($parsedTags || []).map((t) => (t as ParsedTag).title).filter(Boolean) as string[] + ); + + // Compute current placeholders (major filters not already active or dismissed) + let placeholders = $derived( + availableFilters + .filter((f) => !activeTitles.includes(f.title)) + .filter((f) => !hiddenPlaceholders.includes(f.title)) + ); -{#if $parsedTags?.length} - + + {#if $parsedTags?.length} {#each $parsedTags as tag (tag.tag)} - { - const t = $tags.filter((t) => t.tag.includes(tag.tag.split(' ')[0])); - t.forEach((t) => (t ? queries.removeFilter(t) : null)); - queries.apply(); - parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag)); - }}> - {#key tag.tag} - {tag.tag} - {/key} - - + + {@const parts = parseTagParts(tag.tag)} + {@const property = (tag as ParsedTag).title} + + {#each parts as part} + + + + {#if part.operator} + {part.text} + {:else} + {part.text + .split(' or ') + .map((t) => capitalize(t)) + .join(' or ')} + {/if} + + + {#if property} + {@const filter = getFilterFor(property)} + {#if filter} + {@const isArray = filter?.array} + {@const selectedArray = Array.isArray(tag.value) + ? tag.value + : []} + {#each filter.options as option (filter.title + option.value + option.label)} + + { + if (isArray) { + const exists = + selectedArray.includes( + option.value + ); + const next = exists + ? selectedArray.filter( + (v) => + v !== option.value + ) + : [ + ...selectedArray, + option.value + ]; + addFilterAndApply( + filter.id, + filter.title, + filter.operator, + null, + next, + $columns, + analyticsSource + ); + } else { + addFilterAndApply( + filter.id, + filter.title, + filter.operator, + option.value, + [], + $columns, + analyticsSource + ); + } + }}> + + {#if isArray} + + {/if} + {capitalize(option.label)} + + + + {/each} + {/if} + {/if} + + + + {/each} + { + const t = $tags.filter((t) => + t.tag.includes((tag as ParsedTag).title) + ); + t.forEach((t) => (t ? queries.removeFilter(t) : null)); + queries.apply(); + parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag)); + }}> + + + {tag?.value?.toString()} {/each} + {/if} + + + {#if placeholders?.length} + {#each placeholders as filter (filter.title + filter.id)} + + + + + {capitalize(filter.title)} + + { + e.stopPropagation(); + if (!hiddenPlaceholders.includes(filter.title)) { + hiddenPlaceholders = [...hiddenPlaceholders, filter.title]; + } + }}> + + + + + {#if filter.options} + {#each filter.options as option (filter.title + option.value + option.label)} + + { + addFilterAndApply( + filter.id, + filter.title, + filter.operator, + filter?.array ? null : option.value, + filter?.array ? [option.value] : [], + $columns, + analyticsSource + ); + }}> + {capitalize(option.label)} + + + {/each} + {/if} + + + + {/each} + {/if} + + {#if $parsedTags?.length} - -{/if} + {/if} + + {#if filterCols?.length && !$isSmallViewport} + + {/if} + diff --git a/src/lib/components/filters/quickFilters.svelte b/src/lib/components/filters/quickFilters.svelte index 76bbf90c5..af2b94d42 100644 --- a/src/lib/components/filters/quickFilters.svelte +++ b/src/lib/components/filters/quickFilters.svelte @@ -13,19 +13,36 @@ let { columns, filterCols, - analyticsSource + analyticsSource, + buttonVariant = 'ghost' }: { columns: Writable; filterCols: FilterData[]; analyticsSource?: string; + buttonVariant?: 'ghost' | 'secondary'; } = $props(); - + {#if buttonVariant === 'secondary'} + + {:else} + + {/if} {#each filterCols.filter((f) => f?.options) as filter (filter.title + filter.id)} {#if filter.options} diff --git a/src/lib/components/filters/setFilters.ts b/src/lib/components/filters/setFilters.ts index e6f07083c..ff83314a5 100644 --- a/src/lib/components/filters/setFilters.ts +++ b/src/lib/components/filters/setFilters.ts @@ -3,15 +3,21 @@ import { get, writable } from 'svelte/store'; import { type FilterData } from './quickFilters'; import { tags, type TagValue } from './store'; -export const parsedTags = writable([]); +export type ParsedTag = TagValue & { + title: string; +}; + +export const parsedTags = writable([]); export function setFilters(localTags: TagValue[], filterCols: FilterData[], $columns: Column[]) { if (!localTags?.length) { + parsedTags.set([]); filterCols.forEach((filter) => { resetOptions(filter); cleanOldTags(filter?.title); }); } else { + parsedTags.set([]); filterCols.forEach((filter) => { const id = filter?.id?.toLowerCase(); if (id?.includes('duration')) { @@ -47,9 +53,10 @@ export function setFilterData(filter: FilterData) { }); } cleanOldTags(filter?.title); - const newTag = { + const newTag: ParsedTag = { tag: tagData.tag.replace(',', ' or '), - value: tagData.value + value: tagData.value, + title: filter.title }; parsedTags.update((tags) => { @@ -69,9 +76,10 @@ export function setTimeFilter(filter: FilterData, columns: Column[]) { const ranges = col.elements as { value: string; label: string }[]; const timeRange = ranges.find((range) => range.value === timeTag.value); if (timeRange) { - const newTag = { + const newTag: ParsedTag = { tag: `**${filter.title}** is **${timeRange.label}**`, - value: timeRange.value + value: timeRange.value, + title: filter.title }; cleanOldTags(filter?.title); @@ -102,9 +110,10 @@ export function setSizeFilter(filter: FilterData, columns: Column[]) { if (sizeRange) { cleanOldTags(filter?.title); - const newTag = { + const newTag: ParsedTag = { tag: `**${filter.title}** is **${sizeRange.label}**`, - value: sizeTag.value + value: sizeTag.value, + title: filter.title }; parsedTags.update((tags) => { tags.push(newTag); @@ -126,9 +135,10 @@ export function setStatusCodeFilter(filter: FilterData, columns: Column[]) { const codeRange = ranges.find((c) => c?.value && c.value === statusCodeTag.value); if (codeRange) { cleanOldTags(filter?.title); - const newTag = { + const newTag: ParsedTag = { tag: `**${filter.title}** is **${codeRange.label}**`, - value: statusCodeTag.value + value: statusCodeTag.value, + title: filter.title }; parsedTags.update((tags) => { tags.push(newTag); @@ -156,9 +166,10 @@ export function setDateFilter(filter: FilterData, columns: Column[]) { }); if (dateRange) { cleanOldTags(filter?.title); - const newTag = { + const newTag: ParsedTag = { tag: `**${filter.title}** is **${dateRange.label}**`, - value: dateTag.value + value: dateTag.value, + title: filter.title }; parsedTags.update((tags) => { tags.push(newTag); diff --git a/src/lib/components/filters/store.ts b/src/lib/components/filters/store.ts index fde630607..734f5e581 100644 --- a/src/lib/components/filters/store.ts +++ b/src/lib/components/filters/store.ts @@ -156,6 +156,10 @@ export enum ValidOperators { export enum ValidTypes { String = 'string', + Varchar = 'varchar', + Text = 'text', + Mediumtext = 'mediumtext', + Longtext = 'longtext', Integer = 'integer', Double = 'double', Boolean = 'boolean', @@ -175,8 +179,32 @@ const operatorsDefault = new Map< hideInput?: boolean; } >([ - [ValidOperators.StartsWith, { query: Query.startsWith, types: [ValidTypes.String] }], - [ValidOperators.EndsWith, { query: Query.endsWith, types: [ValidTypes.String] }], + [ + ValidOperators.StartsWith, + { + query: Query.startsWith, + types: [ + ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext + ] + } + ], + [ + ValidOperators.EndsWith, + { + query: Query.endsWith, + types: [ + ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext + ] + } + ], [ ValidOperators.GreaterThan, { @@ -211,6 +239,10 @@ const operatorsDefault = new Map< query: Query.equal, types: [ ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext, ValidTypes.Integer, ValidTypes.Double, ValidTypes.Boolean, @@ -227,6 +259,10 @@ const operatorsDefault = new Map< query: Query.notEqual, types: [ ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext, ValidTypes.Integer, ValidTypes.Double, ValidTypes.Boolean, @@ -242,6 +278,10 @@ const operatorsDefault = new Map< query: Query.isNotNull, types: [ ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext, ValidTypes.Integer, ValidTypes.Double, ValidTypes.Boolean, @@ -260,6 +300,10 @@ const operatorsDefault = new Map< query: Query.isNull, types: [ ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext, ValidTypes.Integer, ValidTypes.Double, ValidTypes.Boolean, @@ -278,6 +322,10 @@ const operatorsDefault = new Map< query: Query.contains, types: [ ValidTypes.String, + ValidTypes.Varchar, + ValidTypes.Text, + ValidTypes.Mediumtext, + ValidTypes.Longtext, ValidTypes.Integer, ValidTypes.Double, ValidTypes.Boolean, diff --git a/src/lib/components/git/selectRootModal.svelte b/src/lib/components/git/selectRootModal.svelte index bc817623e..fed59657c 100644 --- a/src/lib/components/git/selectRootModal.svelte +++ b/src/lib/components/git/selectRootModal.svelte @@ -152,7 +152,13 @@ Select the directory where your site code is located using the menu below. - + diff --git a/src/lib/components/progressbar/ProgressBar.svelte b/src/lib/components/progressbar/ProgressBar.svelte index 4591e706e..4b4b25c5a 100644 --- a/src/lib/components/progressbar/ProgressBar.svelte +++ b/src/lib/components/progressbar/ProgressBar.svelte @@ -9,19 +9,31 @@ */ export let maxSize: $$Props['maxSize']; + /** + * Hides empty segments where size is <= 0; + */ + export let hideEmptySegments: $$Props['hideEmptySegments'] = false; + /** * The data for the progressbar */ export let data: $$Props['data']; + $: segments = hideEmptySegments ? data.filter((item) => item.size > 0) : data; + /** * The remaining value of the progressbar */ - $: remainder = data.reduce((sum: number, item: ProgressbarData) => sum - item.size, maxSize); + $: remainder = segments.reduce( + (sum: number, item: ProgressbarData) => sum - item.size, + maxSize + ); + + $: segmentGap = segments.length > 1 ? '2px' : '0px'; -
- {#each $$props.data as item} +
+ {#each segments as item}
; }; diff --git a/src/lib/helpers/faker.ts b/src/lib/helpers/faker.ts index c7ce16e9d..7abd56c62 100644 --- a/src/lib/helpers/faker.ts +++ b/src/lib/helpers/faker.ts @@ -183,7 +183,8 @@ function generateValueForField( function generateSingleValue(field: Field): string | number | boolean | NestedNumberArray | null { switch (field.type) { - case 'string': { + case 'string': + case 'varchar': { if ('format' in field && field.format) { switch (field.format) { case 'email': { @@ -214,6 +215,21 @@ function generateSingleValue(field: Field): string | number | boolean | NestedNu } } + case 'text': { + // TEXT type max size is 16,383 characters (utf8mb4) + return generateStringValue(field.key, 1000); + } + + case 'mediumtext': { + // MEDIUMTEXT type max size is 4,194,303 characters (utf8mb4) + return generateStringValue(field.key, 1000); + } + + case 'longtext': { + // LONGTEXT type max size is 1,073,741,823 characters (utf8mb4) + return generateStringValue(field.key, 1000); + } + case 'integer': { const intAttr = field as Models.ColumnInteger; const minCompat = coerceToNumber(intAttr.min); diff --git a/src/lib/helpers/stores.ts b/src/lib/helpers/stores.ts index 13a63437d..11c355a8a 100644 --- a/src/lib/helpers/stores.ts +++ b/src/lib/helpers/stores.ts @@ -1,5 +1,5 @@ import { writable, type Writable } from 'svelte/store'; -import { deepClone, objectEntries } from './object'; +import { objectEntries } from './object'; /** * Given an object `obj`, returns an object of writable stores for each key in that object. @@ -16,7 +16,7 @@ export function createConservative>(obj: Obj [K in keyof Obj]: Writable; }; - const history = deepClone(obj); + const history = structuredClone(obj); function listen(input: Obj) { objectEntries(input).forEach(([key, value]) => { diff --git a/src/lib/helpers/types.ts b/src/lib/helpers/types.ts index df6fba0d9..3c3218a40 100644 --- a/src/lib/helpers/types.ts +++ b/src/lib/helpers/types.ts @@ -30,6 +30,10 @@ export type Prettify = T & {}; const columnTypes = [ 'string', + 'varchar', + 'text', + 'mediumtext', + 'longtext', 'integer', 'double', 'boolean', diff --git a/src/lib/layout/activity.svelte b/src/lib/layout/activity.svelte index c77fa3488..04fef37d7 100644 --- a/src/lib/layout/activity.svelte +++ b/src/lib/layout/activity.svelte @@ -49,7 +49,7 @@ expanded={databasesScreen && !insideSideSheet} slotSpacing={databasesScreen && !insideSideSheet}> {#if logs.total} - + User Event diff --git a/src/lib/layout/responsiveContainerHeader.svelte b/src/lib/layout/responsiveContainerHeader.svelte index 98e10321b..e957a1854 100644 --- a/src/lib/layout/responsiveContainerHeader.svelte +++ b/src/lib/layout/responsiveContainerHeader.svelte @@ -1,12 +1,8 @@ @@ -106,21 +104,47 @@ {#if showSearch && hasSearch} {/if} + {#if hasFilters && filtersStyle === 'chips'} +
+ +
+ {/if} {:else} - - + + {#if hasSearch} - +
+ +
+ {/if} + {#if hasFilters && filtersStyle === 'chips'} + + + + {/if}
- - {#if hasFilters && $columns?.length} - {#if hasCustomFiltersOnly} - - {:else} - - {/if} + + {#if hasFilters && filtersStyle === 'dropdown'} + f?.options)} /> {/if} {#if hasDisplaySettings} @@ -131,7 +155,6 @@ {/if} -
@@ -171,7 +194,12 @@ {/snippet} {#snippet filtersButton(icon = false)} - {/snippet} diff --git a/src/lib/layout/shell.svelte b/src/lib/layout/shell.svelte index 1ce8a8137..4a2a7bcbf 100644 --- a/src/lib/layout/shell.svelte +++ b/src/lib/layout/shell.svelte @@ -5,8 +5,9 @@ import { activeHeaderAlert } from '$routes/(console)/store'; import { onMount, setContext } from 'svelte'; import { writable } from 'svelte/store'; - import { showSubNavigation } from '$lib/stores/layout'; + import { showSubNavigation, showOnboardingAnimation } from '$lib/stores/layout'; import { organization, organizationList } from '$lib/stores/organization'; + import { sdk } from '$lib/stores/sdk'; import { user } from '$lib/stores/user'; import { isCloud } from '$lib/system'; @@ -176,7 +177,8 @@ $: subNavigation = $page.data.subNavigation; - $: shouldRenderSidebar = !$isNewWizardStatusOpen && showSideNavigation; + $: shouldRenderSidebar = + !$isNewWizardStatusOpen && showSideNavigation && !$showOnboardingAnimation; $: hasSidebarSpace = shouldRenderSidebar && !$isTabletViewport && !!selectedProject; $: { @@ -203,9 +205,9 @@ class:is-open={$showSubNavigation} class:u-hide={$wizard.show || $wizard.cover} class:is-fixed-layout={$activeHeaderAlert?.show} - class:no-header={!showHeader} + class:no-header={!showHeader || $showOnboardingAnimation} style:--p-side-size={sideSize}> - {#if showHeader} + {#if showHeader && !$showOnboardingAnimation} {/if} @@ -220,7 +222,9 @@ bind:state /> {/if} - + {#if !$showOnboardingAnimation} + + {/if}
= { - appwrite: [...resources], // new terminology, others are ok? +export const providerResources: Record = { + appwrite: Object.values(Resources), supabase: [ - 'user', - 'database', - 'collection', - 'attribute', - 'index', - 'document', - 'bucket', - 'file' + Resources.User, + Resources.Database, + Resources.Collection, + Resources.Attribute, + Resources.Index, + Resources.Document, + Resources.Bucket, + Resources.File ], - nhost: ['user', 'database', 'collection', 'attribute', 'index', 'document', 'bucket', 'file'], - firebase: ['user', 'database', 'collection', 'attribute', 'document', 'bucket', 'file'] + nhost: [ + Resources.User, + Resources.Database, + Resources.Collection, + Resources.Attribute, + Resources.Index, + Resources.Document, + Resources.Bucket, + Resources.File + ], + firebase: [ + Resources.User, + Resources.Database, + Resources.Collection, + Resources.Attribute, + Resources.Document, + Resources.Bucket, + Resources.File + ] }; export const migrationFormToResources = ( formData: MigrationFormData, provider: Provider -): Resource[] => { - const resources: Resource[] = []; - const addResource = (resource: Resource) => { +): Resources[] => { + const resources: Resources[] = []; + const addResource = (resource: Resources) => { if (providerResources[provider].includes(resource)) { resources.push(resource); } }; if (formData.users.root) { - addResource('user'); - } - if (formData.users.teams) { - addResource('team'); - addResource('membership'); + addResource(Resources.User); } if (formData.databases.root) { - addResource('database'); - addResource('table'); - addResource('column'); - addResource('columnIndex'); + addResource(Resources.Database); + addResource(Resources.Table); + addResource(Resources.Column); + addResource(Resources.Index); } if (formData.databases.rows) { - addResource('row'); - } - if (formData.functions.root) { - addResource('function'); - } - if (formData.functions.env) { - addResource('environment-variable'); - } - if (formData.functions.inactive) { - addResource('deployment'); + addResource(Resources.Row); } if (formData.storage.root) { - addResource('bucket'); - addResource('file'); + addResource(Resources.Bucket); + addResource(Resources.File); } return resources; @@ -137,33 +137,18 @@ export const isVersionAtLeast = (version: string, atLeast: string) => { return compareVersions(version, atLeast) >= 0; }; -export const resourcesToMigrationForm = ( - resources: Resource[], - version = '0.0.0' -): MigrationFormData => { +export const resourcesToMigrationForm = (resources: Resources[]): MigrationFormData => { const formData = { ...initialFormData }; - if (resources.includes('user')) { + if (resources.includes(Resources.User)) { formData.users.root = true; } - if (includesAll(resources, ['team', 'membership'])) { - formData.users.teams = true; - } - if (resources.includes('database')) { + if (resources.includes(Resources.Database)) { formData.databases.root = true; } - if (includesAll(resources, ['table', 'column', 'row'])) { + if (includesAll(resources, [Resources.Table, Resources.Column, Resources.Row] as Resources[])) { formData.databases.rows = true; } - if (resources.includes('function') && isVersionAtLeast(version, '1.4.0')) { - formData.functions.root = true; - } - if (resources.includes('environment-variable') && isVersionAtLeast(version, '1.4.0')) { - formData.functions.env = true; - } - if (resources.includes('deployment') && isVersionAtLeast(version, '1.4.0')) { - formData.functions.inactive = true; - } - if (includesAll(resources, ['bucket', 'file'])) { + if (includesAll(resources, [Resources.Bucket, Resources.File] as Resources[])) { formData.storage.root = true; } diff --git a/src/routes/(console)/(migration-wizard)/resource-form.svelte b/src/routes/(console)/(migration-wizard)/resource-form.svelte index 628c6c2a3..54e3f23ba 100644 --- a/src/routes/(console)/(migration-wizard)/resource-form.svelte +++ b/src/routes/(console)/(migration-wizard)/resource-form.svelte @@ -5,14 +5,13 @@ import { createMigrationFormStore, createMigrationProviderStore, - isVersionAtLeast, type MigrationFormData, providerResources, resourcesToMigrationForm } from '$lib/stores/migration'; import { Button } from '$lib/elements/forms'; import { wizard } from '$lib/stores/wizard'; - import type { Models } from '@appwrite.io/console'; + import { Resources, type Models } from '@appwrite.io/console'; import type { sdk } from '$lib/stores/sdk'; import ImportReport from '$routes/(console)/project-[region]-[project]/settings/migrations/(import)/importReport.svelte'; @@ -33,11 +32,9 @@ } function selectAll() { - $formData = resourcesToMigrationForm(resources, version); + $formData = resourcesToMigrationForm(resources); } - $: version = report?.version || '0.0.0'; - let error = false; let isOpen = false; let report: Models.MigrationReport; @@ -98,14 +95,21 @@ const shouldRenderGroup = (groupKey: string): boolean => { if (groupKey === 'functions') { - return resources.includes('function') && isVersionAtLeast(version, '1.4.0'); + // Functions not in SDK Resources enum, skip + return false; } if (groupKey === 'storage') { - return resources.includes('bucket') && resources.includes('file'); + return resources.includes(Resources.Bucket) && resources.includes(Resources.File); } - return resources.includes(groupKey.slice(0, -1)); + // Map groupKey to Resources enum + const groupToResource: Record = { + users: Resources.User, + databases: Resources.Database + }; + const resource = groupToResource[groupKey]; + return resource ? resources.includes(resource) : false; }; // no typecasting in svelte context! @@ -125,14 +129,6 @@ - {#if report && !isVersionAtLeast(version, '1.4.0') && $provider.provider === 'appwrite'} - - Functions not available for import - To migrate your functions, update the version of the Appwrite instance you're importing from - to a version newer than 1.4 - - {/if} - {#if error} {#if migrationType === 'provider'} diff --git a/src/routes/(console)/account/+layout.ts b/src/routes/(console)/account/+layout.ts index 901bf8a83..4476e3a5e 100644 --- a/src/routes/(console)/account/+layout.ts +++ b/src/routes/(console)/account/+layout.ts @@ -3,6 +3,7 @@ import Breadcrumbs from './breadcrumbs.svelte'; import type { LayoutLoad } from './$types'; import { sdk } from '$lib/stores/sdk'; import { Dependencies } from '$lib/constants'; +import { Platform, Query } from '@appwrite.io/console'; export const load: LayoutLoad = async ({ depends }) => { depends(Dependencies.FACTORS); @@ -10,7 +11,9 @@ export const load: LayoutLoad = async ({ depends }) => { const [factors, identities] = await Promise.all([ sdk.forConsole.account.listMFAFactors(), - sdk.forConsole.account.listIdentities() + sdk.forConsole.account.listIdentities({ + queries: [Query.notContains('provider', Platform.Imagine)] + }) ]); return { diff --git a/src/routes/(console)/account/sessions/+page.svelte b/src/routes/(console)/account/sessions/+page.svelte index bbc261abc..30d1e7829 100644 --- a/src/routes/(console)/account/sessions/+page.svelte +++ b/src/routes/(console)/account/sessions/+page.svelte @@ -97,6 +97,7 @@ { await invalidate(Dependencies.ACCOUNT); - goto(`${base}/project-${project.region ?? 'default'}-${project.$id}`); + await goto( + resolve('/(console)/project-[region]-[project]', { + region: project.region ?? 'default', + project: project.$id + }) + ); + showOnboardingAnimation.set(false); }, 3000); } catch (e) { trackError(e, Submit.ProjectCreate); diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index afa38a5ee..184a0ab87 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -169,11 +169,11 @@ {/if} - {#if isCloud && data.currentPlan?.projects !== 0 && activeProjectsTotal <= data.currentPlan.projects && !freePlanAlertDismissed} + {#if isCloud && !data.program && data.currentPlan?.projects && activeProjectsTotal <= data.currentPlan.projects && !freePlanAlertDismissed} Your Free plan includes up to 2 projects and limited resources. Upgrade to unlock - more capacity and features. + >Your Free plan includes up to {data.currentPlan?.projects} projects and limited resources. + Upgrade to unlock more capacity and features.