Merge branch 'main' into feat-block-console-until-verify
@@ -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@f2198f1",
|
||||
"@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@f2198f1",
|
||||
"@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@f2198f1
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1(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@f2198f1
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f2198f1(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@f2198f1':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1}
|
||||
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@f2198f1':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f2198f1}
|
||||
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@f2198f1(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@f2198f1(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'
|
||||
}
|
||||
|
||||
@@ -21,13 +21,16 @@ export const timer: Action<HTMLElement, Props> = (node, props) => {
|
||||
}
|
||||
|
||||
function step() {
|
||||
const diffInSeconds = Math.floor((new Date().getTime() - startDate.getTime()) / 1000);
|
||||
const minutes = Math.floor(diffInSeconds / 60);
|
||||
const elapsedSeconds = Math.max(
|
||||
0,
|
||||
Math.floor((new Date().getTime() - startDate.getTime()) / 1000)
|
||||
);
|
||||
const minutes = Math.floor(elapsedSeconds / 60);
|
||||
if (minutes > 0) {
|
||||
const seconds = diffInSeconds % 60;
|
||||
const seconds = elapsedSeconds % 60;
|
||||
node.textContent = `${minutes}m ${seconds}s`;
|
||||
} else {
|
||||
node.textContent = calculateTime(diffInSeconds);
|
||||
node.textContent = calculateTime(elapsedSeconds);
|
||||
}
|
||||
frame = window.requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
|
||||
$: plans = Object.values(page.data.plans.plans) as Plan[];
|
||||
$: currentPlanInList = plans.some((plan) => plan.$id === $currentPlan?.$id);
|
||||
|
||||
// experiment to remove scale plan temporarily
|
||||
$: plansWithoutScale = plans.filter((plan) => plan.$id != BillingPlan.SCALE);
|
||||
</script>
|
||||
|
||||
<Layout.Stack>
|
||||
{#each plans as plan}
|
||||
{#each plansWithoutScale as plan}
|
||||
<LabelCard
|
||||
name="plan"
|
||||
bind:group={billingPlan}
|
||||
|
||||
@@ -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) {
|
||||
@@ -105,6 +128,8 @@
|
||||
async function createProjectsBottomSheet(
|
||||
organization: Organization
|
||||
): Promise<SheetMenu | null> {
|
||||
const isOnProjects = page.route.id.includes('project-[region]-[project]');
|
||||
|
||||
if (!isOnProjects) return null;
|
||||
|
||||
isLoadingProjects = true;
|
||||
@@ -212,12 +237,10 @@
|
||||
? $currentPlan.name
|
||||
: selectedOrg?.tierName; // fallback
|
||||
|
||||
$: derivedKey = `${selectedOrg?.$id}-${currentProject?.$id}`;
|
||||
$: derivedKey = `${selectedOrg?.$id}-${currentProject?.$id}-${ID.unique()}`;
|
||||
|
||||
$: organizationId = currentProject?.teamId;
|
||||
|
||||
$: isOnProjects = page.route.id.includes('project-[region]-[project]');
|
||||
|
||||
$: shouldReloadProjects = isLoadingProjects
|
||||
? false
|
||||
: currentProject && loadedProjects.projects.length
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -83,7 +83,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,
|
||||
@@ -336,14 +336,14 @@
|
||||
<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') &&
|
||||
!page.url.pathname.includes('/console/verify-email')}
|
||||
showHeader={!page.url.pathname.includes('/console/onboarding/create-project') &&
|
||||
!page.url.pathname.includes('/console/verify-email')}
|
||||
showFooter={!page.url.pathname.includes('/console/onboarding/create-project') &&
|
||||
!page.url.pathname.includes('/console/verify-email')}
|
||||
!page.url.pathname.includes(base + '/account') &&
|
||||
!page.url.pathname.includes(base + '/card') &&
|
||||
!page.url.pathname.includes(base + '/onboarding') &&
|
||||
!page.url.pathname.includes(base + '/verify-email')}
|
||||
showHeader={!page.url.pathname.includes(base + '/onboarding/create-project') &&
|
||||
!page.url.pathname.includes(base + '/verify-email')}
|
||||
showFooter={!page.url.pathname.includes(base + '/onboarding/create-project') &&
|
||||
!page.url.pathname.includes(base + '/verify-email')}
|
||||
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}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
Typography
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { IconCalendar, IconFingerPrint, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport';
|
||||
import { SortButton } from '$lib/components';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import {
|
||||
@@ -156,7 +156,9 @@
|
||||
|
||||
const spreadsheetColumns = $derived(mode === 'rows' ? getRowColumns() : getIndexesColumns());
|
||||
|
||||
const emptyCells = $derived(($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0));
|
||||
const emptyCells = $derived(
|
||||
($isSmallViewport ? 14 : $isTabletViewport ? 17 : 24) + (!$expandTabs ? 2 : 0)
|
||||
);
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
import ColumnItem from './columnItem.svelte';
|
||||
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let columns: Columns[] = [];
|
||||
export let formValues: object = {};
|
||||
export let customId: string | null | undefined = undefined;
|
||||
let {
|
||||
columns = [],
|
||||
formValues = $bindable({}),
|
||||
customId = $bindable(undefined)
|
||||
}: {
|
||||
columns: Columns[];
|
||||
formValues: object;
|
||||
customId: string | null | undefined;
|
||||
} = $props();
|
||||
|
||||
let showCustomId = false;
|
||||
let showCustomId = $state(false);
|
||||
</script>
|
||||
|
||||
{#if columns.length}
|
||||
|
||||
@@ -28,22 +28,18 @@
|
||||
function removeArrayItem(key: string, index: number) {
|
||||
const currentArray = Array.isArray($formStore[key]) ? $formStore[key] : [];
|
||||
const filteredArray = currentArray.filter((_: object, i: number) => i !== index);
|
||||
const next = {
|
||||
...formValues,
|
||||
[key]: filteredArray.length === 0 ? [] : filteredArray
|
||||
};
|
||||
|
||||
formStore.set(next);
|
||||
formStore.update((values) => {
|
||||
values[key] = filteredArray.length === 0 ? [] : filteredArray;
|
||||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
function addArrayItem(key: string) {
|
||||
const currentArray = Array.isArray($formStore[key]) ? $formStore[key] : null;
|
||||
const next = {
|
||||
...formValues,
|
||||
[key]: currentArray ? [...currentArray, null] : [null]
|
||||
};
|
||||
|
||||
formStore.set(next);
|
||||
formStore.update((values) => {
|
||||
values[key] = currentArray ? [...currentArray, null] : [null];
|
||||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
function getColumnType(column: Columns) {
|
||||
@@ -58,8 +54,7 @@
|
||||
case 'enum':
|
||||
return 'Enum';
|
||||
default:
|
||||
'String';
|
||||
break;
|
||||
return 'String';
|
||||
}
|
||||
}
|
||||
return `${capitalize(column.type)}${column.array ? '[]' : ''}`;
|
||||
|
||||
@@ -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>
|
||||
@@ -69,7 +69,8 @@
|
||||
} else {
|
||||
if (value && typeof value === 'object') {
|
||||
row = value as Models.Row;
|
||||
singleRel = row?.$id;
|
||||
// set for combobox and select.
|
||||
newItemValue = singleRel = row?.$id;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -86,7 +87,7 @@
|
||||
databaseId,
|
||||
tableId: column.relatedTable,
|
||||
// limit `5` as `25` would look too much on sheet!
|
||||
queries: [Query.select(displayNames), Query.limit(2)]
|
||||
queries: [Query.select(displayNames), Query.limit(5)]
|
||||
});
|
||||
|
||||
cachedRowsCopyList = rows;
|
||||
@@ -111,7 +112,8 @@
|
||||
}
|
||||
|
||||
function getAvailableOptions(excludeIndex?: number): SelectOption[] {
|
||||
return options?.filter((option) => {
|
||||
const source = options ?? [];
|
||||
return source?.filter((option) => {
|
||||
const otherItems =
|
||||
excludeIndex !== undefined
|
||||
? relatedList.filter((_, idx) => idx !== excludeIndex)
|
||||
@@ -368,23 +370,30 @@
|
||||
|
||||
<!-- Input for adding new items -->
|
||||
{#if showInput}
|
||||
<Layout.Stack direction="row">
|
||||
<Input.ComboBox
|
||||
{id}
|
||||
required
|
||||
placeholder={`Select ${column.key}`}
|
||||
bind:value={newItemValue}
|
||||
options={getAvailableOptions()}
|
||||
on:change={addNewItem}
|
||||
noResultsOption={searchNoResultsOption}
|
||||
leadingIcon={!limited ? IconRelationship : undefined} />
|
||||
{@const availableOptions = getAvailableOptions()}
|
||||
{@const noAvailableOptions = availableOptions.length <= 0}
|
||||
{#key availableOptions}
|
||||
<Layout.Stack direction="row">
|
||||
<Input.ComboBox
|
||||
{id}
|
||||
required
|
||||
on:change={addNewItem}
|
||||
bind:value={newItemValue}
|
||||
options={availableOptions}
|
||||
disabled={noAvailableOptions}
|
||||
placeholder={noAvailableOptions
|
||||
? 'No related items available'
|
||||
: `Select ${column.key}`}
|
||||
noResultsOption={searchNoResultsOption}
|
||||
leadingIcon={!limited ? IconRelationship : undefined} />
|
||||
|
||||
<div style:padding-block-start="0.5rem">
|
||||
<Button icon extraCompact on:click={cancelAddItem}>
|
||||
<Icon icon={IconX} size="s" />
|
||||
</Button>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<div style:padding-block-start="0.5rem">
|
||||
<Button icon extraCompact on:click={cancelAddItem}>
|
||||
<Icon icon={IconX} size="s" />
|
||||
</Button>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
{/key}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -400,44 +409,77 @@
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<Input.ComboBox
|
||||
{id}
|
||||
{options}
|
||||
autofocus={limited}
|
||||
bind:value={newItemValue}
|
||||
required={column.required}
|
||||
label={limited ? undefined : label}
|
||||
placeholder={`Select ${column.key}`}
|
||||
noResultsOption={searchNoResultsOption}
|
||||
on:change={() => {
|
||||
if (newItemValue === null) {
|
||||
value = null;
|
||||
singleRel = null;
|
||||
} else {
|
||||
const selectedRow = rowList.rows.find((row) => row.$id === newItemValue);
|
||||
{#key options}
|
||||
{#if limited}
|
||||
<!-- for unlink badge -->
|
||||
<Input.Select
|
||||
{id}
|
||||
{options}
|
||||
autofocus={limited}
|
||||
bind:value={newItemValue}
|
||||
required={column.required}
|
||||
label={limited ? undefined : label}
|
||||
placeholder={`Select ${column.key}`}
|
||||
noResultsOption={searchNoResultsOption}
|
||||
on:change={() => {
|
||||
if (newItemValue === null) {
|
||||
value = null;
|
||||
singleRel = null;
|
||||
} else {
|
||||
const selectedRow = rowList.rows.find(
|
||||
(row) => row.$id === newItemValue
|
||||
);
|
||||
|
||||
if (selectedRow) {
|
||||
value = selectedRow;
|
||||
singleRel = newItemValue;
|
||||
}
|
||||
}
|
||||
if (selectedRow) {
|
||||
value = selectedRow;
|
||||
singleRel = newItemValue;
|
||||
}
|
||||
}
|
||||
}}
|
||||
leadingIcon={!limited ? IconRelationship : undefined} />
|
||||
{:else}
|
||||
{@const noOptions = options.length <= 0}
|
||||
<Input.ComboBox
|
||||
{id}
|
||||
{options}
|
||||
autofocus={limited}
|
||||
bind:value={newItemValue}
|
||||
required={column.required}
|
||||
label={limited ? undefined : label}
|
||||
disabled={noOptions}
|
||||
placeholder={noOptions ? 'No related items available' : `Select ${column.key}`}
|
||||
noResultsOption={searchNoResultsOption}
|
||||
on:change={() => {
|
||||
if (newItemValue === null) {
|
||||
value = null;
|
||||
singleRel = null;
|
||||
} else {
|
||||
const selectedRow = rowList.rows.find(
|
||||
(row) => row.$id === newItemValue
|
||||
);
|
||||
|
||||
newItemValue = null;
|
||||
}}
|
||||
leadingIcon={!limited ? IconRelationship : undefined} />
|
||||
if (selectedRow) {
|
||||
value = selectedRow;
|
||||
singleRel = newItemValue;
|
||||
}
|
||||
}
|
||||
}}
|
||||
leadingIcon={!limited ? IconRelationship : undefined} />
|
||||
{/if}
|
||||
|
||||
{#if !limited && singleRel}
|
||||
<div style:padding-block-start="2.25rem">
|
||||
<Button
|
||||
icon
|
||||
extraCompact
|
||||
on:click={() => {
|
||||
value = null;
|
||||
singleRel = null;
|
||||
}}>
|
||||
<Icon icon={IconX} size="s" />
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !limited && singleRel}
|
||||
<div style:padding-block-start="2.25rem">
|
||||
<Button
|
||||
icon
|
||||
extraCompact
|
||||
on:click={() => {
|
||||
value = null;
|
||||
newItemValue = singleRel = null;
|
||||
}}>
|
||||
<Icon icon={IconX} size="s" />
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{/key}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import ColumnForm from './columns/columnForm.svelte';
|
||||
import { Permissions } from '$lib/components/permissions';
|
||||
import { type Columns } from '../store';
|
||||
import { type Columns, spreadsheetRenderKey } from '../store';
|
||||
import { ID, type Models } from '@appwrite.io/console';
|
||||
import { Alert, Layout, Typography, Selector } from '@appwrite.io/pink-svelte';
|
||||
import SideSheet from '../layout/sidesheet.svelte';
|
||||
@@ -15,6 +15,7 @@
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { tick } from 'svelte';
|
||||
import { isRelationship, isRelationshipToMany } from './store';
|
||||
import { hash } from '$lib/helpers/string';
|
||||
|
||||
type CreateRow = {
|
||||
id?: string;
|
||||
@@ -93,13 +94,15 @@
|
||||
$createRow.row = prepareRowPayload($createRow);
|
||||
|
||||
try {
|
||||
await sdk.forProject(page.params.region, page.params.project).tablesDB.createRow({
|
||||
databaseId: page.params.database,
|
||||
tableId: page.params.table,
|
||||
rowId: $createRow.id ?? ID.unique(),
|
||||
data: $createRow.row,
|
||||
permissions: $createRow.permissions
|
||||
});
|
||||
const row = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.tablesDB.createRow({
|
||||
databaseId: page.params.database,
|
||||
tableId: page.params.table,
|
||||
rowId: $createRow.id ?? ID.unique(),
|
||||
data: $createRow.row,
|
||||
permissions: $createRow.permissions
|
||||
});
|
||||
|
||||
addNotification({
|
||||
message: 'Row has been created',
|
||||
@@ -109,7 +112,11 @@
|
||||
trackEvent(Submit.RowCreate, {
|
||||
customId: !!$createRow.id
|
||||
});
|
||||
await invalidate(Dependencies.ROW);
|
||||
|
||||
await invalidate(Dependencies.ROWS);
|
||||
|
||||
// re-render spreadsheet on addition!
|
||||
spreadsheetRenderKey.set(hash(row.$id));
|
||||
|
||||
if (createMore) {
|
||||
resetCreateRow();
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Confirm, Id, SortButton } from '$lib/components';
|
||||
import { Dependencies, SPREADSHEET_PAGE_LIMIT } from '$lib/constants';
|
||||
import { Button as ConsoleButton, InputChoice, InputSelect } from '$lib/elements/forms';
|
||||
import { Button as ConsoleButton, InputSelect } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -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,
|
||||
@@ -78,7 +82,7 @@
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import type { HeaderCellAction, RowCellAction } from './sheetOptions.svelte';
|
||||
import SheetOptions from './sheetOptions.svelte';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport';
|
||||
import SpreadsheetContainer from './layout/spreadsheet.svelte';
|
||||
import EditRowCell from './rows/cell/edit.svelte';
|
||||
import { copy } from '$lib/helpers/copy';
|
||||
@@ -107,7 +111,13 @@
|
||||
const organizationId = data.organization.$id ?? data.project.teamId;
|
||||
|
||||
const minimumWidth = 168;
|
||||
const emptyCellsLimit = $isSmallViewport ? 12 : 18;
|
||||
const emptyCellsLimit = $spreadsheetLoading
|
||||
? 30
|
||||
: $isSmallViewport
|
||||
? 12
|
||||
: $isTabletViewport
|
||||
? 18
|
||||
: 24;
|
||||
|
||||
let selectedRows = [];
|
||||
let spreadsheetContainer: SpreadsheetContainer;
|
||||
@@ -117,7 +127,6 @@
|
||||
|
||||
let showDelete = false;
|
||||
let showColumnDelete = false;
|
||||
let deleteConfirmationChecked = false;
|
||||
let selectedRowForDelete: Models.Row['$id'] | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
@@ -305,6 +314,12 @@
|
||||
return IconViewList;
|
||||
case 'relationship':
|
||||
return IconRelationship;
|
||||
case 'point':
|
||||
return IconPoint;
|
||||
case 'linestring':
|
||||
return IconLine;
|
||||
case 'polygon':
|
||||
return IconPolygon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,6 +924,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])}
|
||||
@@ -1073,8 +1092,12 @@
|
||||
</SpreadsheetContainer>
|
||||
|
||||
<Confirm
|
||||
confirmDeletion
|
||||
bind:open={showDelete}
|
||||
onSubmit={handleDelete}
|
||||
confirmDeletionLabel={relatedColumns?.length
|
||||
? `Delete ${selectedRowForDelete !== null ? 'row' : 'rows'} from ${$table.name}`
|
||||
: undefined}
|
||||
title={selectedRows.length === 1 ? 'Delete Row' : 'Delete Rows'}>
|
||||
{@const isSingle = selectedRowForDelete !== null}
|
||||
|
||||
@@ -1125,17 +1148,6 @@
|
||||
|
||||
<Layout.Stack direction="column" gap="m">
|
||||
<Alert.Inline>To change the selection edit the relationship settings.</Alert.Inline>
|
||||
|
||||
<ul>
|
||||
<InputChoice
|
||||
id="delete"
|
||||
label="Delete"
|
||||
showLabel={false}
|
||||
bind:value={deleteConfirmationChecked}>
|
||||
Delete {isSingle ? 'row' : 'rows'} from
|
||||
<span data-private>{$table.name}</span>
|
||||
</InputChoice>
|
||||
</ul>
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<p class="u-bold">This action is irreversible.</p>
|
||||
|
||||
@@ -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]
|
||||
];
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
{/snippet}
|
||||
|
||||
<Card padding="m" radius="m" {variant}>
|
||||
<Layout.Stack gap="xxl">
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.GridFraction start={4} end={6}>
|
||||
<Layout.Stack>
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s" wrap="wrap">
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import { Copy } from '$lib/components';
|
||||
import { logStatusConverter } from './store';
|
||||
import { LogsRequest, LogsResponse } from '$lib/components/logs';
|
||||
import { timer } from '$lib/actions/timer';
|
||||
|
||||
export let selectedLogId: string;
|
||||
export let logs: Models.Execution[];
|
||||
@@ -139,7 +140,11 @@
|
||||
Duration
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
{calculateTime(selectedLog.duration)}
|
||||
{#if ['processing', 'waiting'].includes(selectedLog.status)}
|
||||
<span use:timer={{ start: selectedLog.$createdAt }}></span>
|
||||
{:else}
|
||||
{calculateTime(selectedLog.duration)}
|
||||
{/if}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { timer } from '$lib/actions/timer';
|
||||
|
||||
export let columns: Column[];
|
||||
export let executions: Models.ExecutionList;
|
||||
@@ -121,7 +122,11 @@
|
||||
</span>
|
||||
</Tooltip>
|
||||
{:else if column.id === 'duration'}
|
||||
{calculateTime(log.duration)}
|
||||
{#if ['processing', 'waiting'].includes(log.status)}
|
||||
<span use:timer={{ start: log.$createdAt }}></span>
|
||||
{:else}
|
||||
{calculateTime(log.duration)}
|
||||
{/if}
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
{/each}
|
||||
|
||||
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 171 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.62112 12.97L8.10468 10.9839L8.16326 10.8114L8.10468 10.7155H7.93493L7.35274 10.679L5.3623 10.6244L3.63606 10.5515L1.96362 10.4604L1.54282 10.3693L1.14832 9.84087L1.18896 9.57727L1.54282 9.33554L2.04969 9.38048L3.16983 9.45823L4.85065 9.57606L6.07001 9.64894L7.87635 9.83965H8.16326L8.2039 9.72183L8.10588 9.64894L8.02937 9.57606L6.28998 8.37832L4.40713 7.11257L3.42088 6.38372L2.88771 6.01444L2.61873 5.66824L2.50277 4.91267L2.98693 4.3709L3.63726 4.41585L3.80343 4.46079L4.46212 4.97584L5.86918 6.08247L7.70659 7.45755L7.97557 7.68471L8.08316 7.60696L8.09631 7.5523L7.97557 7.34701L6.97617 5.51154L5.90982 3.64449L5.43523 2.8707L5.3097 2.40667C5.26547 2.21595 5.23319 2.05561 5.23319 1.86004L5.7843 1.09961L6.08914 1L6.82435 1.09961L7.13397 1.37293L7.59063 2.43461L8.33062 4.10609L9.47826 6.37886L9.81419 7.05304L9.9935 7.67742L10.0605 7.86813H10.1764V7.75881L10.2709 6.47847L10.4454 4.9066L10.6151 2.88406L10.6737 2.31435L10.9511 1.63166L11.5022 1.26238L11.9325 1.47132L12.2864 1.98637L12.2374 2.31921L12.027 3.70887L11.6145 5.88568L11.3456 7.34337H11.5022L11.6815 7.16116L12.4071 6.18208L13.6265 4.63328L14.1645 4.01863L14.7921 3.33959L15.1949 3.01647H15.9564L16.5171 3.86314L16.2661 4.73775L15.4818 5.74841L14.8315 6.6048L13.8991 7.88028L13.3169 8.90066L13.3707 8.98205L13.5093 8.96869L15.6157 8.51316L16.7538 8.30422L18.1119 8.06735L18.7263 8.35889L18.7933 8.65528L18.5518 9.26144L17.0993 9.62586L15.3958 9.97206L12.859 10.5819L12.8279 10.6049L12.8638 10.6499L14.0067 10.7592L14.4956 10.7859H15.6923L17.9206 10.9548L18.5028 11.3459L18.8518 11.8245L18.7933 12.189L17.8967 12.653L16.6869 12.3615L13.8632 11.6788L12.8949 11.4334H12.761V11.5148L13.5679 12.3165L15.0467 13.6734L16.8985 15.4226L16.9929 15.855L16.755 16.1964L16.504 16.1599L14.8769 14.916L14.2493 14.3561L12.8279 13.1401H12.7335V13.2676L13.061 13.7548L14.7909 16.3968L14.8805 17.207L14.755 17.4706L14.3067 17.6298L13.8142 17.5387L12.8016 16.0943L11.7568 14.4678L10.914 13.0101L10.8112 13.0696L10.3139 18.5129L10.0808 18.7911L9.54282 19L9.09452 18.6538L8.85663 18.0938L9.09452 16.9872L9.38143 15.5429L9.61454 14.3949L9.82495 12.9688L9.95047 12.4951L9.9421 12.4635L9.83929 12.4769L8.78131 13.9528L7.17222 16.1624L5.89906 17.5472L5.59422 17.6699L5.06583 17.3917L5.11484 16.8949L5.41012 16.4527L7.17222 14.1751L8.23499 12.7635L8.92118 11.9484L8.9164 11.8306H8.87575L4.19554 14.9185L3.3623 15.0278L3.00367 14.6865L3.0479 14.1265L3.21765 13.9443L4.62471 12.9603L4.61992 12.9652L4.62112 12.97Z" fill="#2D2D31"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,37 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_8409_469" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="10" width="16" height="9">
|
||||
<path d="M9.93746 18.3333L17.177 14.1667L9.93746 10L2.69788 14.1667L9.93746 18.3333Z" fill="url(#paint0_linear_8409_469)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_8409_469)">
|
||||
<path d="M9.93746 18.3333L17.177 14.1667L9.93746 10L2.69788 14.1667L9.93746 18.3333Z" fill="#2D2D31"/>
|
||||
</g>
|
||||
<mask id="mask1_8409_469" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="9" y="1" width="9" height="14">
|
||||
<path d="M17.177 14.1667V5.83335L9.93738 1.66669V10L17.177 14.1667Z" fill="url(#paint1_linear_8409_469)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_8409_469)">
|
||||
<path d="M17.177 14.1667V5.83335L9.93738 1.66669V10L17.177 14.1667Z" fill="#2D2D31"/>
|
||||
</g>
|
||||
<mask id="mask2_8409_469" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="1" width="8" height="14">
|
||||
<path d="M9.93746 1.66669L2.69788 5.83335V14.1667L9.93746 10V1.66669Z" fill="url(#paint2_linear_8409_469)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask2_8409_469)">
|
||||
<path d="M9.93746 1.66669L2.69788 5.83335V14.1667L9.93746 10V1.66669Z" fill="#2D2D31"/>
|
||||
</g>
|
||||
<path d="M17.177 5.83331L9.93738 18.3333V9.99998L17.177 5.83331Z" fill="#2D2D31"/>
|
||||
<path d="M17.177 5.83331L9.93738 18.3333V9.99998L17.177 5.83331Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M17.177 5.83331L9.93746 9.99998L2.69788 5.83331H17.177Z" fill="#2D2D31"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_8409_469" x1="9.93746" y1="10" x2="9.93746" y2="18.3333" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.16" stop-opacity="0.39"/>
|
||||
<stop offset="0.658" stop-opacity="0.8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_8409_469" x1="17.177" y1="5.85905" x2="9.93738" y2="10.1042" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.182" stop-opacity="0.31"/>
|
||||
<stop offset="0.715" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_8409_469" x1="9.93746" y1="1.66669" x2="2.69788" y2="14.1667" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.6"/>
|
||||
<stop offset="0.667" stop-opacity="0.22"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.62112 12.97L8.10468 10.9839L8.16326 10.8114L8.10468 10.7155H7.93493L7.35274 10.679L5.3623 10.6244L3.63606 10.5515L1.96362 10.4604L1.54282 10.3693L1.14832 9.84087L1.18896 9.57727L1.54282 9.33554L2.04969 9.38048L3.16983 9.45823L4.85065 9.57606L6.07001 9.64894L7.87635 9.83965H8.16326L8.2039 9.72183L8.10588 9.64894L8.02937 9.57606L6.28998 8.37832L4.40713 7.11257L3.42088 6.38372L2.88771 6.01444L2.61873 5.66824L2.50277 4.91267L2.98693 4.3709L3.63726 4.41585L3.80343 4.46079L4.46212 4.97584L5.86918 6.08247L7.70659 7.45755L7.97557 7.68471L8.08316 7.60696L8.09631 7.5523L7.97557 7.34701L6.97617 5.51154L5.90982 3.64449L5.43523 2.8707L5.3097 2.40667C5.26547 2.21595 5.23319 2.05561 5.23319 1.86004L5.7843 1.09961L6.08914 1L6.82435 1.09961L7.13397 1.37293L7.59063 2.43461L8.33062 4.10609L9.47826 6.37886L9.81419 7.05304L9.9935 7.67742L10.0605 7.86813H10.1764V7.75881L10.2709 6.47847L10.4454 4.9066L10.6151 2.88406L10.6737 2.31435L10.9511 1.63166L11.5022 1.26238L11.9325 1.47132L12.2864 1.98637L12.2374 2.31921L12.027 3.70887L11.6145 5.88568L11.3456 7.34337H11.5022L11.6815 7.16116L12.4071 6.18208L13.6265 4.63328L14.1645 4.01863L14.7921 3.33959L15.1949 3.01647H15.9564L16.5171 3.86314L16.2661 4.73775L15.4818 5.74841L14.8315 6.6048L13.8991 7.88028L13.3169 8.90066L13.3707 8.98205L13.5093 8.96869L15.6157 8.51316L16.7538 8.30422L18.1119 8.06735L18.7263 8.35889L18.7933 8.65528L18.5518 9.26144L17.0993 9.62586L15.3958 9.97206L12.859 10.5819L12.8279 10.6049L12.8638 10.6499L14.0067 10.7592L14.4956 10.7859H15.6923L17.9206 10.9548L18.5028 11.3459L18.8518 11.8245L18.7933 12.189L17.8967 12.653L16.6869 12.3615L13.8632 11.6788L12.8949 11.4334H12.761V11.5148L13.5679 12.3165L15.0467 13.6734L16.8985 15.4226L16.9929 15.855L16.755 16.1964L16.504 16.1599L14.8769 14.916L14.2493 14.3561L12.8279 13.1401H12.7335V13.2676L13.061 13.7548L14.7909 16.3968L14.8805 17.207L14.755 17.4706L14.3067 17.6298L13.8142 17.5387L12.8016 16.0943L11.7568 14.4678L10.914 13.0101L10.8112 13.0696L10.3139 18.5129L10.0808 18.7911L9.54282 19L9.09452 18.6538L8.85663 18.0938L9.09452 16.9872L9.38143 15.5429L9.61454 14.3949L9.82495 12.9688L9.95047 12.4951L9.9421 12.4635L9.83929 12.4769L8.78131 13.9528L7.17222 16.1624L5.89906 17.5472L5.59422 17.6699L5.06583 17.3917L5.11484 16.8949L5.41012 16.4527L7.17222 14.1751L8.23499 12.7635L8.92118 11.9484L8.9164 11.8306H8.87575L4.19554 14.9185L3.3623 15.0278L3.00367 14.6865L3.0479 14.1265L3.21765 13.9443L4.62471 12.9603L4.61992 12.9652L4.62112 12.97Z" fill="#EDEDF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,37 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_8409_615" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="10" width="16" height="9">
|
||||
<path d="M9.93746 18.3333L17.177 14.1667L9.93746 10L2.69788 14.1667L9.93746 18.3333Z" fill="url(#paint0_linear_8409_615)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_8409_615)">
|
||||
<path d="M9.93746 18.3333L17.177 14.1667L9.93746 10L2.69788 14.1667L9.93746 18.3333Z" fill="#EDEDF0"/>
|
||||
</g>
|
||||
<mask id="mask1_8409_615" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="9" y="1" width="9" height="14">
|
||||
<path d="M17.177 14.1667V5.83335L9.93738 1.66669V10L17.177 14.1667Z" fill="url(#paint1_linear_8409_615)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_8409_615)">
|
||||
<path d="M17.177 14.1667V5.83335L9.93738 1.66669V10L17.177 14.1667Z" fill="#EDEDF0"/>
|
||||
</g>
|
||||
<mask id="mask2_8409_615" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="1" width="8" height="14">
|
||||
<path d="M9.93746 1.66669L2.69788 5.83335V14.1667L9.93746 10V1.66669Z" fill="url(#paint2_linear_8409_615)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask2_8409_615)">
|
||||
<path d="M9.93746 1.66669L2.69788 5.83335V14.1667L9.93746 10V1.66669Z" fill="#EDEDF0"/>
|
||||
</g>
|
||||
<path d="M17.177 5.83331L9.93738 18.3333V9.99998L17.177 5.83331Z" fill="#EDEDF0"/>
|
||||
<path d="M17.177 5.83331L9.93738 18.3333V9.99998L17.177 5.83331Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M17.177 5.83331L9.93746 9.99998L2.69788 5.83331H17.177Z" fill="#EDEDF0"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_8409_615" x1="9.93746" y1="10" x2="9.93746" y2="18.3333" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.16" stop-opacity="0.39"/>
|
||||
<stop offset="0.658" stop-opacity="0.8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_8409_615" x1="17.177" y1="5.85905" x2="9.93738" y2="10.1042" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.182" stop-opacity="0.31"/>
|
||||
<stop offset="0.715" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_8409_615" x1="9.93746" y1="1.66669" x2="2.69788" y2="14.1667" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.6"/>
|
||||
<stop offset="0.667" stop-opacity="0.22"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.7515 3.09828C13.7515 2.62741 13.2243 2.6274 12.99 2.68626C13.318 2.42728 13.7027 2.49007 13.8394 2.56855L17.0072 4.12186C17.309 4.26986 17.5005 4.57782 17.5005 4.9153V15.1338C17.5005 15.4758 17.3039 15.7871 16.996 15.9327L14.0151 17.3422C13.8101 17.4304 13.3649 17.6482 12.99 17.3421C13.4586 17.4304 13.7125 17.0969 13.7515 16.8713V3.09828Z" fill="#EDEDF0"/>
|
||||
<path d="M13.0273 2.67773C13.2759 2.62858 13.7508 2.65137 13.7508 3.09824V6.61557L4.14654 13.8813C3.97866 14.0083 3.74241 13.9873 3.59942 13.8325L2.60957 12.7614C2.4545 12.5936 2.46535 12.331 2.63372 12.1767L12.9892 2.68622L13.0273 2.67773Z" fill="#EDEDF0"/>
|
||||
<path d="M13.7508 13.4129L4.14654 6.14708C3.97866 6.02008 3.74241 6.04115 3.59942 6.19587L2.60957 7.26698C2.4545 7.43478 2.46535 7.69744 2.63372 7.85175L12.9892 17.3422C13.4579 17.4305 13.7117 17.0969 13.7508 16.8713V13.4129Z" fill="#EDEDF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 978 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.38283 10.7525L5.39611 7.99401C5.22398 7.51281 5.57208 7.00214 6.06732 7.02528C11.5964 7.2832 14.4331 9.29146 14.1687 15.0827C13.2071 11.0382 8.5951 10.7525 6.38283 10.7525Z" fill="#EDEDF0"/>
|
||||
<path d="M4.89231 5.67595L3.86522 3.00302C3.67933 2.51927 4.02719 1.99404 4.53013 2.00005C10.1767 2.06749 14.1193 2.44873 14.1193 9.95706C13.1577 5.91252 7.10459 5.67595 4.89231 5.67595Z" fill="#EDEDF0"/>
|
||||
<path d="M7.91135 15.9917L6.85283 13.0398C6.68118 12.5612 7.02451 12.0516 7.51714 12.0777C12.0282 12.3168 14.2845 14.2758 14.0232 20C13.0616 15.9555 10.1396 15.9917 7.91135 15.9917Z" fill="#EDEDF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.7514 3.09828C13.7514 2.62741 13.2242 2.6274 12.9899 2.68626C13.3179 2.42728 13.7026 2.49007 13.8392 2.56855L17.0071 4.12186C17.3089 4.26986 17.5004 4.57782 17.5004 4.9153V15.1338C17.5004 15.4758 17.3038 15.7871 16.9958 15.9327L14.015 17.3422C13.81 17.4304 13.3648 17.6482 12.9899 17.3421C13.4585 17.4304 13.7123 17.0969 13.7514 16.8713V3.09828Z" fill="#2D2D31"/>
|
||||
<path d="M13.0273 2.67773C13.2759 2.62858 13.7508 2.65137 13.7508 3.09824V6.61557L4.14654 13.8813C3.97866 14.0083 3.74241 13.9873 3.59942 13.8325L2.60957 12.7614C2.4545 12.5936 2.46535 12.331 2.63372 12.1767L12.9892 2.68622L13.0273 2.67773Z" fill="#2D2D31"/>
|
||||
<path d="M13.7508 13.4129L4.14654 6.14708C3.97866 6.02008 3.74241 6.04115 3.59942 6.19587L2.60957 7.26698C2.4545 7.43478 2.46535 7.69744 2.63372 7.85175L12.9892 17.3422C13.4579 17.4305 13.7117 17.0969 13.7508 16.8713V13.4129Z" fill="#2D2D31"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 982 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.38283 10.7525L5.39611 7.99401C5.22398 7.51281 5.57208 7.00214 6.06732 7.02528C11.5964 7.2832 14.4331 9.29146 14.1687 15.0827C13.2071 11.0382 8.5951 10.7525 6.38283 10.7525Z" fill="#2D2D31"/>
|
||||
<path d="M4.89231 5.67595L3.86522 3.00302C3.67933 2.51927 4.02719 1.99404 4.53013 2.00005C10.1767 2.06749 14.1193 2.44873 14.1193 9.95706C13.1577 5.91252 7.10459 5.67595 4.89231 5.67595Z" fill="#2D2D31"/>
|
||||
<path d="M7.91135 15.9917L6.85283 13.0398C6.68118 12.5612 7.02451 12.0516 7.51714 12.0777C12.0282 12.3168 14.2845 14.2758 14.0232 20C13.0616 15.9555 10.1396 15.9917 7.91135 15.9917Z" fill="#2D2D31"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
import Light from '../assets/claude.svg';
|
||||
import Dark from '../assets/dark/claude.svg';
|
||||
</script>
|
||||
|
||||
<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" />
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
import Light from '../assets/cursor-ai.svg';
|
||||
import Dark from '../assets/dark/cursor-ai.svg';
|
||||
</script>
|
||||
|
||||
<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" />
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
import Light from '../assets/vscode.svg';
|
||||
import Dark from '../assets/dark/vscode.svg';
|
||||
</script>
|
||||
|
||||
<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" />
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { app } from '$lib/stores/app';
|
||||
import Light from '../assets/windsurf.svg';
|
||||
import Dark from '../assets/dark/windsurf.svg';
|
||||
</script>
|
||||
|
||||
<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" />
|
||||
@@ -12,6 +12,8 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { addPlatform, continuePlatform } from './platforms/+page.svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
import AuthPreview from './assets/auth-preview.svg';
|
||||
import AuthPreviewDark from './assets/auth-preview-dark.svg';
|
||||
import {
|
||||
IconArrowRight,
|
||||
IconNodeJs,
|
||||
@@ -20,10 +22,9 @@
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import DatabaseImgSource from './assets/database.png';
|
||||
import DatabaseImgSourceDark from './assets/database-dark.png';
|
||||
import UsersImgSource from './assets/users.svg';
|
||||
import UsersImgSourceDark from './assets/users-dark.svg';
|
||||
import DiscordImgSource from './assets/discord.png';
|
||||
import DiscordImgSourceDark from './assets/discord-dark.png';
|
||||
import { mcpTools } from '../store';
|
||||
import PlatformIosImgSource from './assets/platform-ios.svg';
|
||||
import PlatformIosImgSourceDark from './assets/platform-ios-dark.svg';
|
||||
import PlatformAndroidImgSource from './assets/platform-android.svg';
|
||||
@@ -102,9 +103,7 @@
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack
|
||||
gap="l"
|
||||
direction={$isSmallViewport ? 'column' : 'row'}>
|
||||
<Layout.Stack gap="l" direction="row">
|
||||
<Card.Button
|
||||
on:click={() => {
|
||||
openPlatformWizard(0, platformMap.get('Web'));
|
||||
@@ -479,10 +478,12 @@
|
||||
<div class="double-width-card">
|
||||
<Card.Base padding="s"
|
||||
><div class="full-height-card">
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack
|
||||
direction={$isSmallViewport ? 'column' : 'row'}>
|
||||
<Layout.Stack
|
||||
gap="xl"
|
||||
justifyContent="space-between">
|
||||
justifyContent="space-between"
|
||||
style={`flex: ${$isSmallViewport ? '1 1 auto' : '0 0 30%'}; min-width: ${$isSmallViewport ? 'auto' : '240px'}`}>
|
||||
<Typography.Title size="s"
|
||||
>Set up Auth</Typography.Title>
|
||||
<Layout.Stack
|
||||
@@ -519,33 +520,95 @@
|
||||
</Layout.Stack>
|
||||
<div
|
||||
class="auth-image"
|
||||
style="flex: 1 1 auto"
|
||||
style:background-image={`url('${
|
||||
$app.themeInUse === 'dark'
|
||||
? UsersImgSourceDark
|
||||
: UsersImgSource
|
||||
? AuthPreviewDark
|
||||
: AuthPreview
|
||||
}')`}>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</div></Card.Base>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack
|
||||
gap="l"
|
||||
direction={$isSmallViewport ? 'column' : 'row'}>
|
||||
<Card.Base
|
||||
padding="s"
|
||||
style={`flex: ${$isSmallViewport ? '1 1 auto' : '1 1 70%'};`}>
|
||||
<div class="full-height-card">
|
||||
<Layout.Stack gap="xl" justifyContent="space-between">
|
||||
<Layout.Stack gap="s">
|
||||
<Typography.Title
|
||||
color="--fgcolor-neutral-secondary"
|
||||
size="s">MCP server</Typography.Title>
|
||||
<Typography.Text
|
||||
color="--fgcolor-neutral-secondary">
|
||||
Deploy the Appwrite MCP server with a single
|
||||
click, or view the <Link.Anchor
|
||||
href="https://appwrite.io/docs"
|
||||
target="_blank">docs</Link.Anchor> for instructions.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="s">
|
||||
<Typography.Text
|
||||
color="--fgcolor-neutral-tertiary"
|
||||
size="s">One-click install</Typography.Text>
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
gap="s"
|
||||
wrap="wrap">
|
||||
{#each mcpTools as tool}
|
||||
<Button.Anchor
|
||||
href={tool.href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
size="s"
|
||||
variant="secondary">
|
||||
<Icon
|
||||
slot="start"
|
||||
icon={tool.icon}
|
||||
size="xs" />
|
||||
{tool.label}
|
||||
</Button.Anchor>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Card.Base>
|
||||
|
||||
<Card.Link
|
||||
href="https://appwrite.io/discord"
|
||||
padding="s"
|
||||
on:click={() => {
|
||||
trackEvent(Click.OnboardingDiscordClick);
|
||||
}}>
|
||||
}}
|
||||
style={`flex: ${$isSmallViewport ? '1 1 auto' : '1 1 28%'};`}>
|
||||
<div class="full-height-card">
|
||||
<Layout.Stack gap="xs" justifyContent="space-between">
|
||||
<Layout.Stack direction="column" gap="xxxs">
|
||||
<img
|
||||
src={$app.themeInUse === 'dark'
|
||||
? DiscordImgSourceDark
|
||||
: DiscordImgSource}
|
||||
class="discord"
|
||||
alt="" />
|
||||
<Typography.Title size="s"
|
||||
>Discord</Typography.Title>
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
class="discord-header">
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
alignItems="flex-start"
|
||||
gap="xs">
|
||||
<img
|
||||
src={$app.themeInUse === 'dark'
|
||||
? DiscordImgSourceDark
|
||||
: DiscordImgSource}
|
||||
class="discord"
|
||||
alt="" />
|
||||
<Typography.Title size="s"
|
||||
>Discord</Typography.Title>
|
||||
</Layout.Stack>
|
||||
<div class="arrow-icon arrow-icon-discord">
|
||||
<Icon icon={IconArrowRight} size="s" />
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
@@ -556,9 +619,6 @@
|
||||
color="--fgcolor-neutral-secondary">
|
||||
Join our Discord for support, tips and
|
||||
product updates</Typography.Text>
|
||||
<div class="arrow-icon arrow-icon-discord">
|
||||
<Icon icon={IconArrowRight} size="s" />
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
@@ -635,7 +695,7 @@
|
||||
margin-bottom: calc(-1 * var(--base-16, 16px));
|
||||
align-self: flex-end;
|
||||
background-size: cover;
|
||||
background-position: left bottom;
|
||||
background-position: right bottom;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
:global(.full-height-card div) {
|
||||
@@ -647,6 +707,30 @@
|
||||
width: 250%;
|
||||
}
|
||||
}
|
||||
.double-width-card .full-height-card {
|
||||
height: 240px;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-image {
|
||||
@media (max-width: 1024px) {
|
||||
height: 180px;
|
||||
background-position: center bottom;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
height: 160px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
color: var(--border-neutral-strong);
|
||||
@@ -656,7 +740,7 @@
|
||||
}
|
||||
}
|
||||
.arrow-icon-discord {
|
||||
align-items: flex-end;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.platform-image {
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
external
|
||||
href="#/"
|
||||
href="https://appwrite.io/docs/advanced/platform/custom-domains"
|
||||
text
|
||||
event="empty_documentation"
|
||||
size="s"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { Avatar, CardGrid, PaginationInline } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Button as FormButton } from '$lib/elements/forms';
|
||||
import { getApiEndpoint } from '$lib/stores/sdk';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import GitDisconnectModal from './GitDisconnectModal.svelte';
|
||||
@@ -15,8 +15,10 @@
|
||||
Icon,
|
||||
Layout,
|
||||
Popover,
|
||||
Table
|
||||
Table,
|
||||
Button
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Link as PinkLink } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconExternalLink,
|
||||
IconGithub,
|
||||
@@ -27,7 +29,7 @@
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import type { ComponentType } from 'svelte';
|
||||
import { Link } from '$lib/elements';
|
||||
import { regionalConsoleVariables } from '../store';
|
||||
import { regionalConsoleVariables, mcpTools } from '../store';
|
||||
|
||||
export let total: number;
|
||||
export let limit: number;
|
||||
@@ -83,7 +85,7 @@
|
||||
{#if total > 0}
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<Button
|
||||
<FormButton
|
||||
secondary
|
||||
href={configureGitHub()}
|
||||
on:click={() => {
|
||||
@@ -91,7 +93,7 @@
|
||||
}}>
|
||||
<Icon icon={IconPlus} slot="start" size="s" />
|
||||
Add installation
|
||||
</Button>
|
||||
</FormButton>
|
||||
</Layout.Stack>
|
||||
|
||||
<Table.Root
|
||||
@@ -171,10 +173,10 @@
|
||||
Before installing Git in a locally hosted Appwrite project, ensure your environment
|
||||
variables are configured.
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
<FormButton
|
||||
compact
|
||||
href="https://appwrite.io/docs/advanced/self-hosting/configuration/version-control"
|
||||
external>Learn more</Button>
|
||||
external>Learn more</FormButton>
|
||||
</svelte:fragment>
|
||||
</Alert.Inline>
|
||||
{:else}
|
||||
@@ -184,10 +186,10 @@
|
||||
title="No installation was added to the project yet"
|
||||
description="Add an installation to connect repositories">
|
||||
<svelte:fragment slot="actions">
|
||||
<Button secondary href={configureGitHub()} external>
|
||||
<FormButton secondary href={configureGitHub()} external>
|
||||
<Icon icon={IconGithub} size="s" slot="start" />
|
||||
Connect to GitHub
|
||||
</Button>
|
||||
</FormButton>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
@@ -198,3 +200,36 @@
|
||||
{#if showGitDisconnect}
|
||||
<GitDisconnectModal bind:showGitDisconnect {selectedInstallation} />
|
||||
{/if}
|
||||
|
||||
<CardGrid>
|
||||
<svelte:fragment slot="title">MCP server</svelte:fragment>
|
||||
Spin up an MCP server to stream rich project context to AI tooling, unlocking deeper code understanding
|
||||
and better completions.
|
||||
<svelte:fragment slot="aside">
|
||||
<Card.Base padding="none" border="dashed">
|
||||
<Empty type="secondary" title="MCP installation">
|
||||
<svelte:fragment slot="description">
|
||||
Deploy the Appwrite MCP server with a single click, or view the <PinkLink.Anchor
|
||||
href="https://appwrite.io/docs"
|
||||
target="_blank"
|
||||
rel="noreferrer">docs</PinkLink.Anchor> for instructions.
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<Layout.Stack direction="row" gap="s" wrap="wrap" justifyContent="center">
|
||||
{#each mcpTools as tool}
|
||||
<Button.Anchor
|
||||
href={tool.href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
size="s"
|
||||
variant="secondary">
|
||||
<Icon slot="start" icon={tool.icon} size="xs" />
|
||||
{tool.label}
|
||||
</Button.Anchor>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
@@ -56,8 +56,10 @@
|
||||
$effect(() => {
|
||||
domainIsValid; /* silences lint for unused var */
|
||||
|
||||
if (domain !== newDomain) {
|
||||
domainStatus = 'pending';
|
||||
if (newDomain) {
|
||||
if (domain !== newDomain) {
|
||||
domainStatus = 'pending';
|
||||
}
|
||||
checkDomain(newDomain);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
let domain = data.domain;
|
||||
let domainIsValid = true;
|
||||
let framework = data?.template?.frameworks[0];
|
||||
let branch = 'main';
|
||||
let branch = '';
|
||||
let rootDir = './';
|
||||
let connectBehaviour: 'now' | 'later' = 'now';
|
||||
let repositoryBehaviour: 'new' | 'existing' = 'new';
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import { Copy } from '$lib/components';
|
||||
import { LogsRequest, LogsResponse } from '$lib/components/logs';
|
||||
import { site } from '../store';
|
||||
import { timer } from '$lib/actions/timer';
|
||||
|
||||
export let open = false;
|
||||
export let selectedLogId: string;
|
||||
@@ -112,7 +113,12 @@
|
||||
Duration
|
||||
</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
{calculateTime(selectedLog.duration)}
|
||||
{#if ['processing', 'waiting'].includes(selectedLog.status)}
|
||||
<span use:timer={{ start: selectedLog.$createdAt }}
|
||||
></span>
|
||||
{:else}
|
||||
{calculateTime(selectedLog.duration)}
|
||||
{/if}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack gap="xs" inline>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import { calculateTime } from '$lib/helpers/timeConversion';
|
||||
import { getBadgeTypeFromStatusCode } from '$lib/helpers/httpStatus';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { timer } from '$lib/actions/timer';
|
||||
|
||||
export let columns: Column[];
|
||||
export let logs: Models.ExecutionList;
|
||||
@@ -83,7 +84,11 @@
|
||||
{log.requestMethod}
|
||||
</Typography.Code>
|
||||
{:else if column.id === 'duration'}
|
||||
{calculateTime(log.duration)}
|
||||
{#if ['processing', 'waiting'].includes(log.status)}
|
||||
<span use:timer={{ start: log.$createdAt }}></span>
|
||||
{:else}
|
||||
{calculateTime(log.duration)}
|
||||
{/if}
|
||||
{:else if column.id === 'responseStatusCode'}
|
||||
<div>
|
||||
<Badge
|
||||
|
||||
@@ -3,6 +3,10 @@ import type { BarSeriesOption } from 'echarts/charts';
|
||||
import { derived, get, writable } from 'svelte/store';
|
||||
import { regions } from '$lib/stores/organization';
|
||||
import { page } from '$app/stores';
|
||||
import CursorIcon from './overview/components/CursorIcon.svelte';
|
||||
import ClaudeIcon from './overview/components/ClaudeIcon.svelte';
|
||||
import VSCodeIcon from './overview/components/VSCodeIcon.svelte';
|
||||
import WindsurfIcon from './overview/components/WindsurfIcon.svelte';
|
||||
|
||||
export const project = derived(page, ($page) => $page.data.project as Models.Project);
|
||||
|
||||
@@ -73,3 +77,26 @@ function createStats() {
|
||||
}
|
||||
|
||||
export const stats = createStats();
|
||||
|
||||
export const mcpTools = [
|
||||
{
|
||||
href: 'https://apwr.dev/docs-mcp-cursor?ref=docs',
|
||||
icon: CursorIcon,
|
||||
label: 'Cursor'
|
||||
},
|
||||
{
|
||||
href: 'https://appwrite.io/docs/tooling/mcp/claude',
|
||||
icon: ClaudeIcon,
|
||||
label: 'Claude Desktop'
|
||||
},
|
||||
{
|
||||
href: 'https://apwr.dev/docs-mcp-vscode?ref=docs',
|
||||
icon: VSCodeIcon,
|
||||
label: 'VS code'
|
||||
},
|
||||
{
|
||||
href: 'https://appwrite.io/docs/tooling/mcp/windsurf',
|
||||
icon: WindsurfIcon,
|
||||
label: 'Windsurf editor'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -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"
|
||||
}
|
||||