Merge branch 'main' into feat-improve-domain-flows
@@ -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'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 451 KiB |
|
After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -205,7 +205,7 @@
|
||||
<Navbar {...navbarProps} bind:sideBarIsOpen={$isSidebarOpen} bind:showAccountMenu />
|
||||
{/if}
|
||||
|
||||
{#if !$isNewWizardStatusOpen}
|
||||
{#if !$isNewWizardStatusOpen && isProjectPage}
|
||||
<Sidebar
|
||||
project={selectedProject}
|
||||
progressCard={getProgressCard()}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/",
|
||||
"destination": "/console/",
|
||||
"permanent": true
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/console/(.*)",
|
||||
"destination": "/$1"
|
||||
}
|
||||
],
|
||||
"installCommand": "pnpm install --frozen-lockfile"
|
||||
}
|
||||