Merge branch 'main' into feat-improve-domain-flows

This commit is contained in:
Khushboo Verma
2025-09-19 11:58:19 +05:30
61 changed files with 1606 additions and 193 deletions
+3 -1
View File
@@ -32,7 +32,9 @@ export default ts.config(
'svelte/no-at-html-tags': 'off',
'svelte/no-useless-mustaches': 'off',
'svelte/no-reactive-reassign': 'off',
'svelte/no-reactive-literals': 'off'
'svelte/no-reactive-literals': 'off',
// TODO: @itznotabug, this requires a big refactor!
'svelte/no-navigation-without-resolve': 'off'
}
},
{
+4 -4
View File
@@ -22,11 +22,11 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134",
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4a33959",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f4da718",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4a33959",
"@faker-js/faker": "^9.9.0",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
@@ -54,7 +54,7 @@
"@melt-ui/svelte": "^0.86.5",
"@playwright/test": "^1.51.1",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.36.2",
"@sveltejs/kit": "^2.42.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
+47 -40
View File
@@ -12,20 +12,20 @@ importers:
specifier: ^1.1.24
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
'@appwrite.io/console':
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134
specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562
version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562
'@appwrite.io/pink-icons':
specifier: 0.25.0
version: 0.25.0
'@appwrite.io/pink-icons-svelte':
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4a33959
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4a33959(svelte@5.25.3)
'@appwrite.io/pink-legacy':
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f4da718
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f4da718(svelte@5.25.3)
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4a33959
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4a33959(svelte@5.25.3)
'@faker-js/faker':
specifier: ^9.9.0
version: 9.9.0
@@ -34,7 +34,7 @@ importers:
version: 2.11.8
'@sentry/sveltekit':
specifier: ^8.38.0
version: 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
version: 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
'@stripe/stripe-js':
specifier: ^3.5.0
version: 3.5.0
@@ -101,10 +101,10 @@ importers:
version: 1.51.1
'@sveltejs/adapter-static':
specifier: ^3.0.8
version: 3.0.8(@sveltejs/kit@2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))
version: 3.0.8(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))
'@sveltejs/kit':
specifier: ^2.36.2
version: 2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
specifier: ^2.42.1
version: 2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
'@sveltejs/vite-plugin-svelte':
specifier: ^5.0.3
version: 5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
@@ -260,8 +260,8 @@ packages:
'@analytics/type-utils@0.6.2':
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134':
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134}
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562':
resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562}
version: 1.10.0
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
@@ -269,8 +269,8 @@ packages:
peerDependencies:
svelte: ^4.0.0
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718}
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4a33959':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4a33959}
version: 2.0.0-RC.1
peerDependencies:
svelte: ^4.0.0
@@ -284,8 +284,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.3':
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f4da718':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f4da718}
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4a33959':
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4a33959}
version: 2.0.0-RC.2
peerDependencies:
svelte: ^4.0.0
@@ -352,8 +352,8 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/parser@7.28.3':
resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==}
'@babel/parser@7.28.4':
resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -373,8 +373,8 @@ packages:
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
engines: {node: '>=6.9.0'}
'@babel/types@7.28.2':
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
'@babel/types@7.28.4':
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
engines: {node: '>=6.9.0'}
'@csstools/color-helpers@5.0.2':
@@ -1264,8 +1264,8 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.0.0
'@sveltejs/kit@2.36.3':
resolution: {integrity: sha512-MVzwZz1GFznEQbL3f0i2v9AIc3lZH01izQj6XfIrthmZAwOzyXJCgXbLRss8vk//HfYsE4w6Tz+ukbf3rScPNQ==}
'@sveltejs/kit@2.42.1':
resolution: {integrity: sha512-SoWdb/OxEKHMlXZ78eNZa4pR1YdOGnUfcFj5NylJv+ZaTrnHK5xapw+7RJP2MiS1D4T+sEH25/tmMtmEl8p6VA==}
engines: {node: '>=18.13'}
hasBin: true
peerDependencies:
@@ -2607,6 +2607,9 @@ packages:
magic-string@0.30.18:
resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==}
magic-string@0.30.19:
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
magic-string@0.30.7:
resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
engines: {node: '>=12'}
@@ -3050,8 +3053,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
seroval-plugins@1.3.2:
resolution: {integrity: sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==}
seroval-plugins@1.3.3:
resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==}
engines: {node: '>=10'}
peerDependencies:
seroval: ^1.0
@@ -3700,13 +3703,13 @@ snapshots:
'@analytics/type-utils@0.6.2': {}
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134': {}
'@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562': {}
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718(svelte@5.25.3)':
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4a33959(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
@@ -3719,7 +3722,7 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f4da718(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4a33959(svelte@5.25.3)':
dependencies:
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
'@floating-ui/dom': 1.6.13
@@ -3822,9 +3825,9 @@ snapshots:
dependencies:
'@babel/types': 7.27.0
'@babel/parser@7.28.3':
'@babel/parser@7.28.4':
dependencies:
'@babel/types': 7.28.2
'@babel/types': 7.28.4
'@babel/runtime@7.27.0':
dependencies:
@@ -3853,7 +3856,7 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@babel/types@7.28.2':
'@babel/types@7.28.4':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
@@ -4671,14 +4674,14 @@ snapshots:
magic-string: 0.30.7
svelte: 5.25.3
'@sentry/sveltekit@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
'@sentry/sveltekit@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
dependencies:
'@sentry/core': 8.55.0
'@sentry/node': 8.55.0
'@sentry/opentelemetry': 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)
'@sentry/svelte': 8.55.0(svelte@5.25.3)
'@sentry/vite-plugin': 2.22.6
'@sveltejs/kit': 2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
'@sveltejs/kit': 2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
magic-string: 0.30.7
magicast: 0.2.8
sorcery: 1.0.0
@@ -4750,11 +4753,11 @@ snapshots:
dependencies:
acorn: 8.15.0
'@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))':
'@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))':
dependencies:
'@sveltejs/kit': 2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
'@sveltejs/kit': 2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
'@sveltejs/kit@2.36.3(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
'@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
dependencies:
'@standard-schema/spec': 1.0.0
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
@@ -5121,7 +5124,7 @@ snapshots:
'@vue/compiler-core@3.5.13':
dependencies:
'@babel/parser': 7.28.3
'@babel/parser': 7.28.4
'@vue/shared': 3.5.13
entities: 4.5.0
estree-walker: 2.0.2
@@ -5134,13 +5137,13 @@ snapshots:
'@vue/compiler-sfc@3.5.13':
dependencies:
'@babel/parser': 7.28.3
'@babel/parser': 7.28.4
'@vue/compiler-core': 3.5.13
'@vue/compiler-dom': 3.5.13
'@vue/compiler-ssr': 3.5.13
'@vue/shared': 3.5.13
estree-walker: 2.0.2
magic-string: 0.30.18
magic-string: 0.30.19
postcss: 8.5.6
source-map-js: 1.2.1
@@ -6282,6 +6285,10 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
magic-string@0.30.19:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
magic-string@0.30.7:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -6703,7 +6710,7 @@ snapshots:
semver@7.7.1: {}
seroval-plugins@1.3.2(seroval@1.3.2):
seroval-plugins@1.3.3(seroval@1.3.2):
dependencies:
seroval: 1.3.2
@@ -6786,7 +6793,7 @@ snapshots:
dependencies:
csstype: 3.1.3
seroval: 1.3.2
seroval-plugins: 1.3.2(seroval@1.3.2)
seroval-plugins: 1.3.3(seroval@1.3.2)
solid-swr-store@0.10.7(solid-js@1.9.5)(swr-store@0.10.6):
dependencies:
+4 -3
View File
@@ -6,7 +6,7 @@ import { user } from '$lib/stores/user';
import { ENV, MODE, VARS, isCloud } from '$lib/system';
import { AppwriteException } from '@appwrite.io/console';
import { browser } from '$app/environment';
import { getReferrerAndUtmSource } from '$lib/helpers/utm';
import { getReferrerAndUtmSource, getTrackedQueryParams } from '$lib/helpers/utm';
function plausible(domain: string): AnalyticsPlugin {
if (!browser) return { name: 'analytics-plugin-plausible' };
@@ -65,7 +65,7 @@ export function trackEvent(name: string, data: object = null): void {
};
}
data = { ...data, ...getReferrerAndUtmSource() };
data = { ...data, ...getReferrerAndUtmSource(), ...getTrackedQueryParams() };
if (ENV.DEV || ENV.PREVIEW) {
console.debug(`[Analytics] Event ${name} ${path}`, data);
@@ -429,5 +429,6 @@ export enum Submit {
RecordUpdate = 'submit_dns_record_update',
RecordDelete = 'submit_dns_record_delete',
SearchClear = 'submit_clear_search',
FrameworkDetect = 'submit_framework_detect'
FrameworkDetect = 'submit_framework_detect',
TestimonialView = 'testimonial_view'
}
@@ -23,7 +23,7 @@
}
</script>
{#if show && $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && !page.url.pathname.includes('/console/account')}
{#if show && $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && !page.url.pathname.includes(base + '/account')}
<GradientBanner on:close={handleClose}>
<Layout.Stack
gap="m"
+31 -8
View File
@@ -18,14 +18,15 @@
import { BottomSheet } from '$lib/components';
import { isSmallViewport } from '$lib/stores/viewport';
import { isCloud } from '$lib/system';
import { goto } from '$app/navigation';
import { goto, beforeNavigate } from '$app/navigation';
import { base } from '$app/paths';
import { currentPlan, newOrgModal, organization } from '$lib/stores/organization';
import { Click, trackEvent } from '$lib/actions/analytics';
import { type Models, Query } from '@appwrite.io/console';
import { ID, type Models, Query } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import { BillingPlan } from '$lib/constants';
import { onDestroy } from 'svelte';
type Organization = {
name: string;
@@ -34,11 +35,13 @@
isSelected: boolean;
};
const menubarBuilder = createMenubar();
const {
elements: { menubar },
builders: { createMenu }
} = createMenubar();
} = menubarBuilder;
const orgMenuBuilder = createMenu();
const {
elements: {
trigger: triggerOrganizations,
@@ -46,8 +49,9 @@
item: itemOrganizations,
separator: separatorOrganizations
},
builders: { createSubmenu: createSubmenuOrganizations, createMenuRadioGroup }
} = createMenu();
builders: { createSubmenu: createSubmenuOrganizations, createMenuRadioGroup },
states: orgMenuStates
} = orgMenuBuilder;
const {
elements: { radioGroup: radioGroupOrganizations }
@@ -57,14 +61,16 @@
elements: { subMenu: subMenuOrganizations, subTrigger: subTriggerOrganizations }
} = createSubmenuOrganizations();
const projectsMenuBuilder = createMenu();
const {
elements: {
trigger: triggerProjects,
menu: menuProjects,
item: itemProjects,
separator: separatorProjects
}
} = createMenu();
},
states: projectsMenuStates
} = projectsMenuBuilder;
let isLoadingProjects = false;
let loadedProjects: Models.ProjectList = { total: 0, projects: [] };
@@ -76,6 +82,23 @@
let organisationBottomSheetOpen = false;
let projectsBottomSheet: Promise<SheetMenu | null> = null;
function closeAllMenus() {
if (orgMenuStates?.open) {
orgMenuStates.open.set(false);
}
if (projectsMenuStates?.open) {
projectsMenuStates.open.set(false);
}
projectsBottomSheetOpen = false;
organisationBottomSheetOpen = false;
}
beforeNavigate(closeAllMenus);
onDestroy(closeAllMenus);
function createOrg() {
trackEvent(Click.OrganizationClickCreate, { source: 'breadcrumbs' });
if (isCloud) {
@@ -212,7 +235,7 @@
? $currentPlan.name
: selectedOrg?.tierName; // fallback
$: derivedKey = `${selectedOrg?.$id}-${currentProject?.$id}`;
$: derivedKey = `${selectedOrg?.$id}-${currentProject?.$id}-${ID.unique()}`;
$: organizationId = currentProject?.teamId;
+60 -3
View File
@@ -6,10 +6,11 @@
InputText,
InputTags,
InputSelectCheckbox,
InputDateTime
InputDateTime,
InputPoint
} from '$lib/elements/forms';
import { onMount, createEventDispatcher } from 'svelte';
import { operators, addFilter, queries, tags } from './store';
import { operators, addFilter, queries, tags, ValidOperators } from './store';
import type { Column } from '$lib/helpers/types';
import type { Writable } from 'svelte/store';
import { TagList } from '.';
@@ -18,6 +19,7 @@
let {
value = $bindable(null),
distanceValue = $bindable(null),
columns,
columnId = $bindable(null),
arrayValues = $bindable([]),
@@ -27,6 +29,7 @@
// We cast to any to not cause type errors in the input components
/* eslint @typescript-eslint/no-explicit-any: 'off' */
value?: any;
distanceValue?: number | null;
columns: Writable<Column[]>;
columnId?: string | null;
arrayValues?: string[];
@@ -61,21 +64,57 @@
}));
});
// Check if the current operator is a distance-based operator
let isDistanceOperator = $derived(
operatorKey &&
[
ValidOperators.DistanceEqual,
ValidOperators.DistanceNotEqual,
ValidOperators.DistanceGreaterThan,
ValidOperators.DistanceLessThan
].includes(operatorKey as ValidOperators)
);
onMount(() => {
value = column?.array ? [] : null;
if (column?.type === 'datetime') {
const now = new Date();
value = now.toISOString().slice(0, 16);
}
// Initialize spatial data with default values
if (column?.type === 'point') {
value = [0, 0];
} else if (column?.type === 'linestring') {
value = [
[0, 0],
[1, 1]
];
} else if (column?.type === 'polygon') {
value = [
[
[0, 0],
[1, 1],
[2, 2],
[0, 0]
]
];
}
});
const dispatch = createEventDispatcher<{ clear: void; apply: { applied: number } }>();
function addFilterAndReset() {
addFilter(columnsArray, columnId, operatorKey, value, arrayValues);
// For distance operators, pass the distance as a separate parameter
if (isDistanceOperator && distanceValue !== null && value !== null) {
addFilter(columnsArray, columnId, operatorKey, value, arrayValues, distanceValue);
} else {
addFilter(columnsArray, columnId, operatorKey, value, arrayValues);
}
columnId = null;
operatorKey = null;
value = null;
distanceValue = null;
arrayValues = [];
dispatch('apply', { applied: appliedTags.length });
if (singleCondition) {
@@ -143,11 +182,29 @@
{#key value}
<InputDateTime id="value" bind:value step={60} type="datetime-local" />
{/key}
{:else if column.type === 'point' || column.type === 'linestring' || column.type === 'polygon'}
<InputPoint
values={value || [0, 0]}
onChangePoint={(index, newValue) => {
if (!value) value = [0, 0];
value[index] = newValue;
}} />
{:else}
<InputText id="value" bind:value placeholder="Enter value" />
{/if}
</ul>
{/if}
{#if isDistanceOperator}
<div class="u-margin-block-start-8">
<InputNumber
id="distance"
bind:value={distanceValue}
placeholder="Enter distance"
step={0.001}
required />
</div>
{/if}
{/if}
{#if !singleCondition}
<Button text disabled={isDisabled} class="u-margin-block-start-4" submit>
+23 -1
View File
@@ -36,6 +36,7 @@
/* eslint @typescript-eslint/no-explicit-any: 'off' */
let value: any = null;
let distanceValue: number | null = null;
let selectedColumn: string | null = null;
let operatorKey: string | null = null;
let arrayValues: string[] = [];
@@ -46,6 +47,16 @@
$: filtersAppliedCount = $tags.length;
// Check if the current operator is a distance-based operator
$: isDistanceOperator =
operatorKey &&
[
ValidOperators.DistanceEqual,
ValidOperators.DistanceNotEqual,
ValidOperators.DistanceGreaterThan,
ValidOperators.DistanceLessThan
].includes(operatorKey as ValidOperators);
beforeNavigate(() => {
showFiltersDesktop = false;
showFiltersMobile = false;
@@ -54,6 +65,8 @@
function clearAll() {
selectedColumn = null;
value = null;
distanceValue = null;
queries.clearAll();
if (clearOnClick) {
trackEvent(Submit.FilterClear, { source: analyticsSource });
@@ -73,9 +86,15 @@
value ||
arrayValues.length)
) {
addFilter($columns, selectedColumn, operatorKey, value, arrayValues);
// For distance operators, pass the distance as a separate parameter
if (isDistanceOperator && distanceValue !== null && value !== null) {
addFilter($columns, selectedColumn, operatorKey, value, arrayValues, distanceValue);
} else {
addFilter($columns, selectedColumn, operatorKey, value, arrayValues);
}
selectedColumn = null;
value = null;
distanceValue = null;
operatorKey = null;
arrayValues = [];
}
@@ -97,6 +116,7 @@
$: if (!showFiltersDesktop && !showFiltersMobile) {
selectedColumn = null;
value = null;
distanceValue = null;
operatorKey = null;
arrayValues = [];
}
@@ -163,6 +183,7 @@
bind:columnId={selectedColumn}
bind:operatorKey
bind:value
bind:distanceValue
bind:arrayValues
{columns}
{singleCondition}
@@ -225,6 +246,7 @@
bind:columnId={selectedColumn}
bind:operatorKey
bind:value
bind:distanceValue
bind:arrayValues
{singleCondition}
on:apply={afterApply}
+155 -14
View File
@@ -5,6 +5,7 @@ import { Query } from '@appwrite.io/console';
import { toLocaleDateTime } from '$lib/helpers/date';
import { derived, get, writable } from 'svelte/store';
import type { Column, ColumnType } from '$lib/helpers/types';
import { isSpatialType } from '$routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/rows/store';
export type TagValue = {
tag: string;
@@ -13,7 +14,7 @@ export type TagValue = {
export type Operator = {
toTag: (column: string, input?: string | number | string[], type?: string) => TagValue;
toQuery: (column: string, input?: string | number | string[]) => string;
toQuery: (column: string, input?: string | number | string[], distance?: number) => string;
types: ColumnType[];
hideInput?: boolean;
};
@@ -35,13 +36,14 @@ function initQueries(initialValue = new Map<TagValue, string>()) {
operator: Operator;
column: Column;
value: string | number | string[];
distance?: number;
};
function addFilter({ column, operator, value }: AddFilterArgs) {
function addFilter({ column, operator, value, distance }: AddFilterArgs) {
queries.update((map) => {
map.set(
operator.toTag(column.title, value, column?.type),
operator.toQuery(column.id, value)
operator.toQuery(column.id, value, distance)
);
return map;
});
@@ -101,15 +103,27 @@ export function addFilter(
columnId: string,
operatorKey: string,
value: any, // We cast to any to not cause type errors in the input components
arrayValues: string[] = []
arrayValues: string[] = [],
distance?: number
) {
const operator = operatorKey ? operators[operatorKey] : null;
const column = columns.find((c) => c.id === columnId) as Column;
if (!column || !operator) return;
if (column.array) {
queries.addFilter({ column, operator, value: arrayValues });
queries.addFilter({ column, operator, value: arrayValues, distance });
} else {
queries.addFilter({ column, operator, value: value ?? '' });
const isSpatialArrayWrappingRequired =
isSpatialType(column) &&
[
ValidOperators.Contains,
ValidOperators.Equal,
ValidOperators.NotEqual,
ValidOperators.NotContains
].includes(operatorKey as ValidOperators);
const preparedValue =
isSpatialArrayWrappingRequired && value != null ? [value] : (value ?? '');
queries.addFilter({ column, operator, value: preparedValue, distance });
}
}
@@ -124,7 +138,20 @@ export enum ValidOperators {
NotEqual = 'not equal',
IsNotNull = 'is not null',
IsNull = 'is null',
Contains = 'contains'
Contains = 'contains',
NotContains = 'not contains',
Crosses = 'crosses',
NotCrosses = 'not crosses',
DistanceEqual = 'distance equal',
DistanceNotEqual = 'distance not equal',
DistanceGreaterThan = 'distance greater than',
DistanceLessThan = 'distance less than',
Intersects = 'intersects',
NotIntersects = 'not intersects',
Overlaps = 'overlaps',
NotOverlaps = 'not overlaps',
Touches = 'touches',
NotTouches = 'not touches'
}
export enum ValidTypes {
@@ -134,13 +161,16 @@ export enum ValidTypes {
Boolean = 'boolean',
Datetime = 'datetime',
Relationship = 'relationship',
Enum = 'enum'
Enum = 'enum',
Point = 'point',
Line = 'linestring',
Polygon = 'polygon'
}
const operatorsDefault = new Map<
ValidOperators,
{
query: (col: string, input: string | number | string[]) => string;
query: (col: string, input: string | number | string[], distance?: number) => string;
types: ColumnType[];
hideInput?: boolean;
}
@@ -184,7 +214,10 @@ const operatorsDefault = new Map<
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
ValidTypes.Enum
ValidTypes.Enum,
ValidTypes.Point,
ValidTypes.Line,
ValidTypes.Polygon
]
}
],
@@ -192,7 +225,15 @@ const operatorsDefault = new Map<
ValidOperators.NotEqual,
{
query: Query.notEqual,
types: [ValidTypes.String, ValidTypes.Integer, ValidTypes.Double, ValidTypes.Boolean]
types: [
ValidTypes.String,
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
ValidTypes.Point,
ValidTypes.Line,
ValidTypes.Polygon
]
}
],
[
@@ -205,7 +246,10 @@ const operatorsDefault = new Map<
ValidTypes.Double,
ValidTypes.Boolean,
ValidTypes.Datetime,
ValidTypes.Relationship
ValidTypes.Relationship,
ValidTypes.Point,
ValidTypes.Line,
ValidTypes.Polygon
],
hideInput: true
}
@@ -220,7 +264,10 @@ const operatorsDefault = new Map<
ValidTypes.Double,
ValidTypes.Boolean,
ValidTypes.Datetime,
ValidTypes.Relationship
ValidTypes.Relationship,
ValidTypes.Point,
ValidTypes.Line,
ValidTypes.Polygon
],
hideInput: true
}
@@ -235,9 +282,103 @@ const operatorsDefault = new Map<
ValidTypes.Double,
ValidTypes.Boolean,
ValidTypes.Datetime,
ValidTypes.Enum
ValidTypes.Enum,
ValidTypes.Point,
ValidTypes.Line,
ValidTypes.Polygon
]
}
],
[
ValidOperators.NotContains,
{
query: Query.contains,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.Crosses,
{
query: Query.crosses,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.NotCrosses,
{
query: Query.notCrosses,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.DistanceEqual,
{
query: Query.distanceEqual,
types: [ValidTypes.Point]
}
],
[
ValidOperators.DistanceNotEqual,
{
query: Query.distanceNotEqual,
types: [ValidTypes.Point]
}
],
[
ValidOperators.DistanceGreaterThan,
{
query: Query.distanceGreaterThan,
types: [ValidTypes.Point]
}
],
[
ValidOperators.DistanceLessThan,
{
query: Query.distanceLessThan,
types: [ValidTypes.Point]
}
],
[
ValidOperators.Intersects,
{
query: Query.intersects,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.NotIntersects,
{
query: Query.notIntersects,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.Overlaps,
{
query: Query.overlaps,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.NotOverlaps,
{
query: Query.notOverlaps,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.Touches,
{
query: Query.touches,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
],
[
ValidOperators.NotTouches,
{
query: Query.notTouches,
types: [ValidTypes.Point, ValidTypes.Line, ValidTypes.Polygon]
}
]
]);
+7 -6
View File
@@ -42,6 +42,7 @@
import type { HTMLAttributes } from 'svelte/elements';
import type { Models } from '@appwrite.io/console';
import { noWidthTransition } from '$lib/stores/sidebar';
import { base } from '$app/paths';
type $$Props = HTMLAttributes<HTMLElement> & {
state?: 'closed' | 'open' | 'icons';
@@ -138,7 +139,7 @@
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
class="progress-card"
href={`/console/project-${project.region}-${project.$id}/get-started`}
href={`${base}/project-${project?.region}-${project?.$id}/get-started`}
on:click={() => {
trackEvent('click_menu_get_started');
sideBarIsOpen = false;
@@ -162,7 +163,7 @@
<Layout.Stack direction="column" gap="s">
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/overview/platforms`}
href={`${base}/project-${project.region}-${project.$id}/overview/platforms`}
class="link"
class:active={isSelected('overview')}
on:click={() => {
@@ -192,7 +193,7 @@
{#each buildProjectOptions as projectOption}
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/${projectOption.slug}`}
href={`${base}/project-${project.region}-${project.$id}/${projectOption.slug}`}
class="link"
class:active={isSelected(projectOption.slug)}
on:click={() => {
@@ -223,7 +224,7 @@
{#each deployProjectOptions as projectOption}
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/${projectOption.slug}`}
href={`${base}/project-${project.region}-${project.$id}/${projectOption.slug}`}
class="link"
class:active={isSelected(projectOption.slug)}
on:click={() => {
@@ -253,7 +254,7 @@
<div class="mobile-tablet-settings">
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/settings`}
href={`${base}/project-${project.region}-${project.$id}/settings`}
on:click={() => {
trackEvent('click_menu_settings');
sideBarIsOpen = false;
@@ -313,7 +314,7 @@
<div class="only-desktop">
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/settings`}
href={`${base}/project-${project.region}-${project.$id}/settings`}
class="link"
on:click={() => {
trackEvent('click_menu_settings');
+58
View File
@@ -0,0 +1,58 @@
import DevKind from '$lib/images/testimonials/hassan.png';
import LangX from '$lib/images/testimonials/xue.webp';
import KCollect from '$lib/images/testimonials/ryan-oconner-testimonial.png';
import Phil from '$lib/images/testimonials/phil.jpg';
export interface Testimonial {
id: string;
logo: string;
headline: string;
blurb: string;
name: string;
title: string;
avatar: string;
}
export const testimonials: Testimonial[] = [
{
id: 'hassan-devkind',
logo: DevKind,
headline: 'DevKind reduced development time by 60% and lowered server costs by 40%',
blurb: 'A special thanks to Appwrite for providing robust features and seamless functionality.',
name: 'Hassan Ahmed',
title: 'Engineer at DevKind',
avatar: DevKind
},
{
id: 'xue-langx',
logo: LangX,
headline: 'LangX handled millions of requests using Appwrite',
blurb: 'With its comprehensive suite of services, Appwrite emerged as an ideal choice for my needs.',
name: 'Xue',
title: 'Founder at LangX',
avatar: LangX
},
{
id: 'ryan-kcollect',
logo: KCollect,
headline: 'K-Collect reduced infrastructure costs by 700%',
blurb: 'A major impact that Appwrite made was the amount of time and stress saved.',
name: "Ryan O'Connor",
title: 'Founder at K-Collect',
avatar: KCollect
},
{
id: 'phil-majikkids',
logo: Phil,
headline: 'Just like a Swiss Army Knife - choose and use the tools you need',
blurb: 'Just like a Swiss Army Knife you can choose and use the tools that you need with Appwrite.',
name: 'Phil McCluskey',
title: 'App Manager, Majik Kids',
avatar: Phil
}
];
export function getRandomTestimonial(): Testimonial {
const randomIndex = Math.floor(Math.random() * testimonials.length);
return testimonials[randomIndex];
}
+3
View File
@@ -31,3 +31,6 @@ export { default as InputDigits } from './inputDigits.svelte';
export { default as InputSelectCheckbox } from './inputSelectCheckbox.svelte';
export { default as InputFilePicker } from './inputFilePicker.svelte';
export { default as Seekbar } from './Seekbar.svelte';
export { default as InputPoint } from './inputPoint.svelte';
export { default as InputLine } from './inputLine.svelte';
export { default as InputPolygon } from './inputPolygon.svelte';
+61
View File
@@ -0,0 +1,61 @@
<script lang="ts">
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import InputPoint from './inputPoint.svelte';
import Button from './button.svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import type { Snippet } from 'svelte';
type Props = {
values: number[][];
nullable?: boolean;
minDeletableIndex?: number;
allowLineDelete?: boolean;
onAddPoint: (index: number) => void;
onDeletePoint: (index: number) => void;
onChangePoint: (pointIndex: number, coordIndex: number, newValue: number) => void;
addLineButton?: Snippet;
};
let {
values,
nullable = false,
minDeletableIndex = 2,
allowLineDelete,
onAddPoint,
onDeletePoint,
onChangePoint,
addLineButton
}: Props = $props();
function isDeleteDisabled(index: number) {
let disable = index < minDeletableIndex;
if (allowLineDelete !== undefined) {
disable = disable && allowLineDelete;
}
return disable;
}
</script>
<Layout.Stack alignItems="flex-start" gap="xs">
<Layout.Stack>
{#each values as value, index}
<InputPoint
{nullable}
values={value}
deletePoints
disableDelete={isDeleteDisabled(index)}
onDeletePoint={() => onDeletePoint(index)}
onChangePoint={(coordIndex, newValue) =>
onChangePoint?.(index, coordIndex, newValue)} />
{/each}
</Layout.Stack>
{#if values}
<Layout.Stack direction="row" gap="s" alignItems="center">
<Button size="xs" compact on:click={() => onAddPoint(-1)} disabled={nullable}>
<Icon icon={IconPlus} size="s" /> Add coordinate
</Button>
{@render addLineButton?.()}
</Layout.Stack>
{/if}
</Layout.Stack>
+54
View File
@@ -0,0 +1,54 @@
<script lang="ts">
import { Input } from '@appwrite.io/pink-svelte';
import { Layout, Icon } from '@appwrite.io/pink-svelte';
import Button from './button.svelte';
import { IconX } from '@appwrite.io/pink-icons-svelte';
interface Props {
values: number[];
nullable?: boolean;
deletePoints?: boolean;
onDeletePoint?: () => void;
disableDelete?: boolean;
onChangePoint: (index: number, newValue: number) => void;
}
const nullableSkeletonShape: number[] = [0, 0];
let {
values,
nullable = false,
deletePoints = false,
disableDelete = false,
onDeletePoint,
onChangePoint
}: Props = $props();
</script>
<Layout.Stack>
<Layout.Stack direction="row" gap="m">
{#if nullable}
{#each nullableSkeletonShape as _, index}
<Input.Number id={`default-${index}`} placeholder="0" disabled />
{/each}
{:else}
{#each values as _, index}
<Input.Number
id={`point-${index}`}
placeholder="Enter value"
step={0.0001}
value={values[index]}
on:change={(e) => onChangePoint(index, Number.parseFloat(`${e.detail}`))} />
{/each}
{/if}
{#if deletePoints}
<Button
size="s"
secondary
disabled={nullable || disableDelete}
on:click={() => onDeletePoint?.()}>
<Icon icon={IconX} size="s" />
</Button>
{/if}
</Layout.Stack>
</Layout.Stack>
@@ -0,0 +1,59 @@
<script lang="ts">
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import Button from './button.svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import InputLine from './inputLine.svelte';
type Props = {
values: number[][][];
nullable?: boolean;
minDeletableIndex?: number;
onAddPoint: (index: number) => void;
onAddLine?: (index: number) => void;
onDeletePoint: (index: number) => void;
onChangePoint: (
lineIndex: number,
pointIndex: number,
coordIndex: number,
newValue: number
) => void;
};
let {
values,
nullable = false,
minDeletableIndex = 4,
onAddPoint,
onAddLine,
onDeletePoint,
onChangePoint
}: Props = $props();
</script>
<Layout.Stack gap="s">
{#each values as value, index}
<Layout.Stack gap="xs">
<InputLine
values={value}
onAddPoint={() => onAddPoint(index)}
{nullable}
onDeletePoint={() => onDeletePoint(index)}
onChangePoint={(pointIndex, coordIndex, newValue) =>
onChangePoint(index, pointIndex, coordIndex, newValue)}
allowLineDelete={index < 2}
{minDeletableIndex}>
{#snippet addLineButton()}
{#if index === values.length - 1}
<Button
disabled={nullable}
size="xs"
compact
on:click={() => onAddLine?.(-1)}>
<Icon icon={IconPlus} size="s" /> Add line
</Button>
{/if}
{/snippet}
</InputLine>
</Layout.Stack>
{/each}
</Layout.Stack>
+53 -6
View File
@@ -3,6 +3,7 @@ import type { Columns } from '$routes/(console)/project-[region]-[project]/datab
import { ID, type Models } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { isWithinSafeRange } from '$lib/helpers/numbers';
import type { NestedNumberArray } from './types';
export async function generateColumns(
project: Models.Project,
@@ -42,7 +43,24 @@ export async function generateColumns(
size: 1000,
required: false
}),
client.tablesDB.createBooleanColumn({ databaseId, tableId, key: 'active', required: false })
client.tablesDB.createBooleanColumn({
databaseId,
tableId,
key: 'active',
required: false
}),
client.tablesDB.createPointColumn({
databaseId,
tableId,
key: 'location',
required: false
}),
client.tablesDB.createLineColumn({
databaseId,
tableId,
key: 'route',
required: false
})
]);
}
@@ -66,7 +84,10 @@ export function generateFakeRecords(
const id = ID.unique();
ids.push(id);
let row: Record<string, string | number | boolean | Array<string | number | boolean>> = {
let row: Record<
string,
string | number | boolean | Array<string | number | boolean | NestedNumberArray>
> = {
$id: id
};
@@ -78,7 +99,12 @@ export function generateFakeRecords(
age: faker.number.int({ min: 18, max: 80 }),
city: faker.location.city(),
description: faker.lorem.sentence(),
active: faker.datatype.boolean()
active: faker.datatype.boolean(),
location: [faker.location.longitude(), faker.location.latitude()],
route: Array.from({ length: 5 }, () => [
faker.location.longitude(),
faker.location.latitude()
])
};
} else {
for (const column of filteredColumns) {
@@ -116,10 +142,10 @@ function generateStringValue(key: string, maxLength: number): string {
function generateValueForColumn(
column: Columns
): string | number | boolean | null | Array<string | number | boolean> {
): string | number | boolean | null | Array<string | number | boolean | NestedNumberArray> {
if (column.array) {
const arraySize = faker.number.int({ min: 1, max: 5 });
const items: Array<string | number | boolean> = [];
const items: Array<string | number | NestedNumberArray | boolean> = [];
for (let i = 0; i < arraySize; i++) {
const itemAttribute = { ...column, array: false };
@@ -135,7 +161,9 @@ function generateValueForColumn(
return generateSingleValue(column);
}
function generateSingleValue(column: Columns): string | number | boolean | null {
function generateSingleValue(
column: Columns
): string | number | boolean | NestedNumberArray | null {
switch (column.type) {
case 'string': {
if ('format' in column && column.format) {
@@ -192,6 +220,25 @@ function generateSingleValue(column: Columns): string | number | boolean | null
return faker.date.recent({ days: 365 }).toISOString();
}
case 'point': {
return [faker.location.longitude(), faker.location.latitude()];
}
case 'linestring': {
return Array.from({ length: 5 }, () => [
faker.location.longitude(),
faker.location.latitude()
]);
}
case 'polygon': {
const points = Array.from({ length: 5 }, () => [
faker.location.longitude(),
faker.location.latitude()
]);
points.push(points[0]); // close polygon
return [points];
}
default: {
return faker.lorem.word();
}
+6 -1
View File
@@ -35,7 +35,10 @@ const columnTypes = [
'boolean',
'datetime',
'relationship',
'enum'
'enum',
'point',
'linestring',
'polygon'
] as const;
export type ColumnType = (typeof columnTypes)[number];
export type Column = PinkColumn & {
@@ -77,3 +80,5 @@ export type TableRootProp = {
addAvailableId: (id: string) => void;
removeAvailableId: (id: string) => void;
};
export type NestedNumberArray = Array<number | NestedNumberArray>;
+37
View File
@@ -1,3 +1,17 @@
const LINKEDIN_TRACKING_ID = 'li_fat_id';
const GOOGLE_TRACKING_ID = 'gclid';
const TWITTER_TRACKING_ID = 'twclid';
const REDDIT_TRACKING_ID = 'rdt_cid';
const TRACKED_QUERY_PARAMS = [
LINKEDIN_TRACKING_ID,
GOOGLE_TRACKING_ID,
TWITTER_TRACKING_ID,
REDDIT_TRACKING_ID
] as const;
type TrackedParams = Record<(typeof TRACKED_QUERY_PARAMS)[number], string>;
export function getReferrerAndUtmSource() {
if (sessionStorage) {
let values = {};
@@ -15,3 +29,26 @@ export function getReferrerAndUtmSource() {
}
return {};
}
export function getTrackedQueryParams(): Partial<TrackedParams> {
if (typeof window === 'undefined' || !window.location) {
return {};
}
try {
const urlParams = new URLSearchParams(window.location.search);
const params: Partial<TrackedParams> = {};
for (const paramName of TRACKED_QUERY_PARAMS) {
const value = urlParams.get(paramName);
if (value) {
params[paramName] = value;
}
}
return params;
} catch (error) {
console.warn('Failed to parse URL search parameters:', error);
return {};
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+1 -1
View File
@@ -205,7 +205,7 @@
<Navbar {...navbarProps} bind:sideBarIsOpen={$isSidebarOpen} bind:showAccountMenu />
{/if}
{#if !$isNewWizardStatusOpen}
{#if !$isNewWizardStatusOpen && isProjectPage}
<Sidebar
project={selectedProject}
progressCard={getProgressCard()}
+1 -1
View File
@@ -109,7 +109,7 @@
<Layout.Stack gap="s" direction="row">
{#if currentReview?.image}
<Avatar
src={getCampaignImageUrl(currentReview?.image)}
src={currentReview.image}
alt={currentReview.name}
size="m" />
{:else}
+1 -1
View File
@@ -620,7 +620,7 @@ export const billingURL = derived(
($page) => `${base}/organization-${$page.data?.organization?.$id}/billing`
);
export const hideBillingHeaderRoutes = ['/console/create-organization', '/console/account'];
export const hideBillingHeaderRoutes = [base + '/create-organization', base + '/account'];
export function calculateExcess(addon: AggregationTeam, plan: Plan) {
return {
+6 -6
View File
@@ -82,7 +82,7 @@
keys: ['g', 'p'],
group: 'navigation',
disabled:
(page.url.pathname.includes('/console/organization-') &&
(page.url.pathname.includes(base + '/organization-') &&
!page.url.pathname.endsWith('/members') &&
!page.url.pathname.endsWith('/settings')) ||
!$canSeeProjects,
@@ -335,11 +335,11 @@
<Shell
showSideNavigation={page.url.pathname !== '/' &&
!page?.params.organization &&
!page.url.pathname.includes('/console/account') &&
!page.url.pathname.includes('/console/card') &&
!page.url.pathname.includes('/console/onboarding')}
showHeader={!page.url.pathname.includes('/console/onboarding/create-project')}
showFooter={!page.url.pathname.includes('/console/onboarding/create-project')}
!page.url.pathname.includes(base + '/account') &&
!page.url.pathname.includes(base + '/card') &&
!page.url.pathname.includes(base + '/onboarding')}
showHeader={!page.url.pathname.includes(base + '/onboarding/create-project')}
showFooter={!page.url.pathname.includes(base + '/onboarding/create-project')}
selectedProject={page.data?.project}>
<!-- <Header slot="header" />-->
<slot />
@@ -174,7 +174,7 @@
'',
clientSecret,
paymentMethodId,
`/console/apply-credit?${params}`
`${base}/apply-credit?${params}`
);
org = await sdk.forConsole.billing.validateOrganization(org.teamId, collaborators);
}
+10 -11
View File
@@ -1,8 +1,8 @@
import { isCloud } from '$lib/system';
import { isSameDay } from '$lib/helpers/date';
import { type BottomModalAlertItem, showBottomModalAlert } from '$lib/stores/bottom-alerts';
import TablesApiLight from '$lib/images/promos/tables-api-light.png';
import TablesApiDark from '$lib/images/promos/tables-api-dark.png';
import SpatialColumnsLight from '$lib/images/promos/spatial-columns-api-light.png';
import SpatialColumnsDark from '$lib/images/promos/spatial-columns-api-dark.png';
import InversionQueriesDark from '$lib/images/promos/inversion-queries-dark.png';
import InversionQueriesLight from '$lib/images/promos/inversion-queries-light.png';
import TimeHelperQueriesDark from '$lib/images/promos/time-helper-queries-dark.png';
@@ -11,21 +11,20 @@ import TimeHelperQueriesLight from '$lib/images/promos/time-helper-queries-light
const listOfPromotions: BottomModalAlertItem[] = [];
if (isCloud) {
const tablesApiPromo: BottomModalAlertItem = {
id: 'modal:tables_api_announcement',
const spatialColumnsPromo: BottomModalAlertItem = {
id: 'modal:spatial_columns_announcement',
src: {
dark: TablesApiDark,
light: TablesApiLight
dark: SpatialColumnsDark,
light: SpatialColumnsLight
},
title: 'Announcing the new TablesDB UI and more!',
message:
'This is Appwrite Databases’ most significant update so far, introducing new terminology, a new UI, and a supporting TablesDB API.',
title: 'Announcing API for spatial columns',
message: 'Store and query geo data directly in your database.',
plan: 'free',
importance: 8,
scope: 'project',
cta: {
text: 'Read announcement',
link: () => 'https://apwr.dev/E64A90f',
link: () => 'https://appwrite.io/blog/post/announcing-spatial-columns',
external: true,
hideOnClick: true
},
@@ -71,7 +70,7 @@ if (isCloud) {
},
show: true
};
listOfPromotions.push(inversionQueriesPromo, timeHelperQueriesPromo, tablesApiPromo);
listOfPromotions.push(spatialColumnsPromo, inversionQueriesPromo, timeHelperQueriesPromo);
}
export function addBottomModalAlerts() {
@@ -143,7 +143,7 @@
'',
clientSecret,
paymentMethodId,
`/console/create-organization?${params}`
`${base}/create-organization?${params}`
);
await validate(org.teamId, collaborators);
}
@@ -82,13 +82,13 @@
<Loading {startAnimation} />
{:else}
<img
src="/console/images/appwrite-logo-light.svg"
src="{base}/images/appwrite-logo-light.svg"
width="120"
height="22"
class="u-only-light"
alt="Appwrite Logo" />
<img
src="/console/images/appwrite-logo-dark.svg"
src="{base}/images/appwrite-logo-dark.svg"
width="120"
height="22"
class="u-only-dark"
@@ -4,11 +4,12 @@ import { getLimit, getPage, getSearch, pageToOffset } from '$lib/helpers/load';
import { CARD_LIMIT, Dependencies } from '$lib/constants';
import type { PageLoad } from './$types';
import { redirect } from '@sveltejs/kit';
import { base } from '$app/paths';
export const load: PageLoad = async ({ params, url, route, depends, parent }) => {
const { scopes } = await parent();
if (!scopes.includes('projects.read') && scopes.includes('billing.read')) {
return redirect(301, `/console/organization-${params.organization}/billing`);
return redirect(301, `${base}/organization-${params.organization}/billing`);
}
depends(Dependencies.ORGANIZATION);
@@ -5,12 +5,13 @@ import { sdk } from '$lib/stores/sdk';
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
import { isCloud } from '$lib/system';
import { base } from '$app/paths';
export const load: PageLoad = async ({ parent, depends }) => {
const { organization, scopes, currentPlan, countryList, locale } = await parent();
if (!scopes.includes('billing.read')) {
return redirect(301, `/console/organization-${organization.$id}`);
return redirect(301, `${base}/organization-${organization.$id}`);
}
depends(Dependencies.PAYMENT_METHODS);
@@ -146,7 +146,7 @@
function getBillingData(currentPlan, currentAggregation, isSmallViewport) {
const projectsList = getProjectsList(currentAggregation);
const base = {
const basePlan = {
id: 'base-plan',
expandable: false,
cells: {
@@ -303,7 +303,7 @@
{
id: `usage-details`,
cells: {
item: `<a href="/console/project-${String(project.region || 'default')}-${project.projectId}/settings/usage" style="text-decoration: underline; color: var(--fgcolor-accent-neutral);">Usage details</a>`,
item: `<a href="${base}/project-${String(project.region || 'default')}-${project.projectId}/settings/usage" style="text-decoration: underline; color: var(--fgcolor-accent-neutral);">Usage details</a>`,
usage: '',
price: ''
}
@@ -311,7 +311,7 @@
]
}));
const noProjects = [];
return [base, ...addons, ...projects, ...noProjects];
return [basePlan, ...addons, ...projects, ...noProjects];
}
$: billingData = getBillingData(currentPlan, currentAggregation, $isSmallViewport);
@@ -277,7 +277,7 @@
'',
clientSecret,
paymentMethodId,
'/console/change-plan?' + params.toString()
base + '/change-plan?' + params.toString()
);
await validate(org.teamId, collaborators);
}
@@ -1,5 +1,6 @@
<script lang="ts">
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/state';
import { CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
@@ -20,7 +21,7 @@
type: 'success',
message: 'Domain moved successfully'
});
await goto(`/console/organization-${selectedOrg}/domains/`);
await goto(`${base}/organization-${selectedOrg}/domains/`);
await invalidate(Dependencies.ORGANIZATION);
await invalidate(Dependencies.DOMAINS);
} catch (e) {
@@ -15,7 +15,7 @@
Tooltip,
Typography
} from '@appwrite.io/pink-svelte';
import { isRelationship, isString } from '../rows/store';
import { isRelationship, isSpatialType, isString } from '../rows/store';
import FailedModal from '../failedModal.svelte';
import {
columns,
@@ -329,7 +329,7 @@
{#if _default === null}
<Badge variant="secondary" content="NULL" size="xs" />
{:else}
{_default}
{isSpatialType(column) ? JSON.stringify(_default) : _default}
{/if}
</Spreadsheet.Cell>
<Spreadsheet.Cell column="actions" {root} isEditable={false}>
@@ -0,0 +1,147 @@
<script module lang="ts">
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import type { Models } from '@appwrite.io/console';
export async function submitLine(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnLine>
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.createLineColumn({
databaseId,
tableId,
key,
required: data.required,
xdefault: data?.default
});
}
export async function updateLine(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnLine>,
originalKey?: string
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.updateLineColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data?.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import { Selector, Layout, Typography } from '@appwrite.io/pink-svelte';
import { InputLine } from '$lib/elements/forms';
import { getDefaultSpatialData } from '../store';
import { onMount } from 'svelte';
interface Props {
data?: Partial<Models.ColumnLine>;
editing?: boolean;
}
let { data = { required: false, default: null }, editing = false }: Props = $props();
let savedDefault = $state(data.default);
let defaultChecked = $state(!!data.default);
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault ?? getDefaultSpatialData('linestring');
}
}
function pushCoordinate() {
if (data.default) {
data.default.push(getDefaultSpatialData('point') as number[]);
data.default = [...data.default];
}
}
function deleteCoordinate(_: number) {
if (data.default && data.default.length > 2) {
data.default.pop();
data.default = [...data.default];
}
}
const {
stores: { required },
listen
} = createConservative<Partial<Models.ColumnLine>>({
required: false,
...data
});
$effect(() => {
listen(data);
});
$effect(() => {
data.required = $required;
if ($required) {
handleDefaultState(true);
}
});
onMount(() => {
if (!editing) {
savedDefault = getDefaultSpatialData('linestring');
defaultChecked = false;
$required = false;
}
});
</script>
<Selector.Checkbox
size="s"
id="required"
label="Required"
bind:checked={$required}
on:change={(e) => {
if (e.detail) defaultChecked = false;
}}
description="Indicate whether this column is required" />
<Selector.Checkbox
size="s"
id="default"
label="Default value"
bind:checked={defaultChecked}
on:change={(e) => {
if (e.detail) {
$required = false;
handleDefaultState(false);
} else {
data.default = null;
}
}}
description="Enable to set a predefined value for this column" />
<Layout.Stack>
{#if defaultChecked}
<Layout.Stack direction="row" alignItems="center">
<Typography.Text variant="m-600">Default</Typography.Text>
<Typography.Caption variant="400">Optional</Typography.Caption>
</Layout.Stack>
{/if}
<InputLine
values={defaultChecked ? data.default : null}
onAddPoint={() => pushCoordinate()}
onDeletePoint={deleteCoordinate}
onChangePoint={(pointIndex: number, coordIndex: number, newValue: number) => {
if (data.default) {
data.default[pointIndex][coordIndex] = newValue;
data.default = [...data.default];
}
}} />
</Layout.Stack>
@@ -0,0 +1,136 @@
<script module lang="ts">
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import type { Models } from '@appwrite.io/console';
export async function submitPoint(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnPoint>
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.createPointColumn({
databaseId,
tableId,
key,
required: data.required,
xdefault: data?.default
});
}
export async function updatePoint(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnPoint>,
originalKey?: string
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.updatePointColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data?.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { InputPoint } from '$lib/elements/forms';
import { createConservative } from '$lib/helpers/stores';
import { Selector, Typography, Layout } from '@appwrite.io/pink-svelte';
import { getDefaultSpatialData } from '../store';
import { onMount } from 'svelte';
interface Props {
data?: Partial<Models.ColumnPoint>;
editing?: boolean;
}
let { data = { required: false, default: null }, editing }: Props = $props();
let savedDefault = $state(data.default);
let defaultChecked = $state(!!data.default);
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault ?? getDefaultSpatialData('point');
}
}
const {
stores: { required },
listen
} = createConservative<Partial<Models.ColumnPoint>>({
required: false,
...data
});
$effect(() => {
listen(data);
});
$effect(() => {
data.required = $required;
if ($required) {
handleDefaultState(true);
}
});
$effect(() => {
defaultChecked = data.default !== null;
});
onMount(() => {
if (!editing) {
savedDefault = getDefaultSpatialData('point');
defaultChecked = false;
$required = false;
}
});
</script>
<Selector.Checkbox
size="s"
id="required"
label="Required"
bind:checked={$required}
on:change={(e) => {
if (e.detail) defaultChecked = false;
}}
description="Indicate whether this column is required" />
<Selector.Checkbox
size="s"
id="default"
label="Default value"
bind:checked={defaultChecked}
on:change={(e) => {
if (e.detail) {
$required = false;
handleDefaultState(false);
} else {
data.default = null;
}
}}
description="Enable to set a predefined value for this column" />
<Layout.Stack gap="xl">
{#if defaultChecked}
<Layout.Stack direction="row" alignItems="center">
<Typography.Text variant="m-600">Default</Typography.Text>
<Typography.Caption variant="400">Optional</Typography.Caption>
</Layout.Stack>
{/if}
<InputPoint
values={defaultChecked ? data.default : null}
onChangePoint={(index, newValue) => {
if (data.default) {
data.default[index] = newValue;
data.default = [...data.default];
}
}} />
</Layout.Stack>
@@ -0,0 +1,161 @@
<script module lang="ts">
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import type { Models } from '@appwrite.io/console';
export async function submitPolygon(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnPolygon>
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.createPolygonColumn({
databaseId,
tableId,
key,
required: data.required,
xdefault: data?.default
});
}
export async function updatePolygon(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnPolygon>,
originalKey?: string
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.updatePolygonColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data?.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import { Selector, Typography, Layout } from '@appwrite.io/pink-svelte';
import { InputPolygon } from '$lib/elements/forms';
import { getDefaultSpatialData, getSingleRingPolygon } from '../store';
import { onMount } from 'svelte';
interface Props {
data?: Partial<Models.ColumnPolygon>;
editing?: boolean;
}
let { data = { required: false, default: null }, editing = false }: Props = $props();
let savedDefault = $state(data.default);
let defaultChecked = $state(!!data.default);
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault ?? getDefaultSpatialData('polygon');
}
}
const {
stores: { required },
listen
} = createConservative<Partial<Models.ColumnPolygon>>({
required: false,
...data
});
function pushCoordinate(ringIndex: number) {
const ring = data.default?.at(ringIndex);
if (!ring) return;
const newPoint = getDefaultSpatialData('point') as number[];
ring.splice(ring.length - 1, 0, newPoint);
ring[ring.length - 1] = [...ring[0]];
data.default = [...(data.default || [])];
}
function pushLine() {
data.default = [...(data.default || []), getSingleRingPolygon()];
}
function deleteCoordinate(ringIndex: number) {
const ring = data.default?.at(ringIndex);
ring.splice(ring.length - 1, 1);
if (ring.length === 0) {
data.default.splice(ringIndex, 1);
}
data.default = [...(data.default || [])];
}
$effect(() => {
listen(data);
});
$effect(() => {
data.required = $required;
if ($required) {
handleDefaultState(true);
}
});
onMount(() => {
if (!editing) {
savedDefault = getDefaultSpatialData('polygon');
defaultChecked = false;
$required = false;
}
});
</script>
<Selector.Checkbox
size="s"
id="required"
label="Required"
bind:checked={$required}
on:change={(e) => {
if (e.detail) defaultChecked = false;
else data.default = null;
}}
description="Indicate whether this column is required" />
<Selector.Checkbox
size="s"
id="default"
label="Default value"
bind:checked={defaultChecked}
on:change={(e) => {
if (e.detail) {
$required = false;
handleDefaultState(false);
} else {
data.default = null;
}
}}
description="Enable to set a predefined value for this column" />
<Layout.Stack gap="xl">
{#if defaultChecked}
<Layout.Stack direction="row" alignItems="center">
<Typography.Text variant="m-600">Default</Typography.Text>
<Typography.Caption variant="400">Optional</Typography.Caption>
</Layout.Stack>
{/if}
<InputPolygon
values={defaultChecked ? data.default : null}
onAddLine={pushLine}
onAddPoint={pushCoordinate}
onDeletePoint={deleteCoordinate}
onChangePoint={(lineIndex, pointIndex, coordIndex, newValue) => {
if (data.default && data.default[lineIndex] && data.default[lineIndex][pointIndex]) {
data.default[lineIndex][pointIndex][coordIndex] = newValue;
data.default = [...data.default];
}
}} />
</Layout.Stack>
@@ -138,7 +138,7 @@
$: search = data.relatedTable || undefined;
$: if (search) {
$: if (search && !editing) {
const exists = tableList?.tables?.some((c) =>
c.$id.toLocaleLowerCase().includes(search.toLocaleLowerCase())
);
@@ -191,6 +191,7 @@
bind:value={data.key}
helper="Allowed characters: a-z, A-Z, 0-9, -, ."
required />
{#if way === 'two'}
<InputText
id="keyRelated"
@@ -9,6 +9,9 @@ import Ip, { submitIp, updateIp } from './ip.svelte';
import String, { submitString, updateString } from './string.svelte';
import Url, { submitUrl, updateUrl } from './url.svelte';
import Datetime, { submitDatetime, updateDatetime } from './datetime.svelte';
import Point, { submitPoint, updatePoint } from './point.svelte';
import Line, { submitLine, updateLine } from './line.svelte';
import Polygon, { submitPolygon, updatePolygon } from './polygon.svelte';
import type { Columns } from '../store';
import Relationship, { submitRelationship, updateRelationship } from './relationship.svelte';
import {
@@ -20,7 +23,10 @@ import {
IconMail,
IconRelationship,
IconText,
IconToggle
IconToggle,
IconPoint,
IconLine,
IconPolygon
} from '@appwrite.io/pink-icons-svelte';
import type { ComponentType } from 'svelte';
@@ -35,10 +41,22 @@ export type Option = {
| 'IP'
| 'URL'
| 'Enum'
| 'Relationship';
| 'Relationship'
| 'Point'
| 'Line'
| 'Polygon';
sentenceName: string;
component: Component;
type: 'string' | 'integer' | 'double' | 'boolean' | 'datetime' | 'relationship';
type:
| 'string'
| 'integer'
| 'double'
| 'boolean'
| 'datetime'
| 'relationship'
| 'point'
| 'linestring'
| 'polygon';
create: (
databaseId: string,
tableId: string,
@@ -101,6 +119,33 @@ export const columnOptions: Option[] = [
update: updateDatetime,
icon: IconCalendar
},
{
name: 'Point',
sentenceName: 'point',
component: Point,
type: 'point',
create: submitPoint,
update: updatePoint,
icon: IconPoint
},
{
name: 'Line',
sentenceName: 'linestring',
component: Line,
type: 'linestring',
create: submitLine,
update: updateLine,
icon: IconLine
},
{
name: 'Polygon',
sentenceName: 'polygon',
component: Polygon,
type: 'polygon',
create: submitPolygon,
update: updatePolygon,
icon: IconPolygon
},
{
name: 'Email',
sentenceName: 'email',
@@ -9,7 +9,7 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { IndexType } from '@appwrite.io/console';
import { isRelationship } from '../rows/store';
import { isRelationship, isSpatialType } from '../rows/store';
import { table, indexes } from '../store';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
@@ -26,23 +26,51 @@
const databaseId = page.params.database;
let key = $state('');
let types = [
{ value: IndexType.Key, label: 'Key' },
{ value: IndexType.Unique, label: 'Unique' },
{ value: IndexType.Fulltext, label: 'Fulltext' }
];
let selectedType = $state(IndexType.Key);
let selectedType = $state<IndexType>(IndexType.Key);
let columnOptions = $table.columns
.filter((column) => !isRelationship(column))
.map((column) => ({
value: column.key,
label: column.key
}));
let columnOptions = $derived(
$table.columns
.filter((column) => {
if (selectedType === IndexType.Spatial) {
return isSpatialType(column); // keep only spatial
}
return !isRelationship(column) && !isSpatialType(column); // keep non-relationship and non-spatial
})
.map((column) => ({ value: column.key, label: column.key }))
);
let columnList = $state([{ value: '', order: '', length: null }]);
const types = [
{ value: IndexType.Key, label: 'Key' },
{ value: IndexType.Unique, label: 'Unique' },
{ value: IndexType.Fulltext, label: 'Fulltext' },
{ value: IndexType.Spatial, label: 'Spatial' }
];
// order options derived from selected type
let orderOptions = $derived.by(() =>
selectedType === IndexType.Spatial
? [
{ value: 'ASC', label: 'ASC' },
{ value: 'DESC', label: 'DESC' },
{ value: null, label: 'NONE' }
]
: [
{ value: 'ASC', label: 'ASC' },
{ value: 'DESC', label: 'DESC' }
]
);
// spatial type selected -> reset column list to single empty column
// and the column already is not spatial type
$effect(() => {
if (selectedType === IndexType.Spatial && !columnList.at(0).value) {
columnList = [{ value: '', order: null, length: null }];
}
});
function generateIndexKey() {
let indexKeys = $indexes.map((index) => index.key);
@@ -55,29 +83,41 @@
}
function initialize() {
const column = $table.columns.filter((column) => externalColumnKey === column.key);
const isSpatial = column.length && isSpatialType(column[0]);
const order = isSpatial ? null : 'ASC';
selectedType = isSpatial ? IndexType.Spatial : IndexType.Key;
columnList = externalColumnKey
? [{ value: externalColumnKey, order: 'ASC', length: null }]
: [{ value: '', order: 'ASC', length: null }];
selectedType = IndexType.Key;
? [{ value: externalColumnKey, order, length: null }]
: [{ value: '', order, length: null }];
key = `index_${$indexes.length + 1}`;
}
const addColumnDisabled = $derived(!columnList.at(-1)?.value || !columnList.at(-1)?.order);
const addColumnDisabled = $derived(
selectedType === IndexType.Spatial ||
!columnList.at(-1)?.value ||
(!columnList.at(-1)?.order && columnList.at(-1)?.order !== null)
);
const isOnIndexesPage = $derived(page.route.id?.endsWith('/indexes'));
const navigatorPathToIndexes = $derived(
`${base}/project-${page.params.region}-${page.params.project}/databases/database-${databaseId}/table-${$table?.$id}/indexes`
);
let initializedForOpen = $state(false);
$effect(() => {
if (showCreateIndex) {
if (showCreateIndex && !initializedForOpen) {
initialize();
key = generateIndexKey();
initializedForOpen = true;
}
if (!showCreateIndex && initializedForOpen) {
initializedForOpen = false;
}
});
export async function create() {
if (!(key && selectedType && !addColumnDisabled)) {
if (!key || !selectedType || (selectedType !== IndexType.Spatial && addColumnDisabled)) {
addNotification({
type: 'error',
message: 'Selected column key or type invalid'
@@ -86,14 +126,15 @@
}
try {
const orders = columnList.map((a) => a.order).filter((order) => order !== null);
await sdk.forProject(page.params.region, page.params.project).tablesDB.createIndex({
databaseId,
tableId: $table.$id,
key,
type: selectedType,
columns: columnList.map((a) => a.value),
orders: columnList.map((a) => a.order),
lengths: columnList.map((a) => (a.length ? Number(a.length) : null))
lengths: columnList.map((a) => (a.length ? Number(a.length) : null)),
...(orders.length ? { orders } : {})
});
await Promise.allSettled([
@@ -151,9 +192,14 @@
<InputSelect
required
options={[
{ value: '$id', label: '$id' },
{ value: '$createdAt', label: '$createdAt' },
{ value: '$updatedAt', label: '$updatedAt' },
// allow system fields only for non-spatial index types
...(selectedType === IndexType.Spatial
? []
: [
{ value: '$id', label: '$id' },
{ value: '$createdAt', label: '$createdAt' },
{ value: '$updatedAt', label: '$updatedAt' }
]),
...columnOptions
]}
id={`column-${index}`}
@@ -162,10 +208,7 @@
bind:value={column.value} />
<InputSelect
options={[
{ value: 'ASC', label: 'ASC' },
{ value: 'DESC', label: 'DESC' }
]}
options={orderOptions}
required
id={`order-${index}`}
label={index === 0 ? 'Order' : undefined}
@@ -7,6 +7,10 @@
import Relationship from './types/relationship.svelte';
import String from './types/string.svelte';
import Url from './types/url.svelte';
import Point from './types/point.svelte';
import Line from './types/line.svelte';
import Polygon from './types/polygon.svelte';
import { isSpatialType } from '../store';
export let id: string;
export let label: string;
@@ -24,7 +28,10 @@
| Models.ColumnIp
| Models.ColumnString
| Models.ColumnDatetime
| Models.ColumnUrl;
| Models.ColumnUrl
| Models.ColumnPoint
| Models.ColumnLine
| Models.ColumnPolygon;
const columnsTypeMap = {
string: String,
@@ -32,7 +39,10 @@
double: Integer,
boolean: Boolean,
datetime: Datetime,
relationship: Relationship
relationship: Relationship,
point: Point,
linestring: Line,
polygon: Polygon
};
const columnsFormatMap = {
@@ -59,7 +69,9 @@
{:else}
<!-- the `on:click` is from string > array mode for advanced edit button -->
<svelte:component
this={column.array ? columnsTypeMap['string'] : columnsTypeMap[column.type]}
this={column.array || (isSpatialType(column) && limited)
? columnsTypeMap['string']
: columnsTypeMap[column.type]}
{id}
{editing}
{limited}
@@ -0,0 +1,67 @@
<script lang="ts">
import { InputLine } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { Layout, Typography, Icon } from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { getDefaultSpatialData } from '../../../store';
interface Props {
label: string;
value: number[][];
limited: boolean;
column: Models.ColumnLine;
}
let { label, value = $bindable(), limited = false, column }: Props = $props();
const defaultData = getDefaultSpatialData('linestring') as number[][];
function onAddPoint() {
value = [...(value || defaultData), getDefaultSpatialData('point') as number[]];
}
function onDeletePoint() {
if (value && value?.length > 2) value = value.slice(0, value.length - 1);
}
function handlePointChange(pointIndex: number, coordIndex: number, newValue: number) {
if (value && value[pointIndex]) {
value[pointIndex][coordIndex] = newValue;
value = [...value];
}
}
function handleAddDefault() {
value = getDefaultSpatialData('linestring') as number[][];
}
const nullable = $derived(!limited ? !column.required : false);
</script>
<Layout.Stack>
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Typography.Text variant="m-500">{label}</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
{#if nullable}
optional
{:else}
Line
{/if}
</Typography.Text>
</Layout.Stack>
{#if !value}
<Button secondary on:click={handleAddDefault}>
<Icon icon={IconPlus} slot="start" size="s" />
Add Line
</Button>
{/if}
</Layout.Stack>
<InputLine
values={value}
nullable={false}
{onAddPoint}
{onDeletePoint}
onChangePoint={handlePointChange}
minDeletableIndex={2} />
</Layout.Stack>
@@ -0,0 +1,53 @@
<script lang="ts">
import { InputPoint } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Button } from '$lib/elements/forms';
import { getDefaultSpatialData } from '../../../store';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
interface Props {
label: string;
value: number[];
limited: boolean;
column: Models.ColumnPoint;
}
let { label, value = $bindable(), limited = false, column }: Props = $props();
function handlePointChange(index: number, newValue: number) {
if (value) {
value[index] = newValue;
value = [...value];
}
}
function handleAddDefault() {
value = getDefaultSpatialData('point') as number[];
}
const nullable = $derived(!limited ? !column.required : false);
</script>
<Layout.Stack>
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Typography.Text variant="m-500">{label}</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
{#if nullable}
optional
{:else}
Point
{/if}
</Typography.Text>
</Layout.Stack>
{#if !value}
<Button secondary on:click={handleAddDefault}>
<Icon icon={IconPlus} slot="start" size="s" />
Add Point
</Button>
{/if}
</Layout.Stack>
<InputPoint values={value} nullable={false} onChangePoint={handlePointChange} />
</Layout.Stack>
@@ -0,0 +1,89 @@
<script lang="ts">
import type { Models } from '@appwrite.io/console';
import { Button } from '$lib/elements/forms';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Layout, Typography, Icon } from '@appwrite.io/pink-svelte';
import { getDefaultSpatialData, getSingleRingPolygon } from '../../../store';
import InputPolygon from '$lib/elements/forms/inputPolygon.svelte';
interface Props {
label: string;
value: number[][][];
limited: boolean;
column: Models.ColumnPolygon;
}
let { label, value = $bindable(), limited = false, column }: Props = $props();
function pushCoordinate(ringIndex: number) {
const ring = value[ringIndex];
if (!ring) return;
const newPoint = getDefaultSpatialData('point') as number[];
const newRing = [...ring];
newRing.splice(newRing.length - 1, 0, newPoint);
newRing[newRing.length - 1] = [...newRing[0]];
value = value.map((r, i) => (i === ringIndex ? newRing : r));
}
function pushLine() {
if (!value) return;
value = [...value, getSingleRingPolygon()];
}
function deleteCoordinate(ringIndex: number) {
if (!value) return;
value = value
.map((ring, i) => (i === ringIndex ? ring.slice(0, -1) : ring))
.filter((ring) => ring.length > 0);
}
function handlePointChange(
lineIndex: number,
pointIndex: number,
coordIndex: number,
newValue: number
) {
if (value && value[lineIndex] && value[lineIndex][pointIndex]) {
value[lineIndex][pointIndex][coordIndex] = newValue;
value = [...value];
}
}
function handleAddDefault() {
value = getDefaultSpatialData('polygon') as number[][][];
}
const nullable = $derived(!limited ? !column.required : false);
</script>
<Layout.Stack>
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" alignItems="center" gap="s">
<Typography.Text variant="m-500">{label}</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
{#if nullable}
optional
{:else}
Polygon
{/if}
</Typography.Text>
</Layout.Stack>
{#if !value}
<Button secondary on:click={handleAddDefault}>
<Icon icon={IconPlus} slot="start" size="s" />
Add Polygon
</Button>
{/if}
</Layout.Stack>
<InputPolygon
values={value}
nullable={false}
onAddLine={pushLine}
onAddPoint={pushCoordinate}
onDeletePoint={deleteCoordinate}
onChangePoint={handlePointChange} />
</Layout.Stack>
@@ -3,6 +3,7 @@
import { Layout, Link } from '@appwrite.io/pink-svelte';
import { IconText } from '@appwrite.io/pink-icons-svelte';
import { InputText, InputTextarea } from '$lib/elements/forms';
import { isSpatialType } from '../../store';
let {
id,
@@ -21,7 +22,10 @@
| Models.ColumnString
| Models.ColumnInteger
| Models.ColumnFloat
| Models.ColumnBoolean;
| Models.ColumnBoolean
| Models.ColumnPoint
| Models.ColumnLine
| Models.ColumnPolygon;
} = $props();
const autofocus = $derived(limited);
@@ -40,7 +44,6 @@
function parseValue(str: string | null): number | boolean | string | null {
const trimmed = str?.trim() ?? null;
if (!trimmed) return null;
switch (column.type) {
@@ -60,7 +63,10 @@
if (lower === 'false' || lower === '0') return false;
return null;
}
case 'point':
case 'linestring':
case 'polygon':
return trimmed;
case 'string':
default:
return trimmed;
@@ -68,7 +74,9 @@
}
$effect(() => {
if (array && Array.isArray(value)) {
if (isSpatialType(column) && Array.isArray(value)) {
stringValue = JSON.stringify(value);
} else if (array && Array.isArray(value)) {
stringValue = value.map(String).join(', ');
} else if (value !== null && value !== undefined) {
stringValue = String(value);
@@ -83,13 +91,14 @@
.split(',')
.map((item) => parseValue(item))
.filter((item) => item !== null);
if (JSON.stringify(newArray) !== JSON.stringify(value)) {
value = newArray as string[] | number[] | boolean[];
}
} else {
const parsedValue = parseValue(stringValue);
if (parsedValue !== value) {
const parsedValue = isSpatialType(column)
? JSON.parse(stringValue)
: parseValue(stringValue);
if (JSON.stringify(parsedValue) !== JSON.stringify(value)) {
value = parsedValue;
}
}
@@ -124,7 +133,7 @@
};
</script>
{#if columnSize >= 50 || array}
{#if columnSize >= 50 || array || isSpatialType(column)}
<InputTextarea
{id}
{label}
@@ -136,7 +145,7 @@
placeholder={getPlaceholder()}
leadingIcon={!limited ? IconText : undefined}>
<Layout.Stack direction="column" alignItems="flex-start" slot="end">
{#if array}
{#if array || isSpatialType(column)}
<Link.Button on:click size="s" variant="quiet">Advanced edit</Link.Button>
{/if}
</Layout.Stack>
@@ -1,4 +1,5 @@
import { page } from '$app/state';
import type { Column } from '$lib/helpers/types';
import type { Columns } from '../store';
import { type Models, Query } from '@appwrite.io/console';
@@ -26,6 +27,16 @@ export function isString(column: Columns): column is Models.ColumnString {
return column?.type === 'string';
}
export function isSpatialType(
column: Columns | Column
): column is Models.ColumnPoint | Models.ColumnLine | Models.ColumnPolygon {
if (!column) return false;
const spatialTypes = ['point', 'linestring', 'polygon'];
return spatialTypes.includes(column.type.toLowerCase());
}
/**
* Returns select queries for all main and related fields in a table.
*/
@@ -16,6 +16,7 @@
buildWildcardColumnsQuery,
isRelationship,
isRelationshipToMany,
isSpatialType,
isString
} from './rows/store';
import {
@@ -66,10 +67,13 @@
IconDotsHorizontal,
IconFingerPrint,
IconHashtag,
IconLine,
IconLink,
IconLocationMarker,
IconMail,
IconPoint,
IconPlus,
IconPolygon,
IconRelationship,
IconSwitchHorizontal,
IconText,
@@ -305,6 +309,12 @@
return IconViewList;
case 'relationship':
return IconRelationship;
case 'point':
return IconPoint;
case 'linestring':
return IconLine;
case 'polygon':
return IconPolygon;
}
}
@@ -909,6 +919,10 @@
variant="secondary"
size="s" />
{/if}
{:else if isSpatialType(rowColumn) && row[columnId] !== null}
<Typography.Text truncate>
{JSON.stringify(row[columnId])}
</Typography.Text>
{:else}
{@const value = row[columnId]}
{@const formatted = formatColumn(row[columnId])}
@@ -1,5 +1,5 @@
import { page } from '$app/stores';
import type { Column } from '$lib/helpers/types';
import type { Column, ColumnType } from '$lib/helpers/types';
import type { Models } from '@appwrite.io/console';
import { derived, writable } from 'svelte/store';
import type { SortDirection } from '$lib/components';
@@ -15,6 +15,9 @@ export type Columns =
| Models.ColumnIp
| Models.ColumnString
| Models.ColumnUrl
| Models.ColumnPoint
| Models.ColumnLine
| Models.ColumnPolygon
| (Models.ColumnRelationship & { default?: never });
type Table = Omit<Models.Table, 'columns'> & {
@@ -188,3 +191,32 @@ export const PROHIBITED_ROW_KEYS = [
];
export const sheetHeightStore = writable('74.5vh');
export const getDefaultSpatialData = (
type: Extract<ColumnType, 'point' | 'linestring' | 'polygon'>
) => {
if (type === 'point') return [0, 0];
else if (type === 'linestring')
return [
[0, 0],
[0, 0]
];
else if (type === 'polygon')
return [
[
[0, 0],
[0, 0],
[0, 0],
[0, 0]
]
];
};
export const getSingleRingPolygon = () => {
return [
[0, 0],
[0, 0],
[0, 0],
[0, 0]
];
};
@@ -7,13 +7,14 @@
import AppwriteLogoLight from '$lib/images/appwrite-logo-light.svg';
import GithubLogoDark from '$lib/images/github-logo-dark.svg';
import GithubLogoLight from '$lib/images/github-logo-light.svg';
import { base } from '$app/paths';
function onGithubLogin() {
localStorage.setItem('githubEducationProgram', 'true');
sdk.forConsole.account.createOAuth2Session({
provider: OAuthProvider.Github,
success: window.location.origin + '/console/education?success',
failure: window.location.origin + '/console/education?failure',
success: window.location.origin + base + '/education?success',
failure: window.location.origin + base + '/education?failure',
scopes: ['read:user', 'user:email']
});
}
@@ -20,12 +20,35 @@
import { redirectTo } from '$routes/store';
import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
import { Layout, Link, Typography } from '@appwrite.io/pink-svelte';
import { getRandomTestimonial } from '$lib/data/testimonials';
export let data;
let name: string, mail: string, pass: string, disabled: boolean;
let terms = false;
const randomTestimonial = getRandomTestimonial();
const testimonialCampaign = {
$id: 'testimonial-signup',
template: 'review',
title: randomTestimonial.headline,
description: 'Join thousands of developers building amazing apps with Appwrite',
reviews: [
{
name: randomTestimonial.name,
image: randomTestimonial.avatar,
description: randomTestimonial.title,
review: randomTestimonial.blurb
}
]
};
trackEvent(Submit.TestimonialView, {
testimonial_id: randomTestimonial.id,
testimonial_name: randomTestimonial.name,
testimonial_company: randomTestimonial.title
});
async function register() {
try {
disabled = true;
@@ -43,7 +66,9 @@
trackEvent(Submit.AccountCreate, {
campaign_name: data?.couponData?.code,
email: mail,
name: name
name: name,
testimonial_id: randomTestimonial.id,
testimonial_name: randomTestimonial.name
});
if (data?.couponData?.code) {
@@ -94,7 +119,7 @@
<title>Sign up - Appwrite</title>
</svelte:head>
<Unauthenticated coupon={data?.couponData} campaign={data?.campaign}>
<Unauthenticated coupon={data?.couponData} campaign={data?.campaign || testimonialCampaign}>
<svelte:fragment slot="title">Sign up</svelte:fragment>
<svelte:fragment>
<Form onSubmit={register}>
+3 -2
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import { base } from '$app/paths';
import { app } from '$lib/stores/app';
import { loading } from '$routes/store';
import { Typography } from '@appwrite.io/pink-svelte';
@@ -16,13 +17,13 @@
<Typography.Eyebrow color="--fgcolor-neutral-secondary">POWERED BY</Typography.Eyebrow>
{#if $app.themeInUse === 'dark'}
<img
src="/console/images/appwrite-logo-dark.svg"
src="{base}/images/appwrite-logo-dark.svg"
width="120"
height="22"
alt="Appwrite Logo" />
{:else}
<img
src="/console/images/appwrite-logo-light.svg"
src="{base}/images/appwrite-logo-light.svg"
width="120"
height="22"
alt="Appwrite Logo" />
@@ -1,4 +1,5 @@
<script lang="ts">
import { base } from '$app/paths';
import { app } from '$lib/stores/app';
let { children } = $props();
@@ -11,13 +12,13 @@
<footer>
{#if $app.themeInUse === 'dark'}
<img
src="/console/images/appwrite-logo-dark.svg"
src="{base}/images/appwrite-logo-dark.svg"
width="120"
height="22"
alt="Appwrite Logo" />
{:else}
<img
src="/console/images/appwrite-logo-light.svg"
src="{base}/images/appwrite-logo-light.svg"
width="120"
height="22"
alt="Appwrite Logo" />
@@ -5,13 +5,14 @@ import { isCloud } from '$lib/system.js';
import { error, redirect } from '@sveltejs/kit';
import type { OrganizationList } from '$lib/stores/organization.js';
import { redirectTo } from '$routes/store.js';
import { base } from '$app/paths';
export const load = async ({ parent, url, params }) => {
const { account } = await parent();
if (!account && !isCloud) {
redirectTo.set(url.pathname + url.search);
redirect(302, '/console/login?redirect=' + url.pathname + url.search);
redirect(302, base + '/login?redirect=' + url.pathname + url.search);
}
if (!url.searchParams.has('type')) {
+2 -2
View File
@@ -163,14 +163,14 @@
{#each preloadFonts as font}
<link rel="preload" href={font} as="font" type="font/woff2" crossorigin="anonymous" />
{/each}
<link rel="preload" as="style" type="text/css" href="/console/fonts/main.css" />
<link rel="preload" as="style" type="text/css" href="{base}/fonts/main.css" />
<link rel="stylesheet" href={`${base}/fonts/main.css`} />
{#if isCloud}
{#each preloadFontsCloud as font}
<link rel="preload" href={font} as="font" type="font/woff2" crossorigin="anonymous" />
{/each}
<link rel="preload" as="style" type="text/css" href="/console/fonts/cloud.css" />
<link rel="preload" as="style" type="text/css" href="{base}/fonts/cloud.css" />
<link rel="stylesheet" href={`${base}/fonts/cloud.css`} />
{/if}
</svelte:head>
-16
View File
@@ -1,16 +0,0 @@
{
"redirects": [
{
"source": "/",
"destination": "/console/",
"permanent": true
}
],
"rewrites": [
{
"source": "/console/(.*)",
"destination": "/$1"
}
],
"installCommand": "pnpm install --frozen-lockfile"
}