Merge branch 'main' of https://github.com/appwrite/appwrite-console-poc into feat-hover-list

This commit is contained in:
Torsten Dittmann
2022-11-10 10:36:14 +01:00
240 changed files with 3129 additions and 3203 deletions
+1 -1
View File
@@ -147,7 +147,7 @@ Adding a new dependency should have vital value on the product with minimum poss
│ │ │ │ ├── _create.svelte // Component to Create collections
│ │ │ │ └── index.svelte // Entrypoint for "/console/[PROJECT_ID]/database"
│ │ │ ├── storage // Storage Service "/console/[PROJECT]/storage"
│ │ │ └── authentication // Users Service "/console/[PROJECT]/authentication"
│ │ │ └── auth // Users Service "/console/[PROJECT]/auth"
│ │ └──...
│ ├── login.svelte // Component for Login "/console/login"
│ └── register.svelte // Component for Register "/console/register"
+38 -504
View File
@@ -8,11 +8,12 @@
"name": "@appwrite/console",
"version": "0.0.1",
"dependencies": {
"@aw-labs/appwrite-console": "^6.0.0",
"@aw-labs/icons": "0.0.0-66",
"@aw-labs/ui": "0.0.0-66",
"@aw-labs/appwrite-console": "^10.0.0",
"@aw-labs/icons": "0.0.0-72",
"@aw-labs/ui": "0.0.0-72",
"@popperjs/core": "^2.11.6",
"echarts": "^5.4.0",
"pretty-bytes": "^6.0.0",
"prismjs": "^1.29.0",
"tippy.js": "^6.3.7",
"web-vitals": "^3.0.3"
@@ -71,23 +72,23 @@
}
},
"node_modules/@aw-labs/appwrite-console": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@aw-labs/appwrite-console/-/appwrite-console-6.0.0.tgz",
"integrity": "sha512-g4sCMGiIptDUyKhJSfiKsG/gzqMIt3MQCdqod6bNWMunbXUqN7DeA0BT4MuTeVGdQuoOKuSmgHeExatlu0Dh9Q==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@aw-labs/appwrite-console/-/appwrite-console-10.0.0.tgz",
"integrity": "sha512-YyEQ5SIhkvI/Axt/EOkAqKcMTkfEpQt7W1q91lWKQ+Hm9woMzYUcf8KThBnMvEFbphWQRaJKLtfpr8EJjCjm4w==",
"dependencies": {
"cross-fetch": "3.1.5",
"isomorphic-form-data": "2.0.0"
}
},
"node_modules/@aw-labs/icons": {
"version": "0.0.0-66",
"resolved": "https://registry.npmjs.org/@aw-labs/icons/-/icons-0.0.0-66.tgz",
"integrity": "sha512-3r6pxixYZ6Bn9QTUbFk8XYwds+CHz1baRkOVsQ5HoFz6XgvzTWn3/+pvZZ56Ht2Ek//wr4wwI30pdCXaX4QIsA=="
"version": "0.0.0-72",
"resolved": "https://registry.npmjs.org/@aw-labs/icons/-/icons-0.0.0-72.tgz",
"integrity": "sha512-z3k9ex4oKdE/VUPeqc6lpo2HuhC39Dov2NEXv/1v6YBw2l20LavNA6m7NU/DFkq+m286un/dp6w7ORbgpZlIvw=="
},
"node_modules/@aw-labs/ui": {
"version": "0.0.0-66",
"resolved": "https://registry.npmjs.org/@aw-labs/ui/-/ui-0.0.0-66.tgz",
"integrity": "sha512-5qFISwKzOyZq7Ix5FG/BhVKEcIMXd2/Y8Ioba+ejTQ8B9hzj6FlwxUw7hLT49ZDhU+kxpjJQV51hRpbHGvmEag==",
"version": "0.0.0-72",
"resolved": "https://registry.npmjs.org/@aw-labs/ui/-/ui-0.0.0-72.tgz",
"integrity": "sha512-+NR8p3fRMVj/RMPmyVJPcdaRwYkWNw7jJ/wxT4zjYxa7QeeMJwILYfYIFYnnu0GG9SN9nOc2z+yISqL0p0HgfA==",
"dependencies": {
"@aw-labs/icons": "*",
"normalize.css": "^8.0.1",
@@ -721,38 +722,6 @@
"dev": true,
"peer": true
},
"node_modules/@esbuild/android-arm": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.10.tgz",
"integrity": "sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz",
"integrity": "sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@eslint/eslintrc": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
@@ -2899,54 +2868,6 @@
"esbuild-windows-arm64": "0.15.10"
}
},
"node_modules/esbuild-android-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz",
"integrity": "sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz",
"integrity": "sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz",
"integrity": "sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz",
@@ -2963,262 +2884,6 @@
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz",
"integrity": "sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz",
"integrity": "sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz",
"integrity": "sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz",
"integrity": "sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz",
"integrity": "sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz",
"integrity": "sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz",
"integrity": "sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz",
"integrity": "sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz",
"integrity": "sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz",
"integrity": "sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz",
"integrity": "sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz",
"integrity": "sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz",
"integrity": "sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz",
"integrity": "sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz",
"integrity": "sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz",
"integrity": "sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -6278,6 +5943,17 @@
"svelte": "^3.2.0"
}
},
"node_modules/pretty-bytes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.0.0.tgz",
"integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==",
"engines": {
"node": "^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
@@ -7782,23 +7458,23 @@
}
},
"@aw-labs/appwrite-console": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@aw-labs/appwrite-console/-/appwrite-console-6.0.0.tgz",
"integrity": "sha512-g4sCMGiIptDUyKhJSfiKsG/gzqMIt3MQCdqod6bNWMunbXUqN7DeA0BT4MuTeVGdQuoOKuSmgHeExatlu0Dh9Q==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@aw-labs/appwrite-console/-/appwrite-console-10.0.0.tgz",
"integrity": "sha512-YyEQ5SIhkvI/Axt/EOkAqKcMTkfEpQt7W1q91lWKQ+Hm9woMzYUcf8KThBnMvEFbphWQRaJKLtfpr8EJjCjm4w==",
"requires": {
"cross-fetch": "3.1.5",
"isomorphic-form-data": "2.0.0"
}
},
"@aw-labs/icons": {
"version": "0.0.0-66",
"resolved": "https://registry.npmjs.org/@aw-labs/icons/-/icons-0.0.0-66.tgz",
"integrity": "sha512-3r6pxixYZ6Bn9QTUbFk8XYwds+CHz1baRkOVsQ5HoFz6XgvzTWn3/+pvZZ56Ht2Ek//wr4wwI30pdCXaX4QIsA=="
"version": "0.0.0-72",
"resolved": "https://registry.npmjs.org/@aw-labs/icons/-/icons-0.0.0-72.tgz",
"integrity": "sha512-z3k9ex4oKdE/VUPeqc6lpo2HuhC39Dov2NEXv/1v6YBw2l20LavNA6m7NU/DFkq+m286un/dp6w7ORbgpZlIvw=="
},
"@aw-labs/ui": {
"version": "0.0.0-66",
"resolved": "https://registry.npmjs.org/@aw-labs/ui/-/ui-0.0.0-66.tgz",
"integrity": "sha512-5qFISwKzOyZq7Ix5FG/BhVKEcIMXd2/Y8Ioba+ejTQ8B9hzj6FlwxUw7hLT49ZDhU+kxpjJQV51hRpbHGvmEag==",
"version": "0.0.0-72",
"resolved": "https://registry.npmjs.org/@aw-labs/ui/-/ui-0.0.0-72.tgz",
"integrity": "sha512-+NR8p3fRMVj/RMPmyVJPcdaRwYkWNw7jJ/wxT4zjYxa7QeeMJwILYfYIFYnnu0GG9SN9nOc2z+yISqL0p0HgfA==",
"requires": {
"@aw-labs/icons": "*",
"normalize.css": "^8.0.1",
@@ -8291,20 +7967,6 @@
"dev": true,
"peer": true
},
"@esbuild/android-arm": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.10.tgz",
"integrity": "sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz",
"integrity": "sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==",
"dev": true,
"optional": true
},
"@eslint/eslintrc": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
@@ -9991,27 +9653,6 @@
"esbuild-windows-arm64": "0.15.10"
}
},
"esbuild-android-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz",
"integrity": "sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz",
"integrity": "sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz",
"integrity": "sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz",
@@ -10019,118 +9660,6 @@
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz",
"integrity": "sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz",
"integrity": "sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz",
"integrity": "sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz",
"integrity": "sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz",
"integrity": "sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz",
"integrity": "sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz",
"integrity": "sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz",
"integrity": "sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz",
"integrity": "sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz",
"integrity": "sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz",
"integrity": "sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz",
"integrity": "sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz",
"integrity": "sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz",
"integrity": "sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz",
"integrity": "sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.15.10",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz",
"integrity": "sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==",
"dev": true,
"optional": true
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -12460,6 +11989,11 @@
"dev": true,
"requires": {}
},
"pretty-bytes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.0.0.tgz",
"integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg=="
},
"pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+4 -3
View File
@@ -19,11 +19,12 @@
"e2e": "playwright test tests/e2e"
},
"dependencies": {
"@aw-labs/appwrite-console": "^6.0.0",
"@aw-labs/icons": "0.0.0-66",
"@aw-labs/ui": "0.0.0-66",
"@aw-labs/appwrite-console": "^10.0.0",
"@aw-labs/icons": "0.0.0-72",
"@aw-labs/ui": "0.0.0-72",
"@popperjs/core": "^2.11.6",
"echarts": "^5.4.0",
"pretty-bytes": "^6.0.0",
"prismjs": "^1.29.0",
"tippy.js": "^6.3.7",
"web-vitals": "^3.0.3"
+3
View File
@@ -1,11 +1,14 @@
<script lang="ts">
import type { EChartsOption } from 'echarts';
import type { BarSeriesOption } from 'echarts/charts';
import Base from './base.svelte';
export let series: BarSeriesOption[];
export let options: EChartsOption = null;
</script>
<Base
{options}
series={series.map((s) => {
s.type = 'bar';
s.stack = 'total';
+1 -1
View File
@@ -6,7 +6,7 @@
"borderWidth": 1
},
"lineStyle": {
"width": 2
"width": 1
},
"symbolSize": 4,
"symbol": "emptyCircle",
+6 -2
View File
@@ -8,10 +8,12 @@
import dark from './dark.json';
import type { ECharts } from 'echarts/core';
import type { BarSeriesOption, LineSeriesOption } from 'echarts/charts';
import type { EChartsOption } from 'echarts';
registerTheme('light', { ...base, ...light });
registerTheme('dark', { ...base, ...dark });
export let options: EChartsOption;
export let series: (BarSeriesOption | LineSeriesOption)[];
let chart: ECharts;
@@ -19,6 +21,7 @@
$: option = {
...defaultConfig,
...options,
series
};
@@ -39,7 +42,7 @@
(await import('echarts/components')).TransformComponent,
(await import('echarts/components')).LegendComponent,
(await import('echarts/features')).LabelLayout,
(await import('echarts/renderers')).CanvasRenderer
(await import('echarts/renderers')).SVGRenderer
]);
makeChart();
});
@@ -85,6 +88,7 @@
<style>
.echart {
width: 100%;
min-height: 12rem;
min-height: 10rem;
height: 100%;
}
</style>
+8 -3
View File
@@ -17,8 +17,8 @@ export const defaultConfig: EChartsOption = {
containLabel: true,
left: 0,
right: 0,
bottom: 15,
top: 15
bottom: 0,
top: 45
},
xAxis: {
type: 'time',
@@ -27,6 +27,11 @@ export const defaultConfig: EChartsOption = {
yAxis: {
type: 'value',
splitNumber: 3,
minInterval: 1
minInterval: 1,
splitLine: {
lineStyle: {
width: 1
}
}
}
};
+2 -2
View File
@@ -45,11 +45,11 @@
}
},
"axisLabel": {
"color": "#666666"
"color": "#868EA3"
},
"splitLine": {
"lineStyle": {
"color": ["#333333"]
"color": ["#4F5769"]
}
},
"splitArea": {
+1 -1
View File
@@ -34,7 +34,7 @@
},
"splitLine": {
"lineStyle": {
"color": ["#E0E6F1"]
"color": ["#E8E9F0"]
}
}
},
+10
View File
@@ -1,14 +1,24 @@
<script lang="ts">
import type { LineSeriesOption } from 'echarts/charts';
import type { EChartsOption } from 'echarts';
import { Colors } from './config';
import Base from './base.svelte';
export let series: LineSeriesOption[];
export let options: EChartsOption = null;
</script>
<Base
{options}
series={series.map((s) => {
s.type = 'line';
s.stack = 'total';
s.lineStyle = {
shadowBlur: 38,
shadowColor: Colors.Primary,
shadowOffsetY: 15,
shadowOffsetX: 0
};
s.showSymbol = false;
return s;
+2 -1
View File
@@ -1,7 +1,8 @@
<script lang="ts">
export let isTile = false;
export let isDashed = false;
</script>
<article class="card" class:common-section={!isTile}>
<article class="card" class:is-border-dashed={isDashed} class:common-section={!isTile}>
<slot />
</article>
+2 -2
View File
@@ -12,8 +12,8 @@
style={`--grid-gap:1.5rem; --grid-item-size:${total > 3 ? '22rem' : '25rem'};`}>
<slot />
{#if limit + offset > total && (total % 2 !== 0 || total % 4 === 0)}
<Empty isButton on:click>
{#if total > 3 ? total < limit + offset : total % 2 !== 0}
<Empty on:click>
<slot name="empty" />
</Empty>
{/if}
+4 -5
View File
@@ -10,7 +10,6 @@
import 'prismjs/plugins/autoloader/prism-autoloader';
import 'prismjs/plugins/custom-class/prism-custom-class';
import 'prismjs/plugins/line-numbers/prism-line-numbers';
import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
import { afterUpdate } from 'svelte';
import { Copy } from '.';
@@ -49,16 +48,16 @@
{/if}
</div>
<pre class={`language-${language}`} class:line-numbers={withLineNumbers}>
<code>{code}</code>
</pre>
<pre class={`language-${language}`} class:line-numbers={withLineNumbers}><code
>{code}</code></pre>
</section>
<style lang="scss" global>
@import 'prismjs/themes/prism.css';
@import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
.box {
--p-box-background-color: var(--color-neutral-300) !important;
--p-box-background-color: var(--color-neutral-400) !important;
body.theme-light & {
--p-box-background-color: var(--color-neutral-5) !important;
+13 -3
View File
@@ -1,13 +1,23 @@
<script lang="ts">
export let withIndentation = false;
</script>
<li class="collapsible-item">
<details class="collapsible-wrapper">
<summary class="collapsible-button">
<span class="text"><slot name="title" /></span>
<span class="collapsible-button-optional"><slot name="subtitle" /></span>
<slot name="beforetitle" />
<div>
<span class="text"><slot name="title" /></span>
<span class="collapsible-button-optional"><slot name="subtitle" /></span>
</div>
<div class="icon">
<span class="icon-cheveron-down" aria-hidden="true" />
</div>
</summary>
<div class="collapsible-content">
<div
class="collapsible-content"
class:u-margin-block-start-8={withIndentation}
class:u-padding-inline-32={withIndentation}>
<slot />
</div>
</details>
+16 -14
View File
@@ -22,21 +22,23 @@
};
</script>
<div class="input-text-wrapper is-with-end-button">
<div>
<label class:u-hide={!showLabel} class="label" for={label}>{label}</label>
<div class="input-text-wrapper">
<div class="input-text-wrapper" style="--amount-of-buttons:1">
<input {value} id={label} type="text" class="input-text" readonly />
<button
type="button"
class="input-button"
aria-label="Click to copy."
on:click={copy}
on:mouseenter={() => setTimeout(() => (content = 'Click to copy'))}
use:tooltip={{
content,
hideOnClick: false
}}>
<span class="icon-duplicate" aria-hidden="true" />
</button>
<div class="options-list">
<button
type="button"
class="input-button"
aria-label="Click to copy."
on:click={copy}
on:mouseenter={() => setTimeout(() => (content = 'Click to copy'))}
use:tooltip={{
content,
hideOnClick: false
}}>
<span class="icon-duplicate" aria-hidden="true" />
</button>
</div>
</div>
</div>
+2 -2
View File
@@ -5,9 +5,9 @@
<li class="drop-tabs-item">
{#if disabled}
<span class="drop-tabs-button is-disabled">
<button class="drop-tabs-button" disabled>
<span class="text"><slot /></span>
</span>
</button>
{:else}
<a class="drop-tabs-button" {href}>
<span class="text"><slot /></span>
+33 -41
View File
@@ -3,52 +3,44 @@
import { Button } from '$lib/elements/forms';
import EmptyLight from '$lib/images/empty-light.svg';
import EmptyDark from '$lib/images/empty-dark.svg';
import { Heading } from '.';
export let isButton = false;
export let single = false;
export let target: string = null;
export let href: string = null;
</script>
{#if isButton}
{#if single}
<article class="card u-grid u-cross-center u-width-full-line common-section">
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
<button type="button" on:click|preventDefault>
{#if $app.themeInUse === 'dark'}
<img src={EmptyDark} alt="create" aria-hidden="true" />
{:else}
<img src={EmptyLight} alt="create" aria-hidden="true" />
{/if}
</button>
<slot>
<div class="u-text-center">
<Heading size="7" tag="h4">Create your first {target} to get started.</Heading>
<p class="body-text-2 u-margin-block-start-4">
Need a hand? Check out our documentation.
</p>
</div>
<div class="u-flex u-gap-16 u-main-center">
<Button external {href} text>Documentation</Button>
<Button secondary on:click>Create {target}</Button>
</div>
</slot>
</div>
</article>
{:else}
<button
on:click|preventDefault
type="button"
class="card u-grid u-cross-center u-width-full-line dashed"
class:common-section={single}>
{#if single}
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
{#if $app.themeInUse === 'dark'}
<img src={EmptyDark} alt="create" aria-hidden="true" />
{:else}
<img src={EmptyLight} alt="create" aria-hidden="true" />
{/if}
<slot />
</div>
{:else}
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<div class="common-section">
<Button secondary round>
<i class="icon-plus" />
</Button>
</div>
<div class="common-section">
<slot />
</div>
</div>
{/if}
</button>
{:else}
<article class="card u-grid u-cross-center u-width-full-line" class:common-section={single}>
{#if single}
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
{#if $app.themeInUse === 'dark'}
<img src={EmptyDark} alt="create" aria-hidden="true" />
{:else}
<img src={EmptyLight} alt="create" aria-hidden="true" />
{/if}
<slot />
</div>
{:else}
<div class="common-section u-main-center u-flex">
class="card u-grid u-cross-center u-width-full-line dashed">
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<div class="common-section">
<Button secondary round>
<i class="icon-plus" />
</Button>
@@ -56,6 +48,6 @@
<div class="common-section">
<slot />
</div>
{/if}
</article>
</div>
</button>
{/if}
+10 -6
View File
@@ -3,20 +3,24 @@
import Light from '$lib/images/search-light.svg';
import Dark from '$lib/images/search-dark.svg';
import PaginationInline from './paginationInline.svelte';
export let hidePages = false;
</script>
<article class="card u-grid u-cross-center u-width-full-line common-section">
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
{#if $app.themeInUse === 'dark'}
<img src={Dark} alt="create" aria-hidden="true" />
{:else}
<img src={Light} alt="create" aria-hidden="true" />
{/if}
<button type="button" on:click|preventDefault>
{#if $app.themeInUse === 'dark'}
<img src={Dark} alt="create" aria-hidden="true" />
{:else}
<img src={Light} alt="create" aria-hidden="true" />
{/if}
</button>
<slot />
</div>
</article>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: 0</p>
<PaginationInline limit={1} offset={0} sum={0} />
<PaginationInline limit={1} offset={0} sum={0} {hidePages} />
</div>
+27 -21
View File
@@ -1,27 +1,33 @@
<script>
import { FormItem } from '$lib/elements/forms';
export let show = false;
</script>
{#if show}
<section class="modal is-inner-modal">
<header class="modal-header u-flex-wrap">
<h4 class="modal-title body-text-1">
<slot name="title" />
</h4>
<button
type="button"
class="x-button"
aria-label="Close"
title="Close"
on:click={() => (show = false)}>
<span class="icon-x" aria-hidden="true" />
</button>
<p class="u-flex-basis-100-percent">
<slot name="subtitle" />
</p>
</header>
<div class="modal-content">
<slot name="content" />
</div>
</section>
<FormItem>
<section class="modal is-inner-modal">
<div class="modal-form">
<header class="modal-header u-flex-wrap">
<h4 class="modal-title body-text-1">
<slot name="title" />
</h4>
<button
type="button"
class="x-button"
aria-label="Close"
title="Close"
on:click={() => (show = false)}>
<span class="icon-x" aria-hidden="true" />
</button>
<p class="u-flex-basis-100-percent">
<slot name="subtitle" />
</p>
</header>
<div class="modal-content">
<slot name="content" />
</div>
</div>
</section>
</FormItem>
{/if}
+58 -48
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import { Alert } from '$lib/components';
export let show = false;
@@ -13,6 +13,14 @@
const dispatch = createEventDispatcher();
onMount(() => {
if (show) openModal();
});
onDestroy(() => {
if (show) closeModal();
});
function handleBLur(event: MouseEvent) {
if (event.target === dialog) {
closeModal();
@@ -21,7 +29,7 @@
function openModal() {
if (dialog && !dialog.open) {
dialog.showModal();
document.body.classList.add('u-overflow-hidden');
document.documentElement.classList.add('u-overflow-hidden');
}
}
@@ -31,7 +39,7 @@
dispatch('close');
dialog.close();
show = false;
document.body.classList.remove('u-overflow-hidden');
document.documentElement.classList.remove('u-overflow-hidden');
}
}
}
@@ -61,50 +69,52 @@
class:is-small={size === 'small'}
class:is-big={size === 'big'}
bind:this={dialog}>
<!-- svelte-ignore a11y-no-redundant-roles -->
<form class="modal-form" role="form" on:submit|preventDefault>
<header class="modal-header">
{#if warning}
<div class="avatar is-color-orange is-medium">
<span class="icon-exclamation" aria-hidden="true" />
</div>
{/if}
<h4 class="modal-title heading-level-5">
<slot name="header" />
</h4>
{#if closable}
<button
type="button"
class="x-button"
aria-label="Close Modal"
title="Close Modal"
on:click={closeModal}>
<span class="icon-x" aria-hidden="true" />
</button>
{/if}
</header>
<div class="modal-content">
{#if error}
<div bind:this={alert}>
<Alert
dismissible
type="warning"
on:dismiss={() => {
error = null;
}}>
{error}
</Alert>
</div>
{/if}
<slot />
</div>
{#if $$slots.footer}
<div class="modal-footer">
<div class="u-flex u-main-end u-gap-16">
<slot name="footer" />
</div>
{#if show}
<!-- svelte-ignore a11y-no-redundant-roles -->
<form class="modal-form" role="form" on:submit|preventDefault>
<header class="modal-header">
{#if warning}
<div class="avatar is-color-orange is-medium">
<span class="icon-exclamation" aria-hidden="true" />
</div>
{/if}
<h4 class="modal-title heading-level-5">
<slot name="header" />
</h4>
{#if closable}
<button
type="button"
class="x-button"
aria-label="Close Modal"
title="Close Modal"
on:click={closeModal}>
<span class="icon-x" aria-hidden="true" />
</button>
{/if}
</header>
<div class="modal-content">
{#if error}
<div bind:this={alert}>
<Alert
dismissible
type="warning"
on:dismiss={() => {
error = null;
}}>
{error}
</Alert>
</div>
{/if}
<slot />
</div>
{/if}
</form>
{#if $$slots.footer}
<div class="modal-footer">
<div class="u-flex u-main-end u-gap-16">
<slot name="footer" />
</div>
</div>
{/if}
</form>
{/if}
</dialog>
@@ -1,6 +1,5 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { Button } from '$lib/elements/forms';
import { DropList, DropListItem } from '..';
import Custom from './custom.svelte';
import Team from './team.svelte';
@@ -22,10 +21,7 @@
</script>
<DropList bind:show={showDropdown} placement="bottom-end">
<Button text noMargin on:click={() => (showDropdown = !showDropdown)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">Add role</span>
</Button>
<slot />
<svelte:fragment slot="list">
<DropListItem disabled={$groups.has('any')} on:click={() => dispatch('create', ['any'])}>
Any
+1
View File
@@ -1,3 +1,4 @@
export { default as Permissions } from './permissions.svelte';
export { default as Roles } from './roles.svelte';
export { default as Team } from './team.svelte';
export { default as User } from './user.svelte';
@@ -10,26 +10,23 @@
</script>
<script lang="ts">
import { Button } from '$lib/elements/forms';
import {
Table,
TableBody,
TableCell,
TableCellHead,
TableCellText,
TableHeader,
TableRow
} from '$lib/elements/table';
import { difference } from '$lib/helpers/array';
import { symmetricDifference } from '$lib/helpers/array';
import { onDestroy, onMount } from 'svelte';
import { writable, type Unsubscriber } from 'svelte/store';
import { Empty } from '../';
import Actions from './actions.svelte';
import Row from './row.svelte';
export let withCreate = false;
export let withCrud = true;
export let permissions: string[] = [];
export let executeAccess: string[] = [];
let showUser = false;
let showTeam = false;
@@ -43,10 +40,7 @@
permissions.forEach(fromPermissionString);
unsubscribe = groups.subscribe(() => {
const current = exportRoles();
if (
difference(current, permissions).length ||
difference(permissions, current).length
) {
if (symmetricDifference(current, permissions).length) {
permissions = current;
}
});
@@ -145,76 +139,61 @@
return a.localeCompare(b);
}
$: if (executeAccess.length && !permissions.length) {
executeAccess.forEach((permission) => {
addRole(permission);
});
}
$: if ([...$groups]?.length && !permissions.length) {
executeAccess = [...$groups].sort(sortRoles).map(([role]) => role);
}
//TODO: click on Empty components opens the dropdown
</script>
{#if [...$groups]?.length}
<Table noMargin noStyles noMobile>
<TableHeader>
<TableCellHead>Role</TableCellHead>
{#if withCrud}
{#if withCreate}
<TableCellHead width={70}>Create</TableCellHead>
{/if}
<TableCellHead width={70}>Read</TableCellHead>
<TableCellHead width={70}>Update</TableCellHead>
<TableCellHead width={70}>Delete</TableCellHead>
{#if withCreate}
<TableCellHead width={70}>Create</TableCellHead>
{/if}
<TableCellHead width={70}>Read</TableCellHead>
<TableCellHead width={70}>Update</TableCellHead>
<TableCellHead width={70}>Delete</TableCellHead>
<TableCellHead width={40} />
</TableHeader>
<TableBody>
{#each [...$groups].sort(sortRoles) as [role, permission]}
<TableRow>
<TableCellText title="Role">
<TableCell title="Role">
<Row {role} />
</TableCellText>
</TableCell>
{#if withCrud}
{#if withCreate}
<TableCell title="Create">
<input
type="checkbox"
class="icon-check"
aria-label="Create"
checked={permission.create}
on:change={() => togglePermission(role, 'create')} />
</TableCell>
{/if}
<TableCell title="Read">
{#if withCreate}
<TableCell title="Create">
<input
type="checkbox"
class="icon-check"
aria-label="Read"
checked={permission.read}
on:change={() => togglePermission(role, 'read')} />
</TableCell>
<TableCell title="Update">
<input
type="checkbox"
class="icon-check"
aria-label="Update"
checked={permission.update}
on:change={() => togglePermission(role, 'update')} />
</TableCell>
<TableCell title="Delete">
<input
type="checkbox"
class="icon-check"
aria-label="Delete"
checked={permission.delete}
on:change={() => togglePermission(role, 'delete')} />
aria-label="Create"
checked={permission.create}
on:change={() => togglePermission(role, 'create')} />
</TableCell>
{/if}
<TableCell title="Read">
<input
type="checkbox"
class="icon-check"
aria-label="Read"
checked={permission.read}
on:change={() => togglePermission(role, 'read')} />
</TableCell>
<TableCell title="Update">
<input
type="checkbox"
class="icon-check"
aria-label="Update"
checked={permission.update}
on:change={() => togglePermission(role, 'update')} />
</TableCell>
<TableCell title="Delete">
<input
type="checkbox"
class="icon-check"
aria-label="Delete"
checked={permission.delete}
on:change={() => togglePermission(role, 'delete')} />
</TableCell>
<TableCell title="Remove" width={40}>
<div class="u-flex">
@@ -231,18 +210,38 @@
{/each}
</TableBody>
</Table>
<Actions
bind:showCustom
bind:showDropdown
bind:showTeam
bind:showUser
{groups}
on:create={create}>
<Button text noMargin on:click={() => (showDropdown = !showDropdown)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">Add role</span>
</Button>
</Actions>
{:else}
<span class="eyebrow-heading-3 u-sep-block-end">ROLE</span>
<Empty isButton on:click={() => (showDropdown = !showDropdown)}>
Add a role to get started
</Empty>
<article class="card u-grid u-cross-center u-width-full-line dashed">
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<div class="common-section">
<Actions
bind:showCustom
bind:showDropdown
bind:showTeam
bind:showUser
{groups}
on:create={create}>
<Button secondary round on:click={() => (showDropdown = !showDropdown)}>
<i class="icon-plus" />
</Button>
</Actions>
</div>
<div class="common-section">
<span class="text"> Add a role to get started </span>
</div>
</div>
</article>
{/if}
<Actions
bind:showCustom
bind:showDropdown
bind:showTeam
bind:showUser
{groups}
on:create={create} />
+83 -36
View File
@@ -1,13 +1,15 @@
<script lang="ts">
import { Button } from '$lib/elements/forms';
import {
Table,
TableBody,
TableCell,
TableCellHead,
TableCellText,
TableHeader,
TableRow
} from '$lib/elements/table';
import { difference } from '$lib/helpers/array';
import { symmetricDifference } from '$lib/helpers/array';
import { onDestroy, onMount } from 'svelte';
import { writable, type Unsubscriber } from 'svelte/store';
import Actions from './actions.svelte';
@@ -28,7 +30,7 @@
roles.forEach(addRole);
unsubscribe = groups.subscribe((n) => {
const current = Array.from(n.keys());
if (difference(current, roles).length || difference(roles, current).length) {
if (symmetricDifference(current, roles).length) {
roles = current;
}
});
@@ -69,39 +71,84 @@
return n;
});
}
function sortRoles(a: string, b: string) {
if ((a === 'any') !== (b === 'any')) {
return a === 'any' ? -1 : 1;
}
if ((a === 'users') !== (b === 'users')) {
return a === 'users' ? -1 : 1;
}
if ((a === 'guests') !== (b === 'guests')) {
return a === 'guests' ? -1 : 1;
}
return a.localeCompare(b);
}
$: if (roles) {
roles.forEach(addRole);
}
</script>
<Table noMargin noStyles noMobile>
<TableHeader>
<TableCellHead>Role</TableCellHead>
<TableCellHead width={40} />
</TableHeader>
<TableBody>
{#each [...$groups.keys()] as role}
<TableRow>
<TableCellText title="Role">
<Row {role} />
</TableCellText>
<TableCellText title="Remove">
<div class="u-flex">
<button
class="button is-text is-only-icon"
type="button"
aria-label="delete"
on:click={() => deleteRole(role)}>
<span class="icon-x" aria-hidden="true" />
</button>
</div>
</TableCellText>
</TableRow>
{/each}
</TableBody>
</Table>
<Actions
bind:showCustom
bind:showDropdown
bind:showTeam
bind:showUser
{groups}
on:create={create} />
{#if [...$groups.keys()]?.length}
<Table noMargin noStyles noMobile>
<TableHeader>
<TableCellHead>Role</TableCellHead>
<TableCellHead width={40} />
</TableHeader>
<TableBody>
{#each [...$groups.keys()].sort(sortRoles) as role}
<TableRow>
<TableCell title="Role">
<Row {role} />
</TableCell>
<TableCellText title="Remove">
<div class="u-flex">
<button
class="button is-text is-only-icon"
type="button"
aria-label="delete"
on:click={() => deleteRole(role)}>
<span class="icon-x" aria-hidden="true" />
</button>
</div>
</TableCellText>
</TableRow>
{/each}
</TableBody>
</Table>
<Actions
bind:showCustom
bind:showDropdown
bind:showTeam
bind:showUser
{groups}
on:create={create}>
<Button text noMargin on:click={() => (showDropdown = !showDropdown)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">Add role</span>
</Button>
</Actions>
{:else}
<article class="card u-grid u-cross-center u-width-full-line dashed">
<div class="u-flex u-cross-center u-flex-vertical u-main-center u-flex">
<div class="common-section">
<Actions
bind:showCustom
bind:showDropdown
bind:showTeam
bind:showUser
{groups}
on:create={create}>
<Button secondary round on:click={() => (showDropdown = !showDropdown)}>
<i class="icon-plus" />
</Button>
</Actions>
</div>
<div class="common-section">
<span class="text"> Add a role to get started </span>
</div>
</div>
</article>
{/if}
+40 -7
View File
@@ -3,7 +3,7 @@
import { createEventDispatcher } from 'svelte';
import { sdkForProject } from '$lib/stores/sdk';
import { Query, type Models } from '@aw-labs/appwrite-console';
import { AvatarInitials, Modal, PaginationInline } from '..';
import { AvatarInitials, EmptySearch, Modal, PaginationInline } from '..';
import type { Writable } from 'svelte/store';
import type { Permission } from './permissions.svelte';
@@ -60,8 +60,8 @@
<Modal bind:show on:submit={create} on:close={reset} size="big">
<svelte:fragment slot="header">Select teams</svelte:fragment>
<InputSearch bind:value={search} />
{#if results?.teams}
<InputSearch autofocus disabled={!results?.teams?.length && !search} bind:value={search} />
{#if results?.teams?.length}
<div class="table-wrapper">
<table class="table is-table-layout-auto is-remove-outer-styles">
<tbody class="table-tbody">
@@ -93,11 +93,44 @@
</tbody>
</table>
</div>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {results?.total}</p>
<PaginationInline limit={5} bind:offset sum={results?.total} hidePages />
</div>
{:else if search}
<EmptySearch hidePages>
<div class="common-section">
<div class="u-text-center common-section">
<b class="body-text-2">Sorry we couldn't find "{search}"</b>
<p>There are no teams that match your search.</p>
</div>
<div class="u-flex u-gap-16 common-section u-main-center">
<Button external href="https://appwrite.io/docs/client/teams" text
>Documentation</Button>
<Button secondary on:click={() => (search = '')}>Clear search</Button>
</div>
</div>
</EmptySearch>
{:else}
<EmptySearch hidePages>
<div class="common-section">
<div class="u-text-center common-section">
<p class="text u-line-height-1-5">
You have no teams. Create a team to see them here.
</p>
<p class="text u-line-height-1-5">
Need a hand? Check out our <a
href="https://appwrite.io/docs/client/teams"
target="_blank"
rel="noopener noreferrer">
documentation</a
>.
</p>
</div>
</div>
</EmptySearch>
{/if}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {results?.total}</p>
<PaginationInline limit={5} bind:offset sum={results?.total} hidePages />
</div>
<svelte:fragment slot="footer">
<Button submit disabled={!hasSelection}>Create</Button>
</svelte:fragment>
+71 -12
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { Button, InputSearch } from '$lib/elements/forms';
import { createEventDispatcher } from 'svelte';
import { AvatarInitials, Modal, PaginationInline } from '..';
import { AvatarInitials, EmptySearch, Modal, PaginationInline } from '..';
import { sdkForProject } from '$lib/stores/sdk';
import { Query, type Models } from '@aw-labs/appwrite-console';
import type { Writable } from 'svelte/store';
@@ -60,8 +60,8 @@
<Modal bind:show on:submit={create} on:close={reset} size="big">
<svelte:fragment slot="header">Select users</svelte:fragment>
<InputSearch bind:value={search} />
{#if results?.users}
<InputSearch autofocus disabled={!results?.users?.length && !search} bind:value={search} />
{#if results?.users?.length}
<div class="table-wrapper">
<table class="table is-table-layout-auto is-remove-outer-styles">
<tbody class="table-tbody">
@@ -81,11 +81,37 @@
</td>
<td class="table-col" data-title="User">
<label class="u-flex u-cross-center u-gap-8" for={user.$id}>
<AvatarInitials size={32} name={user.name} />
<div class="u-line-height-1-5">
<div class="body-text-2">{user.name}</div>
<div class="u-x-small">{user.$id}</div>
</div>
{#if user.email || user.phone}
{#if user.name}
<AvatarInitials size={32} name={user.name} />
<div class="u-line-height-1-5">
<div class="body-text-2">
{user.name}
</div>
<div class="u-x-small">{user.$id}</div>
</div>
{:else}
<div class="avatar is-size-small ">
<span class="icon-minus-sm" aria-hidden="true" />
</div>
<div class="u-line-height-1-5">
<div class="body-text-2">
{user.email ? user.email : user.phone}
</div>
<div class="u-x-small">{user.$id}</div>
</div>
{/if}
{:else}
<div class="avatar is-size-small ">
<span class="icon-anonymous" aria-hidden="true" />
</div>
<div class="u-line-height-1-5">
<div class="body-text-2">
{user.name ? user.name : '-'}
</div>
<div class="u-x-small">{user.$id}</div>
</div>
{/if}
</label>
</td>
</tr>
@@ -93,11 +119,44 @@
</tbody>
</table>
</div>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {results?.total}</p>
<PaginationInline limit={5} bind:offset sum={results?.total} hidePages />
</div>
{:else if search}
<EmptySearch hidePages>
<div class="common-section">
<div class="u-text-center common-section">
<b class="body-text-2">Sorry we couldn't find "{search}"</b>
<p>There are no Users that match your search.</p>
</div>
<div class="u-flex u-gap-16 common-section u-main-center">
<Button external href="https://appwrite.io/docs/server/users" text
>Documentation</Button>
<Button secondary on:click={() => (search = '')}>Clear search</Button>
</div>
</div>
</EmptySearch>
{:else}
<EmptySearch hidePages>
<div class="common-section">
<div class="u-text-center common-section">
<p class="text u-line-height-1-5">
You have no users. Create a user to see them here.
</p>
<p class="text u-line-height-1-5">
Need a hand? Check out our <a
href="https://appwrite.io/docs/server/users"
target="_blank"
rel="noopener noreferrer">
documentation</a
>.
</p>
</div>
</div>
</EmptySearch>
{/if}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {results?.total}</p>
<PaginationInline limit={5} bind:offset sum={results?.total} hidePages />
</div>
<svelte:fragment slot="footer">
<Button submit disabled={!hasSelection}>Create</Button>
</svelte:fragment>
+6 -2
View File
@@ -34,14 +34,18 @@
clearTimeout(timer);
timer = setTimeout(async () => {
const url = new URL($page.url);
url.search = value;
if (url.search === value) {
return;
}
/**
* Reset to first page if search changes.
*/
if ($page.data.page > 1) {
url.pathname = url.pathname.replace(new RegExp(`/${$page.data.page}*$`), '/');
}
url.search = value;
goto(url, { keepfocus: true });
}, debounce);
};
+39 -36
View File
@@ -1,59 +1,62 @@
export const PAGE_LIMIT = 12; // default page limit
export const CARD_LIMIT = 6; // default card limit
export enum Dependencies {
ORGANIZATION = 'organization',
PROJECT = 'project',
PROJECTS = 'projects',
ACCOUNT = 'account',
ACCOUNT_SESSIONS = 'account_sessions',
USER = 'user',
USERS = 'users',
SESSIONS = 'sessions',
TEAM = 'team',
TEAMS = 'teams',
MEMBERSHIPS = 'memberships',
DATABASE = 'database',
COLLECTION = 'collection',
DOCUMENT = 'document',
DOCUMENTS = 'documents',
ATTRIBUTES = 'attributes',
INDEXES = 'indexes',
BUCKET = 'bucket',
FILE = 'file',
FILES = 'files',
FUNCTION = 'function',
FUNCTIONS = 'functions',
VARIABLES = 'variables',
DEPLOYMENTS = 'deployments',
PLATFORM = 'platform',
PLATFORMS = 'platforms',
KEY = 'key',
KEYS = 'keys',
DOMAINS = 'domains',
WEBHOOK = 'webhook',
WEBHOOKS = 'webhooks'
ORGANIZATION = 'dependency:organization',
PROJECT = 'dependency:project',
PROJECTS = 'dependency:projects',
ACCOUNT = 'dependency:account',
ACCOUNT_SESSIONS = 'dependency:account_sessions',
USER = 'dependency:user',
USERS = 'dependency:users',
SESSIONS = 'dependency:sessions',
TEAM = 'dependency:team',
TEAMS = 'dependency:teams',
MEMBERSHIPS = 'dependency:memberships',
DATABASE = 'dependency:database',
COLLECTION = 'dependency:collection',
DOCUMENT = 'dependency:document',
DOCUMENTS = 'dependency:documents',
BUCKET = 'dependency:bucket',
FILE = 'dependency:file',
FILES = 'dependency:files',
FUNCTION = 'dependency:function',
FUNCTIONS = 'dependency:functions',
VARIABLES = 'dependency:variables',
DEPLOYMENTS = 'dependency:deployments',
EXECUTIONS = 'dependency:executions',
PLATFORM = 'dependency:platform',
PLATFORMS = 'dependency:platforms',
KEY = 'dependency:key',
KEYS = 'dependency:keys',
DOMAINS = 'dependency:domains',
WEBHOOK = 'dependency:webhook',
WEBHOOKS = 'dependency:webhooks'
}
export const scopes = [
export const scopes: {
scope: string;
description: string;
category: string;
}[] = [
{
scope: 'users.read',
description: "Access to read your project's users",
category: 'Authentication'
category: 'Auth'
},
{
scope: 'users.write',
description: "Access to create, update, and delete your project's users",
category: 'Authentication'
category: 'Auth'
},
{
scope: 'teams.read',
description: "Access to read your project's teams",
category: 'Authentication'
category: 'Auth'
},
{
scope: 'teams.write',
description: "Access to create, update, and delete your project's teams",
category: 'Authentication'
category: 'Auth'
},
{
scope: 'databases.read',
+3
View File
@@ -18,6 +18,8 @@
let element: HTMLInputElement;
let error: string;
const pattern = String.raw`^[^]+\s+[^]+\s+[^]+\s+[^]+\s+[^]`;
onMount(() => {
if (element && autofocus) {
element.focus();
@@ -57,6 +59,7 @@
{maxlength}
{readonly}
{step}
{pattern}
type="text"
class="input-text"
bind:value
+3 -2
View File
@@ -21,7 +21,7 @@
}
});
const handleInvalid = (event: Event) => {
function handleInvalid(event: Event) {
event.preventDefault();
if (element.validity.valueMissing) {
@@ -30,7 +30,7 @@
}
error = element.validationMessage;
};
}
$: if (value) {
error = null;
@@ -45,6 +45,7 @@
{disabled}
{readonly}
{required}
step=".001"
autocomplete={autocomplete ? 'on' : 'off'}
type="datetime-local"
class="input-text"
+8 -2
View File
@@ -2,6 +2,12 @@
import { clickOnEnter } from '$lib/helpers/a11y';
</script>
<div class="table-row" role="row" on:keyup={clickOnEnter} on:click|preventDefault>
<!-- svelte-ignore a11y-unknown-role -->
<tr
class="table-row"
role="row button"
tabindex="0"
on:keyup={clickOnEnter}
on:click|preventDefault>
<slot />
</div>
</tr>
+4
View File
@@ -8,3 +8,7 @@ export function difference(arr1: unknown[], arr2: unknown[]) {
const intersection = new Set(arr1.filter((elem) => !set.has(elem)));
return Array.from(intersection);
}
export function symmetricDifference(arr1: unknown[], arr2: unknown[]) {
return difference(arr1, arr2).concat(difference(arr2, arr1));
}
+8 -45
View File
@@ -1,3 +1,5 @@
import prettyBytes from 'pretty-bytes';
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
export function calculateSize(bytes: number, decimals = 1) {
@@ -20,55 +22,16 @@ export function bytesToSize(value: number, unit: string) {
return value / Math.pow(1024, index);
}
export function humanFileSize(value: number): {
export function humanFileSize(bytes: number): {
value: string;
unit: string;
} {
if (!value) return { value: 'No', unit: 'Bytes' };
const length = value.toString().length;
const unit =
length < 7
? 'kilobyte'
: length < 11
? 'megabyte'
: length < 15
? 'gigabyte'
: length < 19
? 'terabyte'
: 'petabyte';
const formatter = Intl.NumberFormat('en', {
style: 'unit',
maximumSignificantDigits: 3,
maximumFractionDigits: 2,
unit
});
while (value > 1000) {
value /= 1000;
}
const parts: {
integer: string;
unit: string;
fraction?: string;
decimal?: string;
} = formatter.formatToParts(value).reduce(
(prev, curr) => {
prev[curr.type] = curr.value;
return prev;
},
{
integer: '',
unit: '',
decimal: '',
fraction: ''
}
);
const value = prettyBytes(bytes, {
locale: 'en'
}).split(' ');
return {
value: `${parts.integer}${parts.decimal}${parts.fraction}`,
unit: parts.unit
value: value[0],
unit: value[1]
};
}
@@ -1,6 +1,5 @@
<script lang="ts">
import { Empty, Pagination } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { EmptySearch, Pagination, Trim } from '$lib/components';
import {
Table,
TableBody,
@@ -12,18 +11,21 @@
} from '$lib/elements/table';
import { Container } from '$lib/layout';
import { toLocaleDateTime } from '$lib/helpers/date';
import { sdkForProject } from '$lib/stores/sdk';
import type { PageData } from './$types';
import { sdkForConsole } from '$lib/stores/sdk';
import { PAGE_LIMIT } from '$lib/constants';
import { page } from '$app/stores';
import type { Models } from '@aw-labs/appwrite-console';
export let data: PageData;
export let logs: Models.LogList;
export let path: string;
export let offset = 0;
const getBrowser = (clientCode: string) => sdkForProject.avatars.getBrowser(clientCode, 80, 80);
const getBrowser = (clientCode: string) => {
return sdkForConsole.avatars.getBrowser(clientCode, 40, 40);
};
</script>
<Container>
{#if data.logs.total}
{#if logs.total}
<Table>
<TableHeader>
<TableCellHead>Client</TableCellHead>
@@ -33,7 +35,7 @@
<TableCellHead>Date</TableCellHead>
</TableHeader>
<TableBody>
{#each data.logs.logs as log}
{#each logs.logs as log}
<TableRow>
<TableCell title="Client">
{#if log.clientName}
@@ -45,20 +47,17 @@
src={getBrowser(log.clientCode).toString()}
alt={log.clientName} />
</div>
<p class="text u-trim">
<Trim>
{log.clientName}
{log.clientVersion}
on {log.osName}
{log.osVersion}
</p>
</Trim>
</div>
{:else}
<div class="u-flex u-cross-center u-gap-12">
<p class="text u-trim">
<span class="avatar is-color-empty" />
Unknown
</p>
<span class="avatar is-color-empty" />
<p class="text u-trim">Unknown</p>
</div>
{/if}
</TableCell>
@@ -77,22 +76,20 @@
{/each}
</TableBody>
</Table>
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {logs.total}</p>
<Pagination limit={PAGE_LIMIT} {path} {offset} sum={logs.total} />
</div>
{:else}
<Empty single>
<p>No logs available</p>
<Button
external
secondary
href="https://appwrite.io/docs/server/authentication?sdk=nodejs-default#usersGetLogs"
>Documentation</Button>
</Empty>
<EmptySearch>
<div class="u-flex u-flex-vertical u-cross-center">
<div class="u-text-center">
<p class="text u-line-height-1-5">
You have no activity. Once your users start interacting with your app you'll
see their activity here.
</p>
</div>
</div>
</EmptySearch>
{/if}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {data.logs.total}</p>
<Pagination
limit={PAGE_LIMIT}
path={`/console/project-${$page.params.project}/authentication/user-${$page.params.user}/activity`}
offset={data.offset}
sum={data.logs.total} />
</div>
</Container>
+1 -1
View File
@@ -47,7 +47,7 @@
<span class="text">Support</span>
</a>
</nav>
<nav class="user-profile">
<nav class="u-flex u-height-100-percents u-sep-inline-start">
{#if $user}
<div class="drop-wrapper" class:is-open={showDropdown} bind:this={droplistElement}>
<button class="user-profile-button" on:click={() => (showDropdown = !showDropdown)}>
+2
View File
@@ -13,3 +13,5 @@ export { default as WizardStep } from './wizardStep.svelte';
export { default as Breadcrumbs } from './breadcrumbs.svelte';
export { default as Unauthenticated } from './unauthenticated.svelte';
export { default as Usage, type UsagePeriods } from './usage.svelte';
export { default as Activity } from './activity.svelte';
export { default as Progress } from './progress.svelte';
+9
View File
@@ -35,8 +35,17 @@
.getElementsByClassName('code-panel-content')[0]
.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') {
event.preventDefault();
$log.show = false;
}
}
</script>
<svelte:window on:keydown={handleKeydown} />
{#if $log.data}
<section class="cover-frame">
<header class="cover-frame-header u-flex u-gap-16 u-main-space-between u-cross-center">
+18 -20
View File
@@ -31,6 +31,15 @@
<span class="text">Overview</span>
</a>
</li>
<li class="drop-list-item">
<a
class="drop-button"
class:is-selected={$page.url.pathname.startsWith(`${path}/auth`)}
href={`${path}/auth`}>
<span class="icon-user-group" aria-hidden="true" />
<span class="text">Auth</span>
</a>
</li>
<li class="drop-list-item">
<a
class="drop-button"
@@ -40,26 +49,6 @@
<span class="text">Databases</span>
</a>
</li>
<li class="drop-list-item">
<a
class="drop-button"
class:is-selected={$page.url.pathname.startsWith(`${path}/storage`)}
href={`${path}/storage`}>
<span class="icon-folder" aria-hidden="true" />
<span class="text">Storage</span>
</a>
</li>
<li class="drop-list-item">
<a
class="drop-button"
class:is-selected={$page.url.pathname.startsWith(
`${path}/authentication`
)}
href={`${path}/authentication`}>
<span class="icon-user-group" aria-hidden="true" />
<span class="text">Authentication</span>
</a>
</li>
<li class="drop-list-item">
<a
class="drop-button"
@@ -69,6 +58,15 @@
<span class="text">Functions</span>
</a>
</li>
<li class="drop-list-item">
<a
class="drop-button"
class:is-selected={$page.url.pathname.startsWith(`${path}/storage`)}
href={`${path}/storage`}>
<span class="icon-folder" aria-hidden="true" />
<span class="text">Storage</span>
</a>
</li>
</ul>
</section>
</div>
+83
View File
@@ -0,0 +1,83 @@
<script lang="ts">
import { afterNavigate, beforeNavigate } from '$app/navigation';
let width: number;
let updater: ReturnType<typeof setTimeout>;
let completed = false;
const minimum = 0.08;
const maximum = 0.994;
const settleTime = 700;
const intervalTime = 800;
const stepSizes = [0, 0.005, 0.01, 0.02];
function animate(): void {
if (updater) {
clearInterval(updater);
}
updater = setInterval(() => {
const randomStep = stepSizes[Math.floor(Math.random() * stepSizes.length)];
const step = getIncrement(width) + randomStep;
if (width < maximum) {
width = width + step;
}
if (width > maximum) {
width = maximum;
stop();
}
}, intervalTime);
}
function start(): void {
width = minimum;
animate();
}
function complete(): void {
clearInterval(updater);
width = 1;
setTimeout(() => {
completed = true;
setTimeout(() => {
completed = false;
width = 0;
}, settleTime);
}, settleTime);
}
function getIncrement(number: number): number {
if (number >= 0 && number < 0.2) return 0.1;
else if (number >= 0.2 && number < 0.5) return 0.04;
else if (number >= 0.5 && number < 0.8) return 0.02;
else if (number >= 0.8 && number < 0.99) return 0.005;
return 0;
}
beforeNavigate(start);
afterNavigate(complete);
$: barStyle = (width && width * 100 && `width: ${width * 100}%;`) || '';
</script>
<svelte:head />
{#if width}
<div class="progress-bar" class:progress-bar-hiding={completed} style={barStyle} />
{/if}
<style>
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 2px;
transition: width 0.2s ease-in-out;
z-index: 5;
background: hsl(var(--color-primary-200));
}
.progress-bar-hiding {
transition: top 0.8s ease;
top: -8px;
}
</style>
+1 -1
View File
@@ -82,7 +82,7 @@
<div class="u-margin-block-start-auto" />
<p class="u-max-width-500 u-width-full-line u-margin-block-start-24">
version 0.15.2.402
<!-- version 0.15.2.402 -->
</p>
<div class="u-margin-block-start-20" />
</div>
+51 -38
View File
@@ -42,12 +42,17 @@
<div class="u-flex u-main-space-between common-section">
<Heading tag="h2" size="5">{title}</Heading>
<DropTabs>
<DropTabsLink href={`${path}/24h`} disabled={$page.params.period === '24h'}
>24h</DropTabsLink>
<DropTabsLink href={`${path}/30d`} disabled={$page.params.period === '30d'}
>30d</DropTabsLink>
<DropTabsLink href={`${path}/90d`} disabled={$page.params.period === '90d'}
>90d</DropTabsLink>
<DropTabsLink href={`${path}/24h`} disabled={$page.params.period === '24h'}>
24h
</DropTabsLink>
<DropTabsLink
href={`${path}/30d`}
disabled={!$page.params.period || $page.params.period === '30d'}>
30d
</DropTabsLink>
<DropTabsLink href={`${path}/90d`} disabled={$page.params.period === '90d'}>
90d
</DropTabsLink>
</DropTabs>
</div>
<Card>
@@ -70,14 +75,16 @@
<Heading tag="h6" size="6">{total(created)}</Heading>
<p>{createdMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: createdMetadata.legend,
data: [...created.map((e) => [e.date, e.value])],
color: Colors.Secondary
}
]} />
<div style="height: 12rem;">
<LineChart
series={[
{
name: createdMetadata.legend,
data: [...created.map((e) => [e.date, e.value])],
color: Colors.Secondary
}
]} />
</div>
{/if}
</Card>
<Card isTile>
@@ -85,14 +92,16 @@
<Heading tag="h6" size="6">{total(read)}</Heading>
<p>{readMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: readMetadata.legend,
data: [...read.map((e) => [e.date, e.value])],
color: Colors.Tertiary
}
]} />
<div style="height: 12rem;">
<LineChart
series={[
{
name: readMetadata.legend,
data: [...read.map((e) => [e.date, e.value])],
color: Colors.Tertiary
}
]} />
</div>
{/if}
</Card>
<Card isTile>
@@ -100,14 +109,16 @@
<Heading tag="h6" size="6">{total(updated)}</Heading>
<p>{updatedMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: updatedMetadata.legend,
data: [...updated.map((e) => [e.date, e.value])],
color: Colors.Quaternary
}
]} />
<div style="height: 12rem;">
<LineChart
series={[
{
name: updatedMetadata.legend,
data: [...updated.map((e) => [e.date, e.value])],
color: Colors.Quaternary
}
]} />
</div>
{/if}
</Card>
<Card isTile>
@@ -115,14 +126,16 @@
<Heading tag="h6" size="6">{total(deleted)}</Heading>
<p>{deletedMetadata.title}</p>
<div class="u-margin-block-start-16" />
<LineChart
series={[
{
name: deletedMetadata.legend,
data: [...deleted.map((e) => [e.date, e.value])],
color: Colors.Quinary
}
]} />
<div style="height: 12rem;">
<LineChart
series={[
{
name: deletedMetadata.legend,
data: [...deleted.map((e) => [e.date, e.value])],
color: Colors.Quinary
}
]} />
</div>
{/if}
</Card>
</Tiles>
+2 -1
View File
@@ -28,7 +28,8 @@
let showExitModal = false;
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && !showExitModal) {
const openDialog = document.querySelectorAll('dialog[open]');
if (event.key === 'Escape' && !showExitModal && !openDialog?.length) {
event.preventDefault();
dispatch('exit');
wizard.hide();
+121 -282
View File
@@ -1,18 +1,14 @@
import { writable } from 'svelte/store';
import type { Models } from '@aw-labs/appwrite-console';
import type { SvelteComponent } from 'svelte';
import Apple from '../../routes/console/project-[project]/authentication/_appleOAuth.svelte';
import Microsoft from '../../routes/console/project-[project]/authentication/_microsoftOAuth.svelte';
import Okta from '../../routes/console/project-[project]/authentication/_oktaOAuth.svelte';
import Auth0 from '../../routes/console/project-[project]/authentication/_auth0OAuth.svelte';
import Main from '../../routes/console/project-[project]/authentication/_mainOAuth.svelte';
import Apple from '../../routes/console/project-[project]/auth/_appleOAuth.svelte';
import Microsoft from '../../routes/console/project-[project]/auth/_microsoftOAuth.svelte';
import Okta from '../../routes/console/project-[project]/auth/_oktaOAuth.svelte';
import Auth0 from '../../routes/console/project-[project]/auth/_auth0OAuth.svelte';
import Main from '../../routes/console/project-[project]/auth/_mainOAuth.svelte';
export type Provider = {
name: string;
export type Provider = Models.Provider & {
icon: string;
active: boolean;
id: string | null;
secret: string | null;
docs?: string;
component?: typeof SvelteComponent;
};
@@ -22,279 +18,122 @@ export type Providers = {
};
const setProviders = (project: Models.Project): Provider[] => {
return [
{
name: 'Amazon',
icon: 'amazon',
active: false,
id: project?.providerAmazonAppid,
secret: project?.providerAmazonSecret,
docs: 'https://developer.amazon.com/apps-and-games/services-and-apis',
component: Main
},
{
name: 'Apple',
icon: 'apple',
active: false,
id: project?.providerAppleAppid,
secret: project?.providerAppleSecret,
docs: 'https://developer.apple.com/',
component: Apple
},
{
name: 'Auth0',
icon: 'auth0',
active: false,
id: project?.providerAuth0Appid,
secret: project?.providerAuth0Secret,
docs: 'https://auth0.com/developers',
component: Auth0
},
{
name: 'Bitbucket',
icon: 'bitBucket',
active: false,
id: project?.providerBitbucketAppid,
secret: project?.providerBitbucketSecret,
docs: 'https://developer.atlassian.com/bitbucket',
component: Main
},
{
name: 'Bitly',
icon: 'bitly',
active: false,
id: project?.providerBitlyAppid,
secret: project?.providerBitlySecret,
docs: 'https://dev.bitly.com/v4_documentation.html',
component: Main
},
{
name: 'Box',
icon: 'box',
active: false,
id: project?.providerBoxAppid,
secret: project?.providerBoxSecret,
docs: 'https://developer.box.com/reference/',
component: Main
},
{
name: 'Discord',
icon: 'discord',
active: false,
id: project?.providerDiscordAppid,
secret: project?.providerDiscordSecret,
docs: 'https://discordapp.com/developers/docs/topics/oauth2',
component: Main
},
{
name: 'Dropbox',
icon: 'dropbox',
active: false,
id: project?.providerDropboxAppid,
secret: project?.providerDropboxSecret,
docs: 'https://www.dropbox.com/developers/documentation',
component: Main
},
{
name: 'Facebook',
icon: 'facebook',
active: false,
id: project?.providerFacebookAppid,
secret: project?.providerFacebookSecret,
docs: 'https://developers.facebook.com/',
component: Main
},
{
name: 'Github',
icon: 'github',
active: false,
id: project?.providerGithubAppid,
secret: project?.providerGithubSecret,
docs: 'https://developer.github.com/',
component: Main
},
{
name: 'Gitlab',
icon: 'gitlab',
active: false,
id: project?.providerGitlabAppid,
secret: project?.providerGitlabSecret,
docs: 'https://docs.gitlab.com/ee/api/',
component: Main
},
{
name: 'Google',
icon: 'google',
active: false,
id: project?.providerGoogleAppid,
secret: project?.providerGoogleSecret,
docs: 'https://support.google.com/googleapi/answer/6158849',
component: Main
},
{
name: 'Linkedin',
icon: 'linkedin',
active: false,
id: project?.providerLinkedinAppid,
secret: project?.providerLinkedinSecret,
docs: 'https://developer.linkedin.com/',
component: Main
},
{
name: 'Microsoft',
icon: 'microsoft',
active: false,
id: project?.providerMicrosoftAppid,
secret: project?.providerMicrosoftSecret,
docs: 'https://developer.microsoft.com/en-us/',
component: Microsoft
},
{
name: 'Notion',
icon: 'notion',
active: false,
id: project?.providerNotionAppid,
secret: project?.providerNotionSecret,
docs: 'https://developers.notion.com/docs',
component: Main
},
{
name: 'Okta',
icon: 'okta',
active: false,
id: project?.providerOktaAppid,
secret: project?.providerOktaSecret,
docs: 'https://developer.okta.com/',
component: Okta
},
{
name: 'Paypal',
icon: 'paypal',
active: false,
id: project?.providerPaypalAppid,
secret: project?.providerPaypalSecret,
docs: 'https://developer.paypal.com/docs/api/overview/',
component: Main
},
{
name: 'Paypal (sandbox)',
icon: 'paypal',
active: false,
id: project?.providerPaypalSandboxAppid,
secret: project?.providerPaypalSandboxSecret,
docs: 'https://developer.paypal.com/docs/api/overview/',
component: Main
},
{
name: 'Salesforce',
icon: 'salesforce',
active: false,
id: project?.providerSalesforceAppid,
secret: project?.providerSalesforceSecret,
docs: 'https://developer.salesforce.com/docs/',
component: Main
},
{
name: 'Slack',
icon: 'slack',
active: false,
id: project?.providerSlackAppid,
secret: project?.providerSlackSecret,
docs: 'https://api.slack.com/',
component: Main
},
{
name: 'Spotify',
icon: 'spotify',
active: false,
id: project?.providerSpotifyAppid,
secret: project?.providerSpotifySecret,
docs: 'https://developer.spotify.com/documentation/general/guides/authorization-guide/',
component: Main
},
{
name: 'Stripe',
icon: 'stripe',
active: false,
id: project?.providerStripeAppid,
secret: project?.providerStripeSecret,
docs: 'https://stripe.com/docs/api',
component: Main
},
{
name: 'Tradeshift',
icon: 'tradeshift',
active: false,
id: project?.providerTradeshiftAppid,
secret: project?.providerTradeshiftSecret,
docs: 'https://developers.tradeshift.com/docs/api',
component: Main
},
{
name: 'Tradeshift(sandbox)',
icon: 'tradeshift',
active: false,
id: project?.providerTradeshiftBoxAppid,
secret: project?.providerTradeshiftBoxSecret,
docs: 'https://developers.tradeshift.com/docs/api',
component: Main
},
{
name: 'Twitch',
icon: 'twitch',
active: false,
id: project?.providerTwitchAppid,
secret: project?.providerTwitchSecret,
docs: 'https://dev.twitch.tv/docs/authentication',
component: Main
},
return (
project?.providers.map((n) => {
let docs: Provider['docs'];
let icon: Provider['icon'] = n.name.toLowerCase();
let component: Provider['component'] = Main;
{
name: 'Wordpress',
icon: 'wordpress',
active: false,
id: project?.providerWordpressAppid,
secret: project?.providerWordpressSecret,
docs: 'https://developer.wordpress.com/docs/oauth2/',
component: Main
},
{
name: 'Yahoo',
icon: 'yahoo',
active: false,
id: project?.providerYahooAppid,
secret: project?.providerYahooSecret,
docs: 'https://developer.yahoo.com/oauth2/guide/flows_authcode/',
component: Main
},
{
name: 'Yammer',
icon: 'ms_yammer',
active: false,
id: project?.providerYammerAppid,
secret: project?.providerYammerSecret,
docs: 'https://developer.yammer.com/docs/oauth-2',
component: Main
},
{
name: 'Yandex',
icon: 'yandex',
active: false,
id: project?.providerYandexAppid,
secret: project?.providerYandexSecret,
docs: 'https://tech.yandex.com/oauth/',
component: Main
},
{
name: 'Zoom',
icon: 'zoom',
active: false,
id: project?.providerZoomAppid,
secret: project?.providerZoomSecret,
docs: 'https://marketplace.zoom.us/docs/guides/auth/oauth/',
component: Main
}
];
switch (n.name.toLowerCase()) {
case 'amazon':
docs = 'https://developer.amazon.com/apps-and-games/services-and-apis';
break;
case 'apple':
docs = 'https://developer.apple.com/';
component = Apple;
break;
case 'auth0':
docs = 'https://auth0.com/developers';
component = Auth0;
break;
case 'bitbucket':
docs = 'https://developer.atlassian.com/bitbucket';
break;
case 'bitly':
docs = 'https://dev.bitly.com/v4_documentation.html';
break;
case 'box':
docs = 'https://developer.box.com/reference/';
break;
case 'discord':
docs = 'https://discordapp.com/developers/docs/topics/oauth2';
break;
case 'dropbox':
docs = 'https://www.dropbox.com/developers/documentation';
break;
case 'facebook':
docs = 'https://developers.facebook.com/';
break;
case 'github':
docs = 'https://developer.github.com';
break;
case 'gitlab':
docs = 'https://docs.gitlab.com/ee/api/';
break;
case 'google':
docs = 'https://support.google.com/googleapi/answer/6158849';
break;
case 'linkedin':
docs = 'https://developer.linkedin.com/';
break;
case 'microsoft':
docs = 'https://developer.microsoft.com/en-us/';
component = Microsoft;
break;
case 'notion':
docs = 'https://developers.notion.com/docs';
break;
case 'okta':
docs = 'https://developer.okta.com';
component = Okta;
break;
case 'paypal':
docs = 'https://developer.paypal.com/docs/api/overview/';
break;
case 'paypalsandbox':
icon = 'paypal';
docs = 'https://developer.paypal.com/docs/api/overview/';
break;
case 'salesforce':
docs = 'https://developer.salesforce.com/docs/';
break;
case 'slack':
docs = 'https://api.slack.com/';
break;
case 'spotify':
docs =
'https://developer.spotify.com/documentation/general/guides/authorization-guide/';
break;
case 'stripe':
docs = 'https://stripe.com/docs/api';
break;
case 'tradeshift':
docs = 'https://developers.tradeshift.com/docs/api';
break;
case 'tradeshiftbox':
icon = 'tradeshift';
docs = 'https://developers.tradeshift.com/docs/api';
break;
case 'twitch':
docs = 'https://dev.twitch.tv/docs/auth';
break;
case 'wordpress':
docs = 'https://developer.wordpress.com/docs/oauth2/';
break;
case 'yahoo':
docs = 'https://developer.yahoo.com/oauth2/guide/flows_authcode/';
break;
case 'yammer':
docs = 'https://developer.yammer.com/docs/oauth-2';
break;
case 'yandex':
docs = 'https://tech.yandex.com/oauth/';
break;
case 'zoom':
docs = 'https://marketplace.zoom.us/docs/guides/auth/oauth/';
break;
default:
break;
}
return {
...n,
icon,
docs,
component
};
}) ?? []
);
};
function createOAuthProviders() {
+4 -2
View File
@@ -8,9 +8,9 @@
import { base } from '$app/paths';
import { browser, dev } from '$app/environment';
import { app } from '$lib/stores/app';
import Notifications from '$lib/layout/notifications.svelte';
import Loading from './loading.svelte';
import { Progress, Notifications } from '$lib/layout';
import { loading } from './store';
import Loading from './loading.svelte';
if (browser) {
window.VERCEL_ANALYTICS_ID = import.meta.env.VERCEL_ANALYTICS_ID?.toString() ?? false;
@@ -95,6 +95,8 @@
<Loading />
{/if}
<Progress />
<style lang="scss" global>
.tippy-box {
--p-tooltip-text-color: var(--color-neutral-10);
-4
View File
@@ -26,10 +26,6 @@
}
</script>
<svelte:head>
<title>Console - Appwrite</title>
</svelte:head>
<Shell
showSideNavigation={$page.url.pathname !== '/console' &&
!$page?.params.organization &&
+1 -1
View File
@@ -5,6 +5,6 @@ import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => {
return {
header: Header,
breadcrumb: Breadcrumbs
breadcrumbs: Breadcrumbs
};
};
@@ -1,105 +1,9 @@
<script lang="ts">
import { Empty, Pagination } from '$lib/components';
import { Button } from '$lib/elements/forms';
import {
Table,
TableBody,
TableHeader,
TableRow,
TableCellHead,
TableCell,
TableCellText
} from '$lib/elements/table';
import { Container } from '$lib/layout';
import { toLocaleDateTime } from '$lib/helpers/date';
import { sdkForConsole } from '$lib/stores/sdk';
import { Activity } from '$lib/layout';
import type { PageData } from './$types';
import { PAGE_LIMIT } from '$lib/constants';
export let data: PageData;
const getBrowser = (clientCode: string) => {
return sdkForConsole.avatars.getBrowser(clientCode, 40, 40);
};
const path = '/console/account/activity';
</script>
<Container>
{#if data.logs.total}
<Table>
<TableHeader>
<TableCellHead>Date</TableCellHead>
<TableCellHead>Event</TableCellHead>
<TableCellHead>Client</TableCellHead>
<TableCellHead>Location</TableCellHead>
<TableCellHead>IP</TableCellHead>
</TableHeader>
<TableBody>
{#each data.logs.logs as log}
<TableRow>
<TableCellText title="Date">{toLocaleDateTime(log.time)}</TableCellText>
<TableCellText title="Event">{log.event}</TableCellText>
<TableCell title="Client">
{#if log.clientName}
<div class="u-flex u-cross-center u-gap-12">
<div class="avatar is-small">
<img
height="20"
width="20"
src={getBrowser(log.clientCode).toString()}
alt={log.clientName} />
</div>
<p
class="text u-trim"
title={`${log.clientName} ${log.clientVersion} on ${log.osName} ${log.osVersion}`}>
{log.clientName}
{log.clientVersion}
on {log.osName}
{log.osVersion}
</p>
</div>
{:else}
<div class="u-flex u-cross-center u-gap-12">
<span class="avatar is-color-empty" />
<p class="text u-trim">Unknown</p>
</div>
{/if}
</TableCell>
<TableCellText title="Location">
{#if log.countryCode !== '--'}
{log.countryName}
{:else}
Unknown
{/if}
</TableCellText>
<TableCellText title="IP">{log.ip}</TableCellText>
</TableRow>
{/each}
</TableBody>
</Table>
{:else}
<Empty>
<div class="u-flex u-flex-vertical u-cross-center">
<div class="common-section">
<p>No logs available</p>
</div>
<div class="common-section">
<Button
external
secondary
href="https://appwrite.io/docs/server/authentication?sdk=nodejs-default#usersGetLogs"
>Documentation</Button>
</div>
</div>
</Empty>
{/if}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {data.logs.total}</p>
<Pagination
limit={PAGE_LIMIT}
path="/console/account/activity"
offset={data.offset}
sum={data.logs.total} />
</div>
</Container>
<Activity {path} logs={data.logs} offset={data.offset} />
+1 -8
View File
@@ -2,8 +2,7 @@
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Copy, Tab, Tabs } from '$lib/components';
import { Pill } from '$lib/elements';
import { Tab, Tabs } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { isTabSelected } from '$lib/helpers/load';
import { Cover, CoverTitle } from '$lib/layout';
@@ -44,12 +43,6 @@
<CoverTitle>
{$user.name}
</CoverTitle>
<Copy value={$user.$id}>
<Pill button>
<span class="icon-duplicate" aria-hidden="true" />
Project ID
</Pill>
</Copy>
<div class="u-margin-inline-start-auto">
<Button secondary on:click={logout}>Logout</Button>
</div>
@@ -66,7 +66,7 @@
</svelte:fragment>
</CardContainer>
{:else}
<Empty isButton single on:click={() => (addOrganization = true)}>
<Empty single on:click={() => (addOrganization = true)}>
<p>Create a new organization</p>
</Empty>
{/if}
@@ -108,7 +108,7 @@
<Button
external
secondary
href="https://appwrite.io/docs/server/authentication?sdk=nodejs-default#usersGetsessions">
href="https://appwrite.io/docs/server/auth?sdk=nodejs-default#usersGetsessions">
Documentation
</Button>
</div>
+1 -1
View File
@@ -55,7 +55,7 @@
</Pill>
</div>
{:else}
<CustomId bind:show={showCustomId} name="organization" bind:id />
<CustomId bind:show={showCustomId} name="Organization" bind:id />
{/if}
</FormList>
<svelte:fragment slot="footer">
@@ -1 +1,5 @@
<svelte:head>
<title>Onboarding - Appwrite</title>
</svelte:head>
<slot />
+6 -1
View File
@@ -19,7 +19,12 @@
try {
loading = true;
const org = await createOrganization();
const project = await sdkForConsole.projects.create(id ?? ID.unique(), name, org.$id);
const project = await sdkForConsole.projects.create(
id ?? ID.unique(),
name,
org.$id,
'default'
);
await invalidate(Dependencies.ACCOUNT);
goto(`/console/project-${project.$id}`);
} catch ({ message }) {
@@ -1,11 +1,12 @@
import Header from './header.svelte';
import Breadcrumbs from './breadcrumbs.svelte';
import { sdkForConsole } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import type { LayoutLoad } from './$types';
import { error } from '@sveltejs/kit';
import { Dependencies } from '$lib/constants';
export const load: LayoutLoad = async ({ params, depends }) => {
export const load: LayoutLoad = async ({ params, parent, depends }) => {
await parent();
depends(Dependencies.ORGANIZATION);
try {
@@ -1,5 +1,4 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { Pill } from '$lib/elements';
import { GridItem1, Heading, Empty, CardContainer, Pagination } from '$lib/components';
@@ -8,7 +7,6 @@
import CreateProject from '../createProject.svelte';
import CreateOrganization from '../../createOrganization.svelte';
import type { PageData } from './$types';
import type { Models } from '@aw-labs/appwrite-console';
import { CARD_LIMIT } from '$lib/constants';
import { page } from '$app/stores';
@@ -17,10 +15,6 @@
let showCreate = false;
let addOrganization = false;
const projectCreated = async (event: CustomEvent<Models.Project>) => {
await goto(`${base}/console/project-${event.detail.$id}`);
};
const getPlatformInfo = (platform: string) => {
let name: string, icon: string;
if (platform.includes('flutter')) {
@@ -44,6 +38,12 @@
}
return { name, icon };
};
function filterPlatforms(platforms: { name: string; icon: string }[]) {
return platforms.filter(
(value, index, self) => index === self.findIndex((t) => t.name === value.name)
);
}
</script>
<Container>
@@ -71,15 +71,14 @@
<svelte:fragment slot="title">
{project.name}
</svelte:fragment>
{@const platformList = project.platforms.map((platform) => platform.type)}
{@const platforms = Array.from(new Set(platformList))}
{@const platforms = filterPlatforms(
project.platforms.map((platform) => getPlatformInfo(platform.type))
)}
{#each platforms as platform, i}
{#if i < 3}
<Pill>
<span
class={`icon-${getPlatformInfo(platform).icon}`}
aria-hidden="true" />
{getPlatformInfo(platform).name}
<span class={`icon-${platform.icon}`} aria-hidden="true" />
{platform.name}
</Pill>
{/if}
{/each}
@@ -96,7 +95,7 @@
</svelte:fragment>
</CardContainer>
{:else}
<Empty isButton single on:click={() => (showCreate = true)}>
<Empty single on:click={() => (showCreate = true)}>
<p>Create a new project</p>
</Empty>
{/if}
@@ -112,7 +111,4 @@
<CreateOrganization bind:show={addOrganization} />
<CreateProject
bind:show={showCreate}
teamId={$page.params.organization}
on:created={projectCreated} />
<CreateProject bind:show={showCreate} teamId={$page.params.organization} />
@@ -25,7 +25,7 @@
url,
name
);
invalidate(Dependencies.ORGANIZATION);
invalidate(Dependencies.ACCOUNT);
showCreate = false;
addNotification({
type: 'success',
@@ -1,4 +1,5 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { Modal, CustomId } from '$lib/components';
import { Pill } from '$lib/elements';
import { InputText, Button, FormList } from '$lib/elements/forms';
@@ -15,20 +16,27 @@
let name: string;
let showCustomId = false;
let error: string;
let isCreating = false;
const create = async () => {
try {
const project = await sdkForConsole.projects.create(id ?? 'unique()', name, teamId);
isCreating = true;
const project = await sdkForConsole.projects.create(
id ?? 'unique()',
name,
teamId,
'default'
);
dispatch('created', project);
addNotification({
type: 'success',
message: `${name} has been created`
});
id = name = null;
showCustomId = false;
show = false;
await goto(`/console/project-${project.$id}`);
} catch ({ message }) {
error = message;
} finally {
isCreating = false;
}
};
</script>
@@ -36,7 +44,13 @@
<Modal {error} on:submit={create} size="big" bind:show>
<svelte:fragment slot="header">Create Project</svelte:fragment>
<FormList>
<InputText id="name" label="Name" bind:value={name} required autofocus={true} />
<InputText
id="name"
label="Name"
bind:value={name}
required
autofocus={true}
disabled={isCreating} />
{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}>
@@ -51,6 +65,6 @@
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (show = false)}>Cancel</Button>
<Button submit>Create</Button>
<Button submit disabled={isCreating}>Create</Button>
</svelte:fragment>
</Modal>
@@ -28,7 +28,7 @@
let showDelete = false;
const url = `${$page.url.origin}/console/`;
const deleted = () => invalidate(Dependencies.ORGANIZATION);
const deleted = () => invalidate(Dependencies.ACCOUNT);
const resend = async (member: Models.Membership) => {
try {
await sdkForConsole.teams.createMembership(
@@ -20,7 +20,7 @@
async function updateName() {
try {
await sdkForConsole.teams.update($organization.$id, name);
invalidate(Dependencies.ORGANIZATION);
await invalidate(Dependencies.ORGANIZATION);
addNotification({
message: 'Name has been updated',
type: 'success'
@@ -1,5 +1,26 @@
<script lang="ts">
import { UploadBox } from '$lib/components';
import { sdkForConsole } from '$lib/stores/sdk';
import { onDestroy, onMount } from 'svelte';
import { stats } from './store';
let unsubscribe: { (): void };
onMount(() => {
unsubscribe = sdkForConsole.client.subscribe(['project', 'console'], (response) => {
if (response.events.includes('stats.connections')) {
for (const [projectId, value] of Object.entries(response.payload)) {
stats.add(projectId, [new Date(response.timestamp).toISOString(), value]);
}
}
});
});
onDestroy(() => {
if (unsubscribe) {
unsubscribe();
}
});
</script>
<slot />
@@ -0,0 +1,5 @@
<svelte:head>
<title>Auth - Appwrite</title>
</svelte:head>
<slot />
@@ -34,7 +34,7 @@
let showCreate = false;
const projectId = $page.params.project;
const userCreated = async (event: CustomEvent<Models.User<Record<string, unknown>>>) => {
await goto(`${base}/console/project-${projectId}/authentication/user-${event.detail.$id}`);
await goto(`${base}/console/project-${projectId}/auth/user-${event.detail.$id}`);
};
</script>
@@ -65,7 +65,7 @@
<TableBody>
{#each data.users.users as user}
<TableRowLink
href={`${base}/console/project-${projectId}/authentication/user-${user.$id}`}>
href={`${base}/console/project-${projectId}/auth/user-${user.$id}`}>
<TableCell title="Name">
<div class="u-flex u-gap-12 u-cross-center">
{#if user.email || user.phone}
@@ -126,39 +126,22 @@
offset={data.offset}
limit={PAGE_LIMIT}
sum={data.users.total}
path={`/console/project-${projectId}/authentication`} />
path={`/console/project-${projectId}/auth`} />
</div>
{:else if data.search}
<EmptySearch>
<div class="u-text-center">
<b>Sorry, we couldnt find {data.search}</b>
<b>Sorry, we couldn't find '{data.search}'</b>
<p>There are no users that match your search.</p>
</div>
<Button href={`/console/project-${projectId}/authentication`} secondary>
Clear Search
</Button>
<Button href={`/console/project-${projectId}/auth`} secondary>Clear Search</Button>
</EmptySearch>
{:else}
<Empty isButton single on:click={() => (showCreate = true)}>
<div
class="common-section"
use:event={{
name: 'console_users',
action: 'click_create',
parameters: {
type: 'user'
}
}}>
<div class="u-text-center common-section">
<p>Create your first User to get started.</p>
<p>Need a hand? Check out our documentation.</p>
</div>
<div class="u-flex u-gap-16 common-section u-main-center">
<Button external href="#/" text>Documentation</Button>
<Button secondary>Create User</Button>
</div>
</div>
</Empty>
<Empty
single
href="https://appwrite.io/docs/server/users"
target="user"
on:click={() => (showCreate = true)} />
{/if}
</Container>
@@ -6,19 +6,20 @@
import { sdkForConsole } from '$lib/stores/sdk';
import type { Provider } from '$lib/stores/oauth-providers';
import { onMount } from 'svelte';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
export let showModal = false;
export let provider: Provider;
let id: string = null;
let active = false;
let appId: string = null;
let enabled: boolean = null;
let keyID: string = null;
let teamID: string = null;
let p8: string = null;
onMount(() => {
id ??= provider.id;
active ??= provider.active;
appId ??= provider.appId;
enabled ??= provider.enabled;
if (provider.secret) ({ keyID, teamID, p8 } = JSON.parse(provider.secret));
});
@@ -30,19 +31,18 @@
await sdkForConsole.projects.updateOAuth2(
projectId,
provider.name.toLowerCase(),
id,
secret
appId,
secret,
enabled
);
provider.active = active;
provider.id = id;
provider.secret = secret;
showModal = false;
addNotification({
type: 'success',
message: `${provider.name} authentication has been ${
provider.active ? 'enabled' : 'disabled'
provider.enabled ? 'enabled' : 'disabled'
}`
});
provider = null;
invalidate(Dependencies.PROJECT);
} catch ({ message }) {
error = message;
}
@@ -51,7 +51,7 @@
$: secret = keyID && teamID && p8 ? JSON.stringify({ keyID, teamID, p8 }) : provider.secret;
</script>
<Modal {error} on:submit={update} size="big" bind:show={showModal}>
<Modal {error} on:submit={update} size="big" show on:close>
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -61,13 +61,13 @@
visit the docs.
</a>
</p>
<InputSwitch id="state" bind:value={active} label={active ? 'Enabled' : 'Disabled'} />
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="bundleID"
label="Bundle ID"
autofocus={true}
placeholder="com.company.appname"
bind:value={id} />
bind:value={appId} />
<InputText id="keyID" label="Key ID" placeholder="SHAB13ROFN" bind:value={keyID} />
<InputText id="teamID" label="Team ID" placeholder="ELA2CD3AED" bind:value={teamID} />
<InputTextarea id="p8" label="P8 File" placeholder="" bind:value={p8} />
@@ -83,12 +83,12 @@
</div>
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showModal = false)}>Cancel</Button>
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
disabled={(secret === provider.secret &&
active === provider.active &&
id === provider.id) ||
!(id && keyID && teamID && p8)}
enabled === provider.enabled &&
appId === provider.appId) ||
!(appId && keyID && teamID && p8)}
submit>Update</Button>
</svelte:fragment>
</Modal>
@@ -6,19 +6,20 @@
import type { Provider } from '$lib/stores/oauth-providers';
import { addNotification } from '$lib/stores/notifications';
import { onMount } from 'svelte';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
export let showModal = false;
export let provider: Provider;
let id: string = null;
let active = false;
let appId: string = null;
let enabled: boolean = null;
let clientSecret: string = null;
let auth0Domain: string = null;
let error: string;
onMount(() => {
id ??= provider.id;
active ??= provider.active;
appId ??= provider.appId;
enabled ??= provider.enabled;
if (provider.secret) ({ clientSecret, auth0Domain } = JSON.parse(provider.secret));
});
@@ -28,19 +29,18 @@
await sdkForConsole.projects.updateOAuth2(
projectId,
provider.name.toLowerCase(),
id,
secret
appId,
secret,
enabled
);
provider.active = active;
provider.id = id;
provider.secret = secret;
showModal = false;
addNotification({
type: 'success',
message: `${provider.name} authentication has been ${
provider.active ? 'enabled' : 'disabled'
provider.enabled ? 'enabled' : 'disabled'
}`
});
provider = null;
invalidate(Dependencies.PROJECT);
} catch ({ message }) {
error = message;
}
@@ -52,7 +52,7 @@
: provider.secret;
</script>
<Modal {error} size="big" bind:show={showModal} on:submit={update}>
<Modal {error} size="big" show on:submit={update} on:close>
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -61,13 +61,13 @@
<a class="link" href={provider.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={active} label={active ? 'Enabled' : 'Disabled'} />
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="clientID"
label="Client ID"
autofocus={true}
placeholder="Enter ID"
bind:value={id} />
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
@@ -92,12 +92,12 @@
</div>
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showModal = false)}>Cancel</Button>
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
disabled={(secret === provider.secret &&
active === provider.active &&
id === provider.id) ||
!(id && clientSecret && auth0Domain)}
enabled === provider.enabled &&
appId === provider.appId) ||
!(appId && clientSecret && auth0Domain)}
submit>Update</Button>
</svelte:fragment>
</Modal>
@@ -6,21 +6,23 @@
import type { Provider } from '$lib/stores/oauth-providers';
import { addNotification } from '$lib/stores/notifications';
import { onMount } from 'svelte';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
export let showModal = false;
export let provider: Provider;
const projectId = $page.params.project;
let active = false;
let id: string = null;
let enabled: boolean = null;
let appId: string = null;
let secret: string = null;
onMount(() => {
active ??= provider.active;
id ??= provider.id;
enabled ??= provider.enabled;
appId ??= provider.appId;
secret ??= provider.secret;
});
let error: string;
const update = async () => {
@@ -28,26 +30,25 @@
await sdkForConsole.projects.updateOAuth2(
projectId,
provider.name.toLowerCase(),
id,
secret
appId,
secret,
enabled
);
provider.active = active;
provider.id = id;
provider.secret = secret;
showModal = false;
addNotification({
type: 'success',
message: `${provider.name} authentication has been ${
provider.active ? 'enabled' : 'disabled'
provider.enabled ? 'enabled' : 'disabled'
}`
});
provider = null;
invalidate(Dependencies.PROJECT);
} catch ({ message }) {
error = message;
}
};
</script>
<Modal {error} size="big" bind:show={showModal} on:submit={update}>
<Modal {error} size="big" show on:submit={update} on:close>
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -56,13 +57,13 @@
<a class="link" href={provider.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={active} label={active ? 'Enabled' : 'Disabled'} />
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="App ID"
autofocus={true}
placeholder="Enter ID"
bind:value={id} />
bind:value={appId} />
<InputPassword
id="secret"
label="App Secret"
@@ -82,11 +83,13 @@
</div>
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showModal = false)}>Cancel</Button>
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
disabled={!id ||
disabled={!appId ||
!secret ||
(id === provider.id && secret === provider.secret && active === provider.active)}
(appId === provider.appId &&
secret === provider.secret &&
enabled === provider.enabled)}
submit>Update</Button>
</svelte:fragment>
</Modal>
@@ -6,19 +6,20 @@
import type { Provider } from '$lib/stores/oauth-providers';
import { addNotification } from '$lib/stores/notifications';
import { onMount } from 'svelte';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
export let showModal = false;
export let provider: Provider;
let id: string = null;
let active = false;
let appId: string = null;
let enabled: boolean = null;
let clientSecret: string = null;
let tenantID: string = null;
let error: string;
onMount(() => {
id ??= provider.id;
active ??= provider.active;
appId ??= provider.appId;
enabled ??= provider.enabled;
if (provider.secret) ({ clientSecret, tenantID } = JSON.parse(provider.secret));
});
@@ -29,20 +30,18 @@
await sdkForConsole.projects.updateOAuth2(
projectId,
provider.name.toLowerCase(),
id,
secret
appId,
secret,
enabled
);
provider.active = active;
provider.id = id;
provider.secret = secret;
showModal = false;
addNotification({
type: 'success',
message: `${provider.name} authentication has been ${
provider.active ? 'enabled' : 'disabled'
provider.enabled ? 'enabled' : 'disabled'
}`
});
provider = null;
invalidate(Dependencies.PROJECT);
} catch ({ message }) {
error = message;
}
@@ -52,7 +51,7 @@
clientSecret && tenantID ? JSON.stringify({ clientSecret, tenantID }) : provider.secret;
</script>
<Modal {error} on:submit={update} size="big" bind:show={showModal}>
<Modal {error} on:submit={update} size="big" show on:close>
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -62,13 +61,13 @@
visit the docs.
</a>
</p>
<InputSwitch id="state" bind:value={active} label={active ? 'Enabled' : 'Disabled'} />
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="Application (client) ID"
autofocus={true}
placeholder="Enter ID"
bind:value={id} />
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
@@ -93,12 +92,12 @@
</div>
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showModal = false)}>Cancel</Button>
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
disabled={(secret === provider.secret &&
active === provider.active &&
id === provider.id) ||
!(id && clientSecret && tenantID)}
enabled === provider.enabled &&
appId === provider.appId) ||
!(appId && clientSecret && tenantID)}
submit>Update</Button>
</svelte:fragment>
</Modal>
@@ -6,19 +6,20 @@
import type { Provider } from '$lib/stores/oauth-providers';
import { addNotification } from '$lib/stores/notifications';
import { onMount } from 'svelte';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
export let showModal = false;
export let provider: Provider;
let id: string = null;
let active = false;
let appId: string = null;
let enabled: boolean = null;
let clientSecret: string = null;
let oktaDomain: string = null;
let authorizationServerId: string = null;
onMount(() => {
id ??= provider.id;
active ??= provider.active;
appId ??= provider.appId;
enabled ??= provider.enabled;
if (provider.secret)
({ clientSecret, oktaDomain, authorizationServerId } = JSON.parse(provider.secret));
});
@@ -32,19 +33,17 @@
await sdkForConsole.projects.updateOAuth2(
projectId,
provider.name.toLowerCase(),
id,
appId,
secret
);
provider.active = active;
provider.id = id;
provider.secret = secret;
showModal = false;
addNotification({
type: 'success',
message: `${provider.name} authentication has been ${
provider.active ? 'enabled' : 'disabled'
provider.enabled ? 'enabled' : 'disabled'
}`
});
provider = null;
invalidate(Dependencies.PROJECT);
} catch ({ message }) {
error = message;
}
@@ -56,7 +55,7 @@
: provider.secret;
</script>
<Modal {error} on:submit={update} size="big" bind:show={showModal}>
<Modal {error} on:submit={update} size="big" show on:close>
<svelte:fragment slot="header">{provider.name} OAuth2 Settings</svelte:fragment>
<FormList>
<p>
@@ -65,13 +64,13 @@
<a class="link" href={provider.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={active} label={active ? 'Enabled' : 'Disabled'} />
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="Client ID"
autofocus={true}
placeholder="Enter ID"
bind:value={id} />
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
@@ -102,12 +101,12 @@
</div>
</FormList>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showModal = false)}>Cancel</Button>
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
disabled={(secret === provider.secret &&
active === provider.active &&
id === provider.id) ||
!(id && clientSecret && oktaDomain && authorizationServerId)}
enabled === provider.enabled &&
appId === provider.appId) ||
!(appId && clientSecret && oktaDomain && authorizationServerId)}
submit>Update</Button>
</svelte:fragment>
</Modal>
@@ -13,8 +13,8 @@
title: $project?.name
},
{
href: `/console/project-${$project?.$id}/authentication`,
title: 'Authentication'
href: `/console/project-${$project?.$id}/auth`,
title: 'Auth'
}
];
</script>
@@ -5,7 +5,7 @@
import { Cover, CoverTitle } from '$lib/layout';
const projectId = $page.params.project;
const path = `/console/project-${projectId}/authentication`;
const path = `/console/project-${projectId}/auth`;
const tabs = [
{
href: path,
@@ -34,7 +34,7 @@
<Cover>
<svelte:fragment slot="header">
<CoverTitle>Authentication</CoverTitle>
<CoverTitle>Auth</CoverTitle>
</svelte:fragment>
<Tabs>
{#each tabs as tab}
@@ -11,16 +11,14 @@
import { event } from '$lib/actions/analytics';
import type { Provider } from '$lib/stores/oauth-providers';
import { app } from '$lib/stores/app';
import { onMount } from 'svelte';
import { page } from '$app/stores';
const projectId = $page.params.project;
let showModal = false;
onMount(async () => {
$: {
authMethods.load($project);
OAuthProviders.load($project);
});
}
const authUpdate = async (box: AuthMethod) => {
try {
@@ -46,7 +44,7 @@
{#if $authMethods && $OAuthProviders}
<Container>
<CardGrid>
<Heading tag="h2" size="7">Authentication Methods</Heading>
<Heading tag="h2" size="7">Auth Methods</Heading>
<p>Enable the authentication methods you wish to use.</p>
<svelte:fragment slot="aside">
<form class="form">
@@ -56,9 +54,7 @@
label={box.label}
id={box.method}
bind:value={box.value}
on:change={() => {
authUpdate(box);
}} />
on:change={() => authUpdate(box)} />
{/each}
</ul>
</form>
@@ -67,12 +63,11 @@
<section class="common-section">
<h2 class="heading-level-6 common-section">OAuth2 Providers</h2>
<ul class="grid-box common-section">
{#each $OAuthProviders.providers as provider}
{#each $OAuthProviders.providers.sort( (a, b) => (a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1) ) as provider}
<button
class="card u-flex u-flex-vertical u-cross-center"
on:click={() => {
selectedProvider = provider;
showModal = true;
}}
use:event={{
name: 'console_users',
@@ -91,8 +86,8 @@
</div>
<p class="u-margin-block-start-8">{provider.name}</p>
<div class="u-margin-block-start-24">
<Pill success={provider.active}>
{provider.active ? 'enabled' : 'disabled'}
<Pill success={provider.enabled}>
{provider.enabled ? 'enabled' : 'disabled'}
</Pill>
</div>
</button>
@@ -105,6 +100,7 @@
{#if selectedProvider}
<svelte:component
this={selectedProvider.component}
provider={selectedProvider}
bind:showModal />
bind:provider={selectedProvider}
on:close={() => (selectedProvider = null)}
showModal />
{/if}
@@ -27,9 +27,7 @@
const project = $page.params.project;
const teamCreated = async (event: CustomEvent<Models.Team>) => {
await goto(
`${base}/console/project-${project}/authentication/teams/team-${event.detail.$id}`
);
await goto(`${base}/console/project-${project}/auth/teams/team-${event.detail.$id}`);
};
</script>
@@ -58,7 +56,7 @@
<TableBody>
{#each data.teams.teams as team}
<TableRowLink
href={`${base}/console/project-${project}/authentication/teams/team-${team.$id}`}>
href={`${base}/console/project-${project}/auth/teams/team-${team.$id}`}>
<TableCell title="ID">
<div class="u-flex u-gap-12">
<AvatarInitials size={32} name={team.name} />
@@ -77,7 +75,7 @@
<p class="text">Total results: {data.teams.total}</p>
<Pagination
limit={PAGE_LIMIT}
path={`/console/project-${$page.params.project}/authentication/teams`}
path={`/console/project-${$page.params.project}/auth/teams`}
offset={data.offset}
sum={data.teams.total} />
</div>
@@ -87,33 +85,16 @@
<b>Sorry, we couldnt find {data.search}</b>
<p>There are no teams that match your search.</p>
</div>
<Button
secondary
href={`/console/project-${$page.params.project}/authentication/teams`}>
<Button secondary href={`/console/project-${$page.params.project}/auth/teams`}>
Clear Search
</Button>
</EmptySearch>
{:else}
<Empty isButton single on:click={() => (showCreate = true)}>
<div
class="common-section"
use:event={{
name: 'console_users',
action: 'click_create',
parameters: {
type: 'team'
}
}}>
<div class="u-text-center common-section">
<p>Create your first Team to get started.</p>
<p>Need a hand? Check out our documentation.</p>
</div>
<div class="u-flex u-gap-16 common-section u-main-center">
<Button external href="#/" text>Documentation</Button>
<Button secondary>Create team</Button>
</div>
</div>
</Empty>
<Empty
single
on:click={() => (showCreate = true)}
href="https://appwrite.io/docs/client/teams"
target="team" />
{/if}
</Container>
@@ -13,6 +13,7 @@ export const load: PageLoad = async ({ params, parent, url }) => {
return {
offset,
search,
page,
teams: sdkForProject.teams.list(
[Query.limit(PAGE_LIMIT), Query.offset(offset), Query.orderDesc('$createdAt')],
search
@@ -0,0 +1,10 @@
<script lang="ts">
import type { PageData } from './$types';
import { page } from '$app/stores';
import { Activity } from '$lib/layout';
export let data: PageData;
const path = `/console/project-${$page.params.project}/auth/teams/team-${$page.params.team}/activity`;
</script>
<Activity {path} logs={data.logs} offset={data.offset} />
@@ -14,11 +14,11 @@
title: $project?.name
},
{
href: `/console/project-${$project?.$id}/authentication`,
title: 'Authentication'
href: `/console/project-${$project?.$id}/auth`,
title: 'Auth'
},
{
href: `/console/project-${$project?.$id}/authentication/teams-${$team?.$id}`,
href: `/console/project-${$project?.$id}/auth/teams-${$team?.$id}`,
title: $team?.name
}
];
@@ -15,7 +15,7 @@
let error: string;
const create = async () => {
const url = `${$page.url.origin}/console/project-${$page.params.project}/authentication/teams-${$page.params.team}/members`;
const url = `${$page.url.origin}/console/project-${$page.params.project}/auth/teams-${$page.params.team}/members`;
try {
const user = await sdkForProject.teams.createMembership(
@@ -23,7 +23,7 @@
showDelete = false;
dispatch('deleted');
await goto(
`${base}/console/project-${$page.params.project}/authentication/teams/team-${selectedMembership.teamId}/members`
`${base}/console/project-${$page.params.project}/auth/teams/team-${selectedMembership.teamId}/members`
);
} catch (error) {
addNotification({
@@ -15,7 +15,7 @@
try {
await sdkForProject.teams.delete(team.$id);
showDelete = false;
await goto(`${base}/console/project-${$page.params.project}/authentication/teams`);
await goto(`${base}/console/project-${$page.params.project}/auth/teams`);
} catch (error) {
addNotification({
type: 'error',
@@ -8,7 +8,7 @@
const projectId = $page.params.project;
const teamId = $page.params.team;
const path = `/console/project-${projectId}/authentication/teams/team-${teamId}`;
const path = `/console/project-${projectId}/auth/teams/team-${teamId}`;
const tabs = [
{
href: path,
@@ -29,7 +29,7 @@
<Cover>
<svelte:fragment slot="header">
<CoverTitle href={`/console/project-${projectId}/authentication/teams`}>
<CoverTitle href={`/console/project-${projectId}/auth/teams`}>
{$team?.name}
</CoverTitle>
<Copy value={$team?.$id}>
@@ -29,9 +29,7 @@
const project = $page.params.project;
async function memberCreated(event: CustomEvent<Models.Membership>) {
await goto(
`${base}/console/project-${project}/authentication/teams-${event.detail.teamId}/members`
);
await goto(`${base}/console/project-${project}/auth/teams-${event.detail.teamId}/members`);
}
</script>
@@ -53,7 +51,7 @@
<TableBody>
{#each data.memberships.memberships as membership}
<TableRowLink
href={`${base}/console/project-${project}/authentication/user-${membership.userId}`}>
href={`${base}/console/project-${project}/auth/user-${membership.userId}`}>
<TableCellText title="Name">
<div class="u-flex u-gap-12">
<AvatarInitials size={32} name={membership.userName} />
@@ -83,7 +81,7 @@
<p class="text">Total results: {data.memberships.total}</p>
<Pagination
limit={PAGE_LIMIT}
path={`/console/project-${$page.params.project}/authentication/teams/team-${$page.params.team}/members`}
path={`/console/project-${$page.params.project}/auth/teams/team-${$page.params.team}/members`}
offset={data.offset}
sum={data.memberships.total} />
</div>
@@ -95,13 +93,15 @@
</div>
<Button
secondary
href={`/console/project-${$page.params.project}/authentication/teams/team-${$page.params.team}/members`}
href={`/console/project-${$page.params.project}/auth/teams/team-${$page.params.team}/members`}
>Clear Search</Button>
</EmptySearch>
{:else}
<Empty isButton single on:click={() => (showCreate = true)}>
<p>Add your first member to get started</p>
</Empty>
<Empty
single
href="https://appwrite.io/docs/client/teams?sdk=web-default#teamsCreateMembership"
target="membership"
on:click={() => (showCreate = true)} />
{/if}
</Container>
@@ -14,7 +14,7 @@
<Usage
title="Users"
path={`/console/project-${$page.params.project}/authentication/usage`}
path={`/console/project-${$page.params.project}/auth/usage`}
{count}
{created}
{read}
@@ -28,8 +28,10 @@
$: if (prefs) {
if (JSON.stringify(prefs) !== JSON.stringify(Object.entries($user.prefs))) {
if (prefs[prefs.length - 1][0] && prefs[prefs.length - 1][1]) {
if (!!prefs[prefs.length - 1][0] && !!prefs[prefs.length - 1][1]) {
arePrefsDisabled = false;
} else {
arePrefsDisabled = true;
}
} else {
arePrefsDisabled = true;
@@ -264,14 +266,16 @@
</DropListItem>
</svelte:fragment>
</DropList>
{:else if !$user.phone}
{:else if $user.email}
<Button secondary on:click={() => updateVerificationEmail()}>
{$user.emailVerification ? 'Unverify' : 'Verify'} account
</Button>
{:else if !$user.email}
{:else if $user.phone}
test
<Button secondary on:click={() => updateVerificationPhone()}>
{$user.phoneVerification ? 'Unverify' : 'Verify'} account
</Button>{/if}
</Button>
{/if}
{/if}
</svelte:fragment>
</CardGrid>
@@ -420,6 +424,7 @@
{/if}
</ul>
<Button
noMargin
text
on:click={() => {
if (prefs[prefs.length - 1][0] && prefs[prefs.length - 1][1]) {
@@ -0,0 +1,10 @@
<script lang="ts">
import type { PageData } from './$types';
import { page } from '$app/stores';
import { Activity } from '$lib/layout';
export let data: PageData;
const path = `/console/project-${$page.params.project}/auth/user-${$page.params.user}/activity`;
</script>
<Activity {path} logs={data.logs} offset={data.offset} />
@@ -11,6 +11,9 @@ export const load: PageLoad = async ({ params, parent }) => {
return {
offset,
logs: await sdkForProject.account.listLogs([Query.limit(PAGE_LIMIT), Query.offset(offset)])
logs: await sdkForProject.users.listLogs(params.user, [
Query.limit(PAGE_LIMIT),
Query.offset(offset)
])
};
};
@@ -14,11 +14,11 @@
title: $project?.name
},
{
href: `/console/project-${$project?.$id}/authentication`,
title: 'Authentication'
href: `/console/project-${$project?.$id}/auth`,
title: 'Auth'
},
{
href: `/console/project-${$project?.$id}/authentication/user-${$user?.$id}`,
href: `/console/project-${$project?.$id}/auth/user-${$user?.$id}`,
title: $user?.name
}
];
@@ -19,7 +19,7 @@
message: `All memberships have been deleted`
});
await goto(
`${base}/console/project-${$page.params.project}/authentication/${$user.$id}/membeships`
`${base}/console/project-${$page.params.project}/auth/${$user.$id}/membeships`
);
} catch (error) {
addNotification({

Some files were not shown because too many files have changed in this diff Show More