diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..3c1848749 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,9 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) +# and commit this file to your remote git repository to share the goodness with others. + +# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart + +tasks: + - init: npm install && npm run build + command: npm run dev diff --git a/package-lock.json b/package-lock.json index f15f8ca36..fca9095fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "@appwrite/console", "dependencies": { "@analytics/google-analytics": "^1.0.5", + "@analytics/google-tag-manager": "^0.5.3", "@appwrite.io/console": "npm:matej-appwrite-console@7.1.123", "@appwrite.io/pink": "0.0.7-next.0", "@appwrite.io/pink-icons": "0.0.7-next.0", @@ -20,6 +21,7 @@ "logrocket": "^3.0.1", "pretty-bytes": "^6.1.0", "prismjs": "^1.29.0", + "svelte-confetti": "^1.2.2", "tippy.js": "^6.3.7", "web-vitals": "^3.1.1" }, @@ -124,6 +126,11 @@ "resolved": "https://registry.npmjs.org/@analytics/google-analytics/-/google-analytics-1.0.7.tgz", "integrity": "sha512-KZ69NaMIi5kOcouzqI8cu7tZgQl7ziGiRahfU6zniUf32G8bv7wQDh73JFz1NwO6gBPloUc+5BzEoWzScM5Rgw==" }, + "node_modules/@analytics/google-tag-manager": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@analytics/google-tag-manager/-/google-tag-manager-0.5.5.tgz", + "integrity": "sha512-bYTzseT3DRwMhkWr0+zJH6BHdk5FDZv1TqU5dHseh60zf/sl3LlWUbb8Lgl41LAOmA2Qy1frJXr3mIO1Tak8Yg==" + }, "node_modules/@analytics/localstorage-utils": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@analytics/localstorage-utils/-/localstorage-utils-0.1.10.tgz", @@ -818,54 +825,6 @@ "dev": true, "peer": true }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.13.tgz", - "integrity": "sha512-KwqFhxRFMKZINHzCqf8eKxE0XqWlAVPRxwy6rc7CbVFxzUWB2sA/s3hbMZeemPdhN3fKBkqOaFhTbS8xJXYIWQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.13.tgz", - "integrity": "sha512-j7NhycJUoUAG5kAzGf4fPWfd17N6SM3o1X6MlXVqfHvs2buFraCJzos9vbeWjLxOyBKHyPOnuCuipbhvbYtTAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.13.tgz", - "integrity": "sha512-M2eZkRxR6WnWfVELHmv6MUoHbOqnzoTVSIxgtsyhm/NsgmL+uTmag/VVzdXvmahak1I6sOb1K/2movco5ikDJg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/darwin-arm64": { "version": "0.18.13", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.13.tgz", @@ -882,294 +841,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.13.tgz", - "integrity": "sha512-RIrxoKH5Eo+yE5BtaAIMZaiKutPhZjw+j0OCh8WdvKEKJQteacq0myZvBDLU+hOzQOZWJeDnuQ2xgSScKf1Ovw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.13.tgz", - "integrity": "sha512-AfRPhHWmj9jGyLgW/2FkYERKmYR+IjYxf2rtSLmhOrPGFh0KCETFzSjx/JX/HJnvIqHt/DRQD/KAaVsUKoI3Xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.13.tgz", - "integrity": "sha512-pGzWWZJBInhIgdEwzn8VHUBang8UvFKsvjDkeJ2oyY5gZtAM6BaxK0QLCuZY+qoj/nx/lIaItH425rm/hloETA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.13.tgz", - "integrity": "sha512-4iMxLRMCxGyk7lEvkkvrxw4aJeC93YIIrfbBlUJ062kilUUnAiMb81eEkVvCVoh3ON283ans7+OQkuy1uHW+Hw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.13.tgz", - "integrity": "sha512-hCzZbVJEHV7QM77fHPv2qgBcWxgglGFGCxk6KfQx6PsVIdi1u09X7IvgE9QKqm38OpkzaAkPnnPqwRsltvLkIQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.13.tgz", - "integrity": "sha512-I3OKGbynl3AAIO6onXNrup/ttToE6Rv2XYfFgLK/wnr2J+1g+7k4asLrE+n7VMhaqX+BUnyWkCu27rl+62Adug==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.13.tgz", - "integrity": "sha512-8pcKDApAsKc6WW51ZEVidSGwGbebYw2qKnO1VyD8xd6JN0RN6EUXfhXmDk9Vc4/U3Y4AoFTexQewQDJGsBXBpg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.13.tgz", - "integrity": "sha512-6GU+J1PLiVqWx8yoCK4Z0GnfKyCGIH5L2KQipxOtbNPBs+qNDcMJr9euxnyJ6FkRPyMwaSkjejzPSISD9hb+gg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.13.tgz", - "integrity": "sha512-pfn/OGZ8tyR8YCV7MlLl5hAit2cmS+j/ZZg9DdH0uxdCoJpV7+5DbuXrR+es4ayRVKIcfS9TTMCs60vqQDmh+w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.13.tgz", - "integrity": "sha512-aIbhU3LPg0lOSCfVeGHbmGYIqOtW6+yzO+Nfv57YblEK01oj0mFMtvDJlOaeAZ6z0FZ9D13oahi5aIl9JFphGg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.13.tgz", - "integrity": "sha512-Pct1QwF2sp+5LVi4Iu5Y+6JsGaV2Z2vm4O9Dd7XZ5tKYxEHjFtb140fiMcl5HM1iuv6xXO8O1Vrb1iJxHlv8UA==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.13.tgz", - "integrity": "sha512-zTrIP0KzYP7O0+3ZnmzvUKgGtUvf4+piY8PIO3V8/GfmVd3ZyHJGz7Ht0np3P1wz+I8qJ4rjwJKqqEAbIEPngA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.13.tgz", - "integrity": "sha512-I6zs10TZeaHDYoGxENuksxE1sxqZpCp+agYeW039yqFwh3MgVvdmXL5NMveImOC6AtpLvE4xG5ujVic4NWFIDQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.13.tgz", - "integrity": "sha512-W5C5nczhrt1y1xPG5bV+0M12p2vetOGlvs43LH8SopQ3z2AseIROu09VgRqydx5qFN7y9qCbpgHLx0kb0TcW7g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.13.tgz", - "integrity": "sha512-X/xzuw4Hzpo/yq3YsfBbIsipNgmsm8mE/QeWbdGdTTeZ77fjxI2K0KP3AlhZ6gU3zKTw1bKoZTuKLnqcJ537qw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.13.tgz", - "integrity": "sha512-4CGYdRQT/ILd+yLLE5i4VApMPfGE0RPc/wFQhlluDQCK09+b4JDbxzzjpgQqTPrdnP7r5KUtGVGZYclYiPuHrw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.13.tgz", - "integrity": "sha512-D+wKZaRhQI+MUGMH+DbEr4owC2D7XnF+uyGiZk38QbgzLcofFqIOwFs7ELmIeU45CQgfHNy9Q+LKW3cE8g37Kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.13", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.13.tgz", - "integrity": "sha512-iVl6lehAfJS+VmpF3exKpNQ8b0eucf5VWfzR8S7xFve64NBNz2jPUgx1X93/kfnkfgP737O+i1k54SVQS7uVZA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -7961,6 +7632,11 @@ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0" } }, + "node_modules/svelte-confetti": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/svelte-confetti/-/svelte-confetti-1.2.4.tgz", + "integrity": "sha512-2ut7fEjfHIQ5foXuBaVwalUdl7JlyhgEkFbepGYpOx2dOGC2vjj4kkepcYr/Fl3+CyqfnVmXre1owM+3rpfJEg==" + }, "node_modules/svelte-hmr": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.2.tgz", diff --git a/package.json b/package.json index 40df41388..b05151048 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@appwrite.io/console": "npm:matej-appwrite-console@7.1.123", "@appwrite.io/pink": "0.0.7-next.0", "@appwrite.io/pink-icons": "0.0.7-next.0", + "@analytics/google-tag-manager": "^0.5.3", "@popperjs/core": "^2.11.6", "@sentry/svelte": "^7.44.2", "@sentry/tracing": "^7.44.2", @@ -32,6 +33,7 @@ "logrocket": "^3.0.1", "pretty-bytes": "^6.1.0", "prismjs": "^1.29.0", + "svelte-confetti": "^1.2.2", "tippy.js": "^6.3.7", "web-vitals": "^3.1.1" }, diff --git a/src/app.html b/src/app.html index f1072de6d..6339817a5 100644 --- a/src/app.html +++ b/src/app.html @@ -43,6 +43,8 @@ + + %sveltekit.head% diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index d841b8be9..a1b1be307 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -1,8 +1,9 @@ import { page } from '$app/stores'; import { user } from '$lib/stores/user'; -import { ENV, MODE, VARS } from '$lib/system'; +import { ENV, MODE, VARS, isCloud } from '$lib/system'; import googleAnalytics from '@analytics/google-analytics'; import { AppwriteException } from '@appwrite.io/console'; +import googleTagManager from '@analytics/google-tag-manager'; import Analytics from 'analytics'; import { get } from 'svelte/store'; @@ -11,7 +12,14 @@ const analytics = Analytics({ plugins: [ googleAnalytics({ measurementIds: [VARS.GOOGLE_ANALYTICS || 'G-R4YJ9JN8L4'] - }) + }), + ...(isCloud + ? [ + googleTagManager({ + containerId: [VARS.GOOGLE_TAG || 'GTM-P3T9TBV'] + }) + ] + : []) ] }); @@ -118,6 +126,7 @@ export enum Submit { UserCreate = 'submit_user_create', UserDelete = 'submit_user_delete', UserUpdateEmail = 'submit_user_update_email', + UserUpdateLabels = 'submit_user_update_labels', UserUpdateName = 'submit_user_update_name', UserUpdatePassword = 'submit_user_update_password', UserUpdatePhone = 'submit_user_update_phone', @@ -131,6 +140,7 @@ export enum Submit { ProjectCreate = 'submit_project_create', ProjectDelete = 'submit_project_delete', ProjectUpdateName = 'submit_project_update_name', + ProjectUpdateTeam = 'submit_project_update_team', ProjectService = 'submit_project_service', MemberCreate = 'submit_member_create', MemberDelete = 'submit_member_delete', diff --git a/src/lib/components/cardGrid.svelte b/src/lib/components/cardGrid.svelte index 2215cc6dc..748900081 100644 --- a/src/lib/components/cardGrid.svelte +++ b/src/lib/components/cardGrid.svelte @@ -19,3 +19,10 @@ {/if} + + diff --git a/src/lib/components/clickableListItem.svelte b/src/lib/components/clickableListItem.svelte index 00308d47a..78a4dbe08 100644 --- a/src/lib/components/clickableListItem.svelte +++ b/src/lib/components/clickableListItem.svelte @@ -4,11 +4,15 @@
  • -
    - -
    -
    -

    -
    + {#if $$slots.default} +
    + +
    + {/if} + {#if $$slots.desc} +
    +

    +
    + {/if}
  • diff --git a/src/lib/components/drop.svelte b/src/lib/components/drop.svelte index 600700a36..1396714c4 100644 --- a/src/lib/components/drop.svelte +++ b/src/lib/components/drop.svelte @@ -14,7 +14,9 @@ export let fullWidth = false; export let fixed = false; - const dispatch = createEventDispatcher(); + const dispatch = createEventDispatcher<{ + blur: undefined; + }>(); let element: HTMLDivElement; let tooltip: HTMLDivElement; diff --git a/src/lib/components/empty.svelte b/src/lib/components/empty.svelte index 2c01f69fa..9dc0c6103 100644 --- a/src/lib/components/empty.svelte +++ b/src/lib/components/empty.svelte @@ -21,7 +21,8 @@ {#if single}
    -
    +
    diff --git a/src/lib/components/feedback/feedbackGeneral.svelte b/src/lib/components/feedback/feedbackGeneral.svelte new file mode 100644 index 000000000..183921a56 --- /dev/null +++ b/src/lib/components/feedback/feedbackGeneral.svelte @@ -0,0 +1,19 @@ + + + + + + + diff --git a/src/lib/components/feedback/feedbackNPS.svelte b/src/lib/components/feedback/feedbackNPS.svelte new file mode 100644 index 000000000..613e10514 --- /dev/null +++ b/src/lib/components/feedback/feedbackNPS.svelte @@ -0,0 +1,30 @@ + + + + How likely are you to recommend Appwrite to a friend or colleague? + +{#if $feedbackData.value} + + + + + +{/if} diff --git a/src/lib/components/feedback/index.ts b/src/lib/components/feedback/index.ts new file mode 100644 index 000000000..15d522683 --- /dev/null +++ b/src/lib/components/feedback/index.ts @@ -0,0 +1,3 @@ +export { default as Feedback } from './feedback.svelte'; +export { default as FeedbackGeneral } from './feedbackGeneral.svelte'; +export { default as FeedbackNPS } from './feedbackNPS.svelte'; diff --git a/src/lib/components/feedbackNPS.svelte b/src/lib/components/feedbackNPS.svelte deleted file mode 100644 index a9276b4a7..000000000 --- a/src/lib/components/feedbackNPS.svelte +++ /dev/null @@ -1,78 +0,0 @@ - - -
    -
    -

    How are we doing?

    - -
    -
    - At Appwrite, we value the feedback of our users. That means you! We'd love to hear what you - think. -
    - - - - How likely are you to recommend Appwrite to a friend or colleague? - - {#if value} - - - - - - {/if} -
    - - -
    - -
    diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index e5cb2d80f..9af31124a 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -31,9 +31,6 @@ export { default as Box } from './box.svelte'; export { default as Search } from './search.svelte'; export { default as SearchQuery } from './searchQuery.svelte'; export { default as GridItem1 } from './gridItem1.svelte'; -export { default as FeedbackGeneral } from './feedbackGeneral.svelte'; -export { default as FeedbackNPS } from './feedbackNPS.svelte'; -export { default as Evaluation } from './evaluation.svelte'; export { default as Steps } from './steps.svelte'; export { default as Step } from './step.svelte'; export { default as Code } from './code.svelte'; diff --git a/src/lib/components/permissions/actions.svelte b/src/lib/components/permissions/actions.svelte index e85deb2fd..d3c7e91cc 100644 --- a/src/lib/components/permissions/actions.svelte +++ b/src/lib/components/permissions/actions.svelte @@ -20,7 +20,7 @@ } - + dispatch('create', ['any'])}> diff --git a/src/lib/components/permissions/permissions.svelte b/src/lib/components/permissions/permissions.svelte index 03a71451f..ec55d1edf 100644 --- a/src/lib/components/permissions/permissions.svelte +++ b/src/lib/components/permissions/permissions.svelte @@ -12,7 +12,6 @@ {#if [...$groups]?.length} - - - Role - {#if withCreate} - Create - {/if} - Read - Update - Delete - - - - {#each [...$groups].sort(sortRoles) as [role, permission] (role)} - - - - +
    +
    + + Role + {#if withCreate} + Create + {/if} + Read + Update + Delete + + + + {#each [...$groups].sort(sortRoles) as [role, permission] (role)} + + + + - {#if withCreate} - + {#if withCreate} + + togglePermission(role, 'create')} /> + + {/if} + togglePermission(role, 'create')} /> + aria-label="Read" + checked={permission.read} + on:change={() => togglePermission(role, 'read')} /> + + + togglePermission(role, 'update')} /> + + + togglePermission(role, 'delete')} /> - {/if} - - togglePermission(role, 'read')} /> - - - togglePermission(role, 'update')} /> - - - togglePermission(role, 'delete')} /> - - -
    - -
    -
    -
    - {/each} -
    -
    + +
    + +
    +
    + + {/each} + + +
    + import { goto } from '$app/navigation'; import { trackEvent } from '$lib/actions/analytics'; + import { last } from '$lib/helpers/array'; + import { getElementDir } from '$lib/helpers/style'; + import { waitUntil } from '$lib/helpers/waitUntil'; export let selected = false; export let href: string = null; export let event: string = null; - function track() { - if (!event) return; - trackEvent(`click_select_tab_${event}`); + async function handleClick(e: Event) { + if (event) { + trackEvent(`click_select_tab_${event}`); + } + + if (href) { + let el = e.target as HTMLElement; + if (el.tagName !== 'A') { + el = el.closest('a'); + } + + await goto(href); + await waitUntil(() => { + console.log('tickUntil', el); + return el.classList.contains('is-selected'); + }, 1000); + el.focus(); + } + } + + const keysMap = { + ltr: { + next: 'ArrowRight', + prev: 'ArrowLeft' + }, + rtl: { + next: 'ArrowLeft', + prev: 'ArrowRight' + } + }; + + function handleKeyDown(e: KeyboardEvent) { + const tabBtn = e.target as HTMLElement; + const tabItem = tabBtn.closest('.tabs-item') as HTMLElement; + const tabsList = tabItem.closest('.tabs') as HTMLElement; + const tabItems = Array.from(tabsList.querySelectorAll('.tabs-item')); + const currentIdx = tabItems.indexOf(tabItem); + + const dir = getElementDir(tabsList); + + switch (e.key) { + case 'Home': { + e.preventDefault(); + const firstTabBtn = tabItems[0].querySelector('.tabs-button') as HTMLElement; + firstTabBtn.focus(); + break; + } + case 'End': { + e.preventDefault(); + const lastTabBtn = last(tabItems).querySelector('.tabs-button') as HTMLElement; + lastTabBtn.focus(); + break; + } + case keysMap[dir].next: { + e.preventDefault(); + const nextIdx = currentIdx === tabItems.length - 1 ? 0 : currentIdx + 1; + const nextTabBtn = tabItems[nextIdx].querySelector('.tabs-button') as HTMLElement; + nextTabBtn.focus(); + break; + } + case keysMap[dir].prev: { + e.preventDefault(); + const prevIdx = currentIdx === 0 ? tabItems.length - 1 : currentIdx - 1; + const prevTabBtn = tabItems[prevIdx].querySelector('.tabs-button') as HTMLElement; + prevTabBtn.focus(); + break; + } + } }
  • {#if href} - + {:else} @@ -22,7 +98,10 @@ class="tabs-button" class:is-selected={selected} on:click|preventDefault - on:click={track}> + on:click={handleClick} + tabindex={selected ? 0 : -1} + on:keydown={handleKeyDown} + role="tab"> {/if} diff --git a/src/lib/components/viewSelector.svelte b/src/lib/components/viewSelector.svelte index 82d7d499b..7ac403079 100644 --- a/src/lib/components/viewSelector.svelte +++ b/src/lib/components/viewSelector.svelte @@ -84,7 +84,7 @@ class="icon-view-boards u-opacity-50" aria-hidden="true" aria-label="columns" /> - Columns + Columns {selectedColumnsNumber} diff --git a/src/lib/elements/forms/button.svelte b/src/lib/elements/forms/button.svelte index 8ac92c1d8..e23f584e4 100644 --- a/src/lib/elements/forms/button.svelte +++ b/src/lib/elements/forms/button.svelte @@ -6,6 +6,7 @@ export let submit = false; export let secondary = false; + export let github = false; export let text = false; export let danger = false; export let disabled = false; @@ -44,6 +45,7 @@ class:is-disabled={disabled} class:is-only-icon={round} class:is-secondary={secondary} + class:is-github={github} class:is-text={text} class:is-danger={danger} class:is-full-width={fullWidth} @@ -59,6 +61,7 @@ class="button" class:is-only-icon={round} class:is-secondary={secondary} + class:is-github={github} class:is-danger={danger} class:is-text={text} class:is-full-width={fullWidth} @@ -68,3 +71,17 @@ {/if} + + diff --git a/src/lib/elements/forms/formItem.svelte b/src/lib/elements/forms/formItem.svelte index d1301d5d6..3e08ea271 100644 --- a/src/lib/elements/forms/formItem.svelte +++ b/src/lib/elements/forms/formItem.svelte @@ -1,3 +1,7 @@ -
  • + + +
  • diff --git a/src/lib/elements/forms/inputFile.svelte b/src/lib/elements/forms/inputFile.svelte index 0c73f3dc2..e85d755e2 100644 --- a/src/lib/elements/forms/inputFile.svelte +++ b/src/lib/elements/forms/inputFile.svelte @@ -1,11 +1,12 @@ `.${n}`).join(',')} type="file" @@ -123,7 +152,7 @@ {fileSize.value + fileSize.unit} - {#if $feedback.type === 'nps'} - - {:else} - - {/if} + + transition:slideFade={{ duration: 150 }}> {#if $organizationList?.total}
      @@ -139,7 +141,7 @@ (showDropdown = false)}> - Your Account + Your account - Sign Out + Sign out
    @@ -200,9 +202,81 @@ + {#if isCloud} +
    + + Claim your Cloud card + +
    + {/if}
    {/if} {/if} + + diff --git a/src/lib/layout/navigation.svelte b/src/lib/layout/navigation.svelte index 15790203b..96892c26b 100644 --- a/src/lib/layout/navigation.svelte +++ b/src/lib/layout/navigation.svelte @@ -1,10 +1,15 @@ @@ -122,6 +136,24 @@ Storage +
  • + trackEvent('click_menu_settings')} + class:is-selected={$page.url.pathname.startsWith( + `${projectPath}/settings` + )} + title="Settings" + use:tooltip={{ + content: 'Settings', + placement: 'right', + disabled: !narrow + }}> + +
  • @@ -129,7 +161,7 @@ {/if} diff --git a/src/lib/layout/unauthenticated.svelte b/src/lib/layout/unauthenticated.svelte index e2470d309..3f4ebff5f 100644 --- a/src/lib/layout/unauthenticated.svelte +++ b/src/lib/layout/unauthenticated.svelte @@ -1,10 +1,17 @@ -
    -
    - -
    +
    +

    Cloud is Live

    + + +
    -
    -
    - {#if $app.themeInUse === 'dark'} - - {:else} - - {/if} -
    +

    Now in public beta

    -
    +
    + +
    +

    Integrate with your favourite technologies

    +
      + {#each technologies as tech} +
    • + +
    • + {/each} +
    +
    +
    +
    + {:else} +
    +
    + + Appwrite + +
    -
    -
    -

    Integrate with your favourite technologies

    -
      - {#each technologies as tech} -
    • - -
    • - {/each} -
    -
    -
    -
    -
    -
    +
    +
    + {#if $app.themeInUse === 'dark'} + + {:else} + + {/if} +
    + +
    + +
    +
    +

    Integrate with your favourite technologies

    +
      + {#each technologies as tech} +
    • + +
    • + {/each} +
    +
    +
    +
    + {/if} +
    +
    + {#if isCloud} + + +
    +
    Public beta
    +
    + {/if} +
    -

    -
    +

    + {#if isCloud} + + + {/if} + +

    +
    @@ -83,12 +178,181 @@
    - -
    -

    - -

    -
    + + diff --git a/src/lib/layout/usage.svelte b/src/lib/layout/usage.svelte index f4240c357..3e2b5c872 100644 --- a/src/lib/layout/usage.svelte +++ b/src/lib/layout/usage.svelte @@ -13,9 +13,8 @@ @@ -72,76 +62,6 @@ {/if} - - - {#if created} - {total(created)} -

    {createdMetadata.title}

    -
    -
    - [e.date, e.value])], - color: Colors.Secondary - } - ]} /> -
    - {/if} - - - {#if read} - {total(read)} -

    {readMetadata.title}

    -
    -
    - [e.date, e.value])], - color: Colors.Tertiary - } - ]} /> -
    - {/if} - - - {#if updated} - {total(updated)} -

    {updatedMetadata.title}

    -
    -
    - [e.date, e.value])], - color: Colors.Quaternary - } - ]} /> -
    - {/if} - - - {#if deleted} - {total(deleted)} -

    {deletedMetadata.title}

    -
    -
    - [e.date, e.value])], - color: Colors.Quinary - } - ]} /> -
    - {/if} - - diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index de3611aee..6273f45a4 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -31,7 +31,9 @@ export const load: LayoutLoad = async ({ depends, url }) => { '/invite', '/auth/magic-url', '/auth/oauth2/success', - '/auth/oauth2/failure' + '/auth/oauth2/failure', + '/card', + '/hackathon' ]; if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) { diff --git a/src/routes/auth/oauth2/failure/+page.svelte b/src/routes/auth/oauth2/failure/+page.svelte index 359c6ffdc..928ad82c4 100644 --- a/src/routes/auth/oauth2/failure/+page.svelte +++ b/src/routes/auth/oauth2/failure/+page.svelte @@ -2,20 +2,56 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import { Heading } from '$lib/components'; - import { onMount } from 'svelte'; - onMount(async () => { - const project = $page.url.searchParams.get('project'); - if (project) { - await goto(`appwrite-callback-${project}://${$page.url.search}`); + const project = $page.url.searchParams.get('project'); + const link = `appwrite-callback-${project}://${$page.url.search}`; + + const redirect = new Promise((resolve, reject) => { + if (!project) { + reject('no-project'); } + // this timeout is needed because goto does not + // throw an exception if the redirect does not work + setTimeout(() => reject('timeout'), 500); + // goto will resolve on successful redirect + goto(link).then(resolve); }); -Missing Redirect URL -

    - Your OAuth login flow is missing a proper redirect URL. Please check the - OAuth docs - and send request for new session with a valid callback URL. -

    +{#await redirect then} +
    +
    + Login failed +

    You will be automatically redirected back to your app shortly.

    +

    + If you are not redirected, please click on the following + link. +

    +
    +
    +{:catch} +
    +
    + Missing Redirect URL +

    + Your OAuth login flow is missing a proper redirect URL. Please check the + OAuth docs + and send request for new session with a valid callback URL. +

    +
    +
    +{/await} + + diff --git a/src/routes/auth/oauth2/success/+page.svelte b/src/routes/auth/oauth2/success/+page.svelte index 359c6ffdc..b67ad0ce6 100644 --- a/src/routes/auth/oauth2/success/+page.svelte +++ b/src/routes/auth/oauth2/success/+page.svelte @@ -2,20 +2,56 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import { Heading } from '$lib/components'; - import { onMount } from 'svelte'; - onMount(async () => { - const project = $page.url.searchParams.get('project'); - if (project) { - await goto(`appwrite-callback-${project}://${$page.url.search}`); + const project = $page.url.searchParams.get('project'); + const link = `appwrite-callback-${project}://${$page.url.search}`; + + const redirect = new Promise((resolve, reject) => { + if (!project) { + reject('no-project'); } + // this timeout is needed because goto does not + // throw an exception if the redirect does not work + setTimeout(() => reject('timeout'), 500); + // goto will resolve on successful redirect + goto(link).then(resolve); }); -Missing Redirect URL -

    - Your OAuth login flow is missing a proper redirect URL. Please check the - OAuth docs - and send request for new session with a valid callback URL. -

    +{#await redirect then} +
    +
    + You're now logged in +

    You will be automatically redirected back to your app shortly.

    +

    + If you are not redirected, please click on the following + link. +

    +
    +
    +{:catch} +
    +
    + Missing Redirect URL +

    + Your OAuth login flow is missing a proper redirect URL. Please check the + OAuth docs + and send request for new session with a valid callback URL. +

    +
    +
    +{/await} + + diff --git a/src/routes/card/+layout.svelte b/src/routes/card/+layout.svelte new file mode 100644 index 000000000..9a0263a42 --- /dev/null +++ b/src/routes/card/+layout.svelte @@ -0,0 +1,13 @@ + + + + + +
    + diff --git a/src/routes/card/+page.ts b/src/routes/card/+page.ts new file mode 100644 index 000000000..209eeb5aa --- /dev/null +++ b/src/routes/card/+page.ts @@ -0,0 +1,10 @@ +import { isCloud } from '$lib/system'; +import { redirect } from '@sveltejs/kit'; + +export async function load({ parent }) { + if (!isCloud) throw redirect(303, '/'); + const { account } = await parent(); + if (!account) throw redirect(303, '/login'); + + throw redirect(303, `/card/${account.$id}`); +} diff --git a/src/routes/card/Card.svelte b/src/routes/card/Card.svelte new file mode 100644 index 000000000..d89854f82 --- /dev/null +++ b/src/routes/card/Card.svelte @@ -0,0 +1,458 @@ + + + + + + +
    +
    + +
    +
    + + diff --git a/src/routes/card/[uid]/+page.svelte b/src/routes/card/[uid]/+page.svelte new file mode 100644 index 000000000..dc6332a5c --- /dev/null +++ b/src/routes/card/[uid]/+page.svelte @@ -0,0 +1,430 @@ + + + + + {seo.title} + + + +
    +
    +

    + {title} + +

    + +
    +
    + {#if variant === 'owner'} +

    Share your card

    + {:else if variant === 'external'} +

    Share card

    + {/if} + {#if variant === 'external'} + Claim your card + {/if} +
    +
    + The front of the card +
      +
    • + + +
    • + {#if variant === 'owner'} +
    • + +
    • + {/if} +
    • + +
    • +
    + +
    +
    + {#if !cardActive} +
    + +
    + {/if} + {#if browser} + + {/if} +
    + {#if cardActive} +
    (cardActive = false)} + on:keydown={() => { + /* no-op */ + }} + transition:fade|local /> + {/if} +
    + +
    +
    + + + Get Embed Code +
    + +
    + + + +
    + + diff --git a/src/routes/card/[uid]/+page.ts b/src/routes/card/[uid]/+page.ts new file mode 100644 index 000000000..83535bd2e --- /dev/null +++ b/src/routes/card/[uid]/+page.ts @@ -0,0 +1,58 @@ +import { VARS } from '$lib/system.js'; +import { redirect } from '@sveltejs/kit'; + +function getCardImgUrls(userId: string, endpoint: string) { + const resolved = endpoint; + + const frontImg = `${resolved}/cards/cloud?userId=${userId}`; + const backImg = `${resolved}/cards/cloud-back?userId=${userId}`; + const ogImg = `${resolved}/cards/cloud-og?userId=${userId}`; + + return { frontImg, backImg, ogImg }; +} + +async function urlContentToDataUri(url: string): Promise { + const res = await fetch(url); + const blob = await res.blob(); + + return await new Promise((callback) => { + const reader = new FileReader(); + reader.onload = function () { + if (typeof this.result !== 'string') return ''; + callback(this.result); + }; + reader.readAsDataURL(blob); + }); +} + +export async function load({ params, url, parent }) { + const { account } = await parent(); + + const userId = params.uid; + const endpoint = VARS.APPWRITE_ENDPOINT ?? `${url.origin}/v1`; + const { frontImg, backImg } = getCardImgUrls(userId, endpoint); + + try { + const res = await fetch(frontImg); + + if (!res.ok) { + throw redirect(303, '/'); + } + + return { + userId: params.uid, + base64: { + front: await urlContentToDataUri(frontImg), + back: await urlContentToDataUri(backImg) + }, + isOwner: account && account.$id === userId, + imgUrls: { + frontImg, + backImg + } + }; + } catch (e) { + console.error(e); + throw redirect(303, '/'); + } +} diff --git a/src/routes/card/helpers.ts b/src/routes/card/helpers.ts new file mode 100644 index 000000000..e59c305f9 --- /dev/null +++ b/src/routes/card/helpers.ts @@ -0,0 +1,9 @@ +export function getCardImgUrls(userId: string, endpoint: string) { + const resolved = endpoint; + + const frontImg = `${resolved}/cards/cloud?userId=${userId}`; + const backImg = `${resolved}/cards/cloud-back?userId=${userId}`; + const ogImg = `${resolved}/cards/cloud-og?userId=${userId}`; + + return { frontImg, backImg, ogImg }; +} diff --git a/src/routes/console/+layout.svelte b/src/routes/console/+layout.svelte index 17df1e1f2..6d059085b 100644 --- a/src/routes/console/+layout.svelte +++ b/src/routes/console/+layout.svelte @@ -6,7 +6,7 @@ import Header from '$lib/layout/header.svelte'; import SideNavigation from '$lib/layout/navigation.svelte'; import Shell from '$lib/layout/shell.svelte'; - import { feedback } from '$lib/stores/app'; + import { feedback } from '$lib/stores/feedback'; import { log } from '$lib/stores/logs'; import { newOrgModal } from '$lib/stores/organization'; import { wizard } from '$lib/stores/wizard'; @@ -14,6 +14,8 @@ import { loading } from '../store'; import Create from './createOrganization.svelte'; + let isOpen = false; + onMount(() => { loading.set(false); @@ -42,12 +44,14 @@
    - +
    diff --git a/src/routes/console/+layout.ts b/src/routes/console/+layout.ts index 3b0cff2ff..275e475d2 100644 --- a/src/routes/console/+layout.ts +++ b/src/routes/console/+layout.ts @@ -2,7 +2,12 @@ import { sdk } from '$lib/stores/sdk'; import type { LayoutLoad } from './$types'; export const load: LayoutLoad = async ({ fetch }) => { - const response = await fetch(`${sdk.forConsole.client.config.endpoint}/health/version`); + const { endpoint, project } = sdk.forConsole.client.config; + const response = await fetch(`${endpoint}/health/version`, { + headers: { + 'X-Appwrite-Project': project + } + }); const data = await response.json(); return { diff --git a/src/routes/console/account/organizations/+page.ts b/src/routes/console/account/organizations/+page.ts index f3cef11bb..0b89b1d30 100644 --- a/src/routes/console/account/organizations/+page.ts +++ b/src/routes/console/account/organizations/+page.ts @@ -15,7 +15,7 @@ export const load: PageLoad = async ({ url, route }) => { organizations: await sdk.forConsole.teams.list([ Query.offset(offset), Query.limit(limit), - Query.orderDesc('$createdAt') + Query.orderDesc('') ]) }; }; diff --git a/src/routes/console/feedbackWizard.svelte b/src/routes/console/feedbackWizard.svelte new file mode 100644 index 000000000..fd55199c3 --- /dev/null +++ b/src/routes/console/feedbackWizard.svelte @@ -0,0 +1,25 @@ + + + diff --git a/src/routes/console/organization-[organization]/+page.svelte b/src/routes/console/organization-[organization]/+page.svelte index d039ac7d3..2b5d3759b 100644 --- a/src/routes/console/organization-[organization]/+page.svelte +++ b/src/routes/console/organization-[organization]/+page.svelte @@ -8,6 +8,8 @@ import CreateOrganization from '../createOrganization.svelte'; import type { PageData } from './$types'; import { page } from '$app/stores'; + import { services } from '$lib/stores/project-services'; + import type { Models } from '@appwrite.io/console'; export let data: PageData; @@ -38,6 +40,17 @@ return { name, icon }; }; + function allServiceDisabled(project: Models.Project): boolean { + let disabled = true; + services.load(project); + $services.list.forEach((service) => { + if (service.value) { + disabled = false; + } + }); + return disabled; + } + function filterPlatforms(platforms: { name: string; icon: string }[]) { return platforms.filter( (value, index, self) => index === self.findIndex((t) => t.name === value.name) @@ -67,6 +80,11 @@ {project.name} + {#if allServiceDisabled(project)} +

    +

    + {/if} {@const platforms = filterPlatforms( project.platforms.map((platform) => getPlatformInfo(platform.type)) )} diff --git a/src/routes/console/organization-[organization]/+page.ts b/src/routes/console/organization-[organization]/+page.ts index 279a55084..fab93a7b4 100644 --- a/src/routes/console/organization-[organization]/+page.ts +++ b/src/routes/console/organization-[organization]/+page.ts @@ -14,9 +14,9 @@ export const load: PageLoad = async ({ params, url, route }) => { limit, projects: await sdk.forConsole.projects.list([ Query.offset(offset), - Query.limit(limit), Query.equal('teamId', params.organization), - Query.orderDesc('$createdAt') + Query.limit(CARD_LIMIT), + Query.orderDesc('') ]) }; }; diff --git a/src/routes/console/project-[project]/auth/+page.svelte b/src/routes/console/project-[project]/auth/+page.svelte index 0063f7ea1..2dee0dbfb 100644 --- a/src/routes/console/project-[project]/auth/+page.svelte +++ b/src/routes/console/project-[project]/auth/+page.svelte @@ -19,7 +19,7 @@ TableRowLink } from '$lib/elements/table'; import { Pill } from '$lib/elements'; - import { toLocaleDateTime } from '$lib/helpers/date'; + import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date'; import { Container } from '$lib/layout'; import { base } from '$app/paths'; import { goto } from '$app/navigation'; @@ -29,6 +29,13 @@ export let data: PageData; + // TODO: Remove this when the console SDK is updated + const users = data.users.users.map((user) => { + const labels: string[] = []; + const accessedAt = ''; + return { accessedAt, labels, ...user }; + }); + let showCreate = false; const projectId = $page.params.project; async function userCreated(event: CustomEvent>>) { @@ -49,10 +56,12 @@ Identifiers Status ID + Labels Joined + Last Activity - {#each data.users.users as user} + {#each users as user} @@ -102,9 +111,15 @@ + + {user.labels.join(', ')} + {toLocaleDateTime(user.registration)} + + {user.accessedAt ? toLocaleDate(user.accessedAt) : 'never'} + {/each} diff --git a/src/routes/console/project-[project]/auth/+page.ts b/src/routes/console/project-[project]/auth/+page.ts index 97b6ad3d3..11d793706 100644 --- a/src/routes/console/project-[project]/auth/+page.ts +++ b/src/routes/console/project-[project]/auth/+page.ts @@ -16,7 +16,7 @@ export const load: PageLoad = async ({ url, route }) => { search, page, users: await sdk.forProject.users.list( - [Query.limit(limit), Query.offset(offset), Query.orderDesc('$createdAt')], + [Query.limit(limit), Query.offset(offset), Query.orderDesc('')], search ) }; diff --git a/src/routes/console/project-[project]/auth/appleOAuth.svelte b/src/routes/console/project-[project]/auth/appleOAuth.svelte index 88b48040c..850ae6721 100644 --- a/src/routes/console/project-[project]/auth/appleOAuth.svelte +++ b/src/routes/console/project-[project]/auth/appleOAuth.svelte @@ -63,9 +63,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/auth0OAuth.svelte b/src/routes/console/project-[project]/auth/auth0OAuth.svelte index b8e0fa35c..52e46a680 100644 --- a/src/routes/console/project-[project]/auth/auth0OAuth.svelte +++ b/src/routes/console/project-[project]/auth/auth0OAuth.svelte @@ -72,9 +72,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/authentikOAuth.svelte b/src/routes/console/project-[project]/auth/authentikOAuth.svelte index 350fac230..e204be4e0 100644 --- a/src/routes/console/project-[project]/auth/authentikOAuth.svelte +++ b/src/routes/console/project-[project]/auth/authentikOAuth.svelte @@ -72,9 +72,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/gitlabOAuth.svelte b/src/routes/console/project-[project]/auth/gitlabOAuth.svelte index 934d73801..510e4a5c6 100644 --- a/src/routes/console/project-[project]/auth/gitlabOAuth.svelte +++ b/src/routes/console/project-[project]/auth/gitlabOAuth.svelte @@ -70,9 +70,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/googleOAuth.svelte b/src/routes/console/project-[project]/auth/googleOAuth.svelte new file mode 100644 index 000000000..c8fd0390b --- /dev/null +++ b/src/routes/console/project-[project]/auth/googleOAuth.svelte @@ -0,0 +1,82 @@ + + + + {provider.name} OAuth2 Settings + +

    + To use {provider.name} authentication in your application, first fill in this form. For more + info you can + visit the docs. +

    + + + + + To complete the setup, create an OAuth2 client ID with "Web application" as the + application type, then add this redirect URI to your {provider.name} configuration. + +
    +

    URI

    + +
    +
    + + + + +
    diff --git a/src/routes/console/project-[project]/auth/mainOAuth.svelte b/src/routes/console/project-[project]/auth/mainOAuth.svelte index 684e8ff81..36d0aa89f 100644 --- a/src/routes/console/project-[project]/auth/mainOAuth.svelte +++ b/src/routes/console/project-[project]/auth/mainOAuth.svelte @@ -63,9 +63,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/microsoftOAuth.svelte b/src/routes/console/project-[project]/auth/microsoftOAuth.svelte index e404fec84..10925962a 100644 --- a/src/routes/console/project-[project]/auth/microsoftOAuth.svelte +++ b/src/routes/console/project-[project]/auth/microsoftOAuth.svelte @@ -72,9 +72,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/oidcOAuth.svelte b/src/routes/console/project-[project]/auth/oidcOAuth.svelte new file mode 100644 index 000000000..6fade4f7f --- /dev/null +++ b/src/routes/console/project-[project]/auth/oidcOAuth.svelte @@ -0,0 +1,125 @@ + + + + {provider.name} OAuth2 Settings + +

    + To use {provider.name} authentication in your application, first fill in this form. For more + info you can + visit the docs. +

    + + + + + + + + + + To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration. + +
    +

    URI

    + +
    +
    + + + + +
    diff --git a/src/routes/console/project-[project]/auth/oktaOAuth.svelte b/src/routes/console/project-[project]/auth/oktaOAuth.svelte index 98f607df1..548a99576 100644 --- a/src/routes/console/project-[project]/auth/oktaOAuth.svelte +++ b/src/routes/console/project-[project]/auth/oktaOAuth.svelte @@ -82,9 +82,7 @@

    URI

    + value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
    diff --git a/src/routes/console/project-[project]/auth/settings/+page.svelte b/src/routes/console/project-[project]/auth/settings/+page.svelte index eba59cea4..afacc54eb 100644 --- a/src/routes/console/project-[project]/auth/settings/+page.svelte +++ b/src/routes/console/project-[project]/auth/settings/+page.svelte @@ -1,17 +1,17 @@ diff --git a/src/routes/console/project-[project]/auth/usage/[[period]]/+page.ts b/src/routes/console/project-[project]/auth/usage/[[period]]/+page.ts index 6ba6fec31..b4a930690 100644 --- a/src/routes/console/project-[project]/auth/usage/[[period]]/+page.ts +++ b/src/routes/console/project-[project]/auth/usage/[[period]]/+page.ts @@ -8,11 +8,7 @@ export const load: PageLoad = async ({ params }) => { try { const response = await sdk.forProject.users.getUsage(period ?? '30d'); return { - count: response.usersCount as unknown as Models.Metric[], - created: response.usersCreate as unknown as Models.Metric[], - read: response.usersRead as unknown as Models.Metric[], - updated: response.usersUpdate as unknown as Models.Metric[], - deleted: response.usersDelete as unknown as Models.Metric[] + usersTotal: response.usersTotal as unknown as Models.Metric[] }; } catch (e) { throw error(e.code, e.message); diff --git a/src/routes/console/project-[project]/auth/user-[user]/+page.svelte b/src/routes/console/project-[project]/auth/user-[user]/+page.svelte index b48becb71..04ef2f14a 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/+page.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/+page.svelte @@ -2,6 +2,7 @@ import { Container } from '$lib/layout'; import DangerZone from './dangerZone.svelte'; import UpdateEmail from './updateEmail.svelte'; + import UpdateLabels from './updateLabels.svelte'; import UpdateName from './updateName.svelte'; import UpdatePassword from './updatePassword.svelte'; import UpdatePhone from './updatePhone.svelte'; @@ -15,6 +16,7 @@ + diff --git a/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte b/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte index 8fa2ff05d..a04d1cfc0 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte @@ -1,10 +1,14 @@ @@ -42,6 +46,7 @@ ? [$user.email, $user.phone].join(',') : $user.email || $user.phone}

    +

    Last activity: {accessedAt ? toLocaleDate(accessedAt) : 'never'}

    diff --git a/src/routes/console/project-[project]/auth/user-[user]/sessions/+page.svelte b/src/routes/console/project-[project]/auth/user-[user]/sessions/+page.svelte index 29aef6a74..eaa1e1cc2 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/sessions/+page.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/sessions/+page.svelte @@ -1,16 +1,16 @@ + +
    + + Labels +

    + Categorize and manage your users based on specific criteria by assigning them + customizable labels. New label-based roles will be assigned. +

    + +
      + +
    • + {error ? error : 'Only alphanumeric characters are allowed'} +
    • +
    • + {#each suggestedLabels as suggestedLabel} + { + if (!labels.includes(suggestedLabel)) { + labels = [...labels, suggestedLabel]; + } else { + labels = labels.filter((e) => e !== suggestedLabel); + } + }}> + + {/each} +
    • +
    +
    + + + + +
    +
    diff --git a/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte b/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte index 044408912..0045cb093 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte @@ -33,9 +33,7 @@ Password
    -

    - Enter a new password. A password must contain at least 8 characters. -

    +

    Enter a new password. A password must contain at least 8 characters.

      Phone +

      + Update user's phone. A phone number must contain leading '+' and maximum of 15 digits. +

        - User Preferences -

        - You can update your user preferences by storing information on the user's objects so - they can easily be shared across devices and sessions. -

        + Preferences +

        Add custom user preferences to share them across devices and sessions.

          diff --git a/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte b/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte index 60a2189d1..80fe968c8 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte @@ -5,7 +5,7 @@ import { Dependencies } from '$lib/constants'; import { Pill } from '$lib/elements'; import { Button } from '$lib/elements/forms'; - import { toLocaleDateTime } from '$lib/helpers/date'; + import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date'; import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; import { user } from './store'; @@ -71,6 +71,9 @@ trackError(error, Submit.UserUpdateStatus); } } + + // TODO: Remove this when the console SDK is updated + $: accessedAt = ($user as unknown as { accessedAt: string }).accessedAt; @@ -100,6 +103,7 @@

          {$user.phone}

          {/if}

          Joined: {toLocaleDateTime($user.registration)}

          +

          Last activity: {accessedAt ? toLocaleDate(accessedAt) : 'never'}

    {#if !$user.status} blocked diff --git a/src/routes/console/project-[project]/databases/+page.svelte b/src/routes/console/project-[project]/databases/+page.svelte index 0d0e1e553..490fdb8d1 100644 --- a/src/routes/console/project-[project]/databases/+page.svelte +++ b/src/routes/console/project-[project]/databases/+page.svelte @@ -24,7 +24,12 @@ - +
    - {attribute.key} + {attribute.key}
    {#if attribute.status !== 'available'} Delete Document -

    - Are you sure you want to delete the document from {$collection.name}? -

    - {#if relAttributes?.length} - +

    This document contains the following relationships:

    + - Relation - Setting + Relation + Setting @@ -91,7 +86,7 @@ {:else} {/if} - {attr.key} + {attr.key} @@ -111,7 +106,14 @@ Delete document from {$collection.name}
    + {:else} +

    + Are you sure you want to delete the document from {$collection.name}? +

    {/if} + diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte index c2a85e9a5..2c0f7faa7 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte @@ -65,7 +65,7 @@ trackEvent(Submit.DocumentUpdate); disableUpdate = true; addNotification({ - message: 'Document was updated!', + message: 'Document has been updated', type: 'success' }); } catch (error) { diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/header.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/header.svelte index 51f5f0a45..2fe829bef 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/header.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/header.svelte @@ -51,7 +51,9 @@ {$collection?.name} - {$collection?.$id} + {#key $collection?.$id} + {$collection?.$id} + {/key} diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte index 3bf7b6776..344598a7a 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte @@ -77,7 +77,7 @@ ); addNotification({ - message: 'Index has been created', + message: 'Creating index', type: 'success' }); trackEvent(Submit.IndexCreate); diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/relationshipsModal.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/relationshipsModal.svelte index f2576dd6a..a0917e087 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/relationshipsModal.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/relationshipsModal.svelte @@ -12,7 +12,10 @@ const databaseId = $page.params.database; const limit = 10; - $: args = preferences.getDisplayNames()?.[selectedRelationship?.relatedCollection] ?? []; + $: args = + preferences + .getDisplayNames() + ?.[selectedRelationship?.relatedCollection]?.filter((p) => p !== '$id') ?? []; $: if (!show) { data = null; diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/displayName.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/displayName.svelte index 3a5f76957..ae13e4629 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/displayName.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/displayName.svelte @@ -37,10 +37,7 @@ $: options = ($attributes as Models.AttributeString[]) .filter( (attr) => - attr.type === 'string' && - attr?.size <= 50 && - !attr?.array && - !names?.some((name) => name === attr.key) + attr.type === 'string' && !attr?.array && !names?.some((name) => name === attr.key) ) .map((attr) => { return { @@ -60,19 +57,28 @@ Display Name

    - Set string attributes with maximum 50 characters to be used as a display name in the - Appwrite console. Maximum 5 names. + Select string attributes as display names for your documents. The selected names will be + used as short forms to identify documents in the Appwrite console, like when creating + database relationships. You can specify up to 5 names.

    -