mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
+4
-3
@@ -19,11 +19,11 @@
|
||||
"e2e:ui": "playwright test tests/e2e --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"@appwrite.io/console": "1.4.1",
|
||||
"@appwrite.io/console": "npm:khushboo-console@^0.0.9",
|
||||
"@appwrite.io/pink": "0.25.0",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "1.0.0-next.5",
|
||||
"@appwrite.io/pink-svelte": "1.0.0-next.54",
|
||||
"@appwrite.io/pink-icons-svelte": "1.0.0-next.7",
|
||||
"@appwrite.io/pink-svelte": "1.0.0-next.82",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.33.1",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
@@ -57,6 +57,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"@vitest/ui": "^1.6.0",
|
||||
"ansicolor": "^2.0.3",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.44.1",
|
||||
|
||||
Generated
+298
-18
@@ -9,8 +9,8 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@appwrite.io/console':
|
||||
specifier: 1.4.1
|
||||
version: 1.4.1
|
||||
specifier: npm:khushboo-console@^0.0.9
|
||||
version: khushboo-console@0.0.9
|
||||
'@appwrite.io/pink':
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
@@ -18,11 +18,11 @@ importers:
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
'@appwrite.io/pink-icons-svelte':
|
||||
specifier: 1.0.0-next.5
|
||||
version: 1.0.0-next.5(svelte@4.2.19)
|
||||
specifier: 1.0.0-next.7
|
||||
version: 1.0.0-next.7(svelte@4.2.19)
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: 1.0.0-next.54
|
||||
version: 1.0.0-next.54(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
specifier: 1.0.0-next.82
|
||||
version: 1.0.0-next.82(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
@@ -117,6 +117,9 @@ importers:
|
||||
'@vitest/ui':
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0(vitest@1.6.0)
|
||||
ansicolor:
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.3
|
||||
eslint:
|
||||
specifier: ^8.57.1
|
||||
version: 8.57.1
|
||||
@@ -199,19 +202,16 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@appwrite.io/console@1.4.1':
|
||||
resolution: {integrity: sha512-Loh1VpMmj1Evy0F9teOarn0f13toh2KZHOrUdPMzeAOsIoxIQl1+SFVijtHvBGEvrOH1dzBGs8+6y2CRNNomlQ==}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@1.0.0-next.5':
|
||||
resolution: {integrity: sha512-hihcDmAlVuXhhogPoUuuWPCtINzl+/HfjDdsFyvZsxf70O0PgczNSSICRJ/VQ8A87IsdEimpRcRb5OnFPUEXDA==}
|
||||
'@appwrite.io/pink-icons-svelte@1.0.0-next.7':
|
||||
resolution: {integrity: sha512-Bmff3YGEliIetqc3UyjzJNLg88FIC6UXYwTzGqHm+0QLCRJxwAFpTkfTCLIqMe5PUqGshq6jM9SPyCCSVVS3aw==}
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons@0.25.0':
|
||||
resolution: {integrity: sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q==}
|
||||
|
||||
'@appwrite.io/pink-svelte@1.0.0-next.54':
|
||||
resolution: {integrity: sha512-VrUbCsuGdVRsgBpOf459t1sDmJrhrv+34h3/Sd+iuPdc/GZM06u18btlQF7QckQJ/ru1DxIJE+CuvD/RUg9iug==}
|
||||
'@appwrite.io/pink-svelte@1.0.0-next.82':
|
||||
resolution: {integrity: sha512-2+e69Vk3rz7Rg4PIC3qC3Q7NZKXZjAK0EE3IB/TCTiiGFjbS3iw+zXX1o/ScCR4k1vBp/p0P4l4ncrwcQ0pGXg==}
|
||||
peerDependencies:
|
||||
react-dom: ^18.0.0
|
||||
svelte: ^4.0.0
|
||||
@@ -1102,6 +1102,21 @@ packages:
|
||||
resolution: {integrity: sha512-+5bsLFRKOZzBp68XigoNE1pJ3tJ4gt2jXluApu54ui0N/yjfqGQ7LQTD7nL4tmJvB5Agwi0e7M7+fcxe9gSgBA==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
'@shikijs/core@1.22.0':
|
||||
resolution: {integrity: sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==}
|
||||
|
||||
'@shikijs/engine-javascript@1.22.0':
|
||||
resolution: {integrity: sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==}
|
||||
|
||||
'@shikijs/engine-oniguruma@1.22.0':
|
||||
resolution: {integrity: sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==}
|
||||
|
||||
'@shikijs/types@1.22.0':
|
||||
resolution: {integrity: sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==}
|
||||
|
||||
'@shikijs/vscode-textmate@9.3.0':
|
||||
resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==}
|
||||
|
||||
'@sinclair/typebox@0.27.8':
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
|
||||
@@ -1211,6 +1226,9 @@ packages:
|
||||
'@types/graceful-fs@4.1.9':
|
||||
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6':
|
||||
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
|
||||
|
||||
@@ -1220,6 +1238,9 @@ packages:
|
||||
'@types/istanbul-reports@3.0.4':
|
||||
resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
|
||||
|
||||
'@types/mdast@4.0.4':
|
||||
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||
|
||||
'@types/mysql@2.15.26':
|
||||
resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==}
|
||||
|
||||
@@ -1244,6 +1265,9 @@ packages:
|
||||
'@types/stack-utils@2.0.3':
|
||||
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
|
||||
|
||||
'@types/unist@3.0.3':
|
||||
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||
|
||||
'@types/yargs-parser@21.0.3':
|
||||
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
|
||||
|
||||
@@ -1436,6 +1460,9 @@ packages:
|
||||
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
ansicolor@2.0.3:
|
||||
resolution: {integrity: sha512-pzusTqk9VHrjgMCcTPDTTvfJfx6Q3+L5tQ6yKC8Diexmoit4YROTFIkxFvRTNL9y5s0Q8HrSrgerCD5bIC+Kiw==}
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -1559,6 +1586,9 @@ packages:
|
||||
caniuse-lite@1.0.30001667:
|
||||
resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==}
|
||||
|
||||
ccount@2.0.1:
|
||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||
|
||||
chai@4.5.0:
|
||||
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -1579,6 +1609,12 @@ packages:
|
||||
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
character-entities-html4@2.1.0:
|
||||
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
|
||||
|
||||
character-entities-legacy@3.0.0:
|
||||
resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
|
||||
|
||||
check-error@1.0.3:
|
||||
resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
|
||||
|
||||
@@ -1628,6 +1664,9 @@ packages:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
comma-separated-tokens@2.0.3:
|
||||
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
@@ -1742,6 +1781,9 @@ packages:
|
||||
devalue@5.1.1:
|
||||
resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
|
||||
|
||||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
diff-sequences@29.6.3:
|
||||
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -2096,6 +2138,12 @@ packages:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hast-util-to-html@9.0.3:
|
||||
resolution: {integrity: sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==}
|
||||
|
||||
hast-util-whitespace@3.0.0:
|
||||
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
|
||||
|
||||
html-encoding-sniffer@3.0.0:
|
||||
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2103,6 +2151,9 @@ packages:
|
||||
html-escaper@2.0.2:
|
||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||
|
||||
html-void-elements@3.0.0:
|
||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||
|
||||
http-proxy-agent@5.0.0:
|
||||
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -2479,6 +2530,9 @@ packages:
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
khushboo-console@0.0.9:
|
||||
resolution: {integrity: sha512-igFlM6MCFvbhG0kqzH+/xP9xJMjTN3L0spyQPEmfQkZWG55vqCmdixhBff9aNn4UNp38UOL4U/STXIyF7l7MMA==}
|
||||
|
||||
kleur@3.0.3:
|
||||
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2568,6 +2622,9 @@ packages:
|
||||
makeerror@1.0.12:
|
||||
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
|
||||
|
||||
mdast-util-to-hast@13.2.0:
|
||||
resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
|
||||
|
||||
mdn-data@2.0.30:
|
||||
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
|
||||
|
||||
@@ -2578,6 +2635,21 @@ packages:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
micromark-util-character@2.1.0:
|
||||
resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
|
||||
|
||||
micromark-util-encode@2.0.0:
|
||||
resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
|
||||
|
||||
micromark-util-sanitize-uri@2.0.0:
|
||||
resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
|
||||
|
||||
micromark-util-symbol@2.0.0:
|
||||
resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
|
||||
|
||||
micromark-util-types@2.0.0:
|
||||
resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
|
||||
|
||||
micromatch@4.0.8:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
@@ -2723,6 +2795,9 @@ packages:
|
||||
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
oniguruma-to-js@0.4.3:
|
||||
resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==}
|
||||
|
||||
optionator@0.9.4:
|
||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -2932,6 +3007,9 @@ packages:
|
||||
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
property-information@6.5.0:
|
||||
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
@@ -2985,6 +3063,9 @@ packages:
|
||||
regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
|
||||
regex@4.3.3:
|
||||
resolution: {integrity: sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==}
|
||||
|
||||
regexp.prototype.flags@1.5.3:
|
||||
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3105,6 +3186,9 @@ packages:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shiki@1.22.0:
|
||||
resolution: {integrity: sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==}
|
||||
|
||||
shimmer@1.2.1:
|
||||
resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
|
||||
|
||||
@@ -3162,6 +3246,9 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
space-separated-tokens@2.0.2:
|
||||
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
@@ -3192,6 +3279,9 @@ packages:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3429,6 +3519,9 @@ packages:
|
||||
resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
trim-lines@3.0.1:
|
||||
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||
|
||||
ts-api-utils@1.3.0:
|
||||
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -3472,6 +3565,21 @@ packages:
|
||||
undici-types@6.19.8:
|
||||
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
|
||||
|
||||
unist-util-is@6.0.0:
|
||||
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
|
||||
|
||||
unist-util-position@5.0.0:
|
||||
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
|
||||
|
||||
unist-util-stringify-position@4.0.0:
|
||||
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
||||
|
||||
unist-util-visit-parents@6.0.1:
|
||||
resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
|
||||
|
||||
unist-util-visit@5.0.0:
|
||||
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
|
||||
|
||||
universalify@0.2.0:
|
||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
@@ -3503,6 +3611,12 @@ packages:
|
||||
resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
|
||||
engines: {node: '>=10.12.0'}
|
||||
|
||||
vfile-message@4.0.2:
|
||||
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
|
||||
|
||||
vfile@6.0.3:
|
||||
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
||||
|
||||
vite-node@1.6.0:
|
||||
resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -3705,6 +3819,9 @@ packages:
|
||||
zrender@5.6.0:
|
||||
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==}
|
||||
|
||||
zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@adobe/css-tools@4.4.0': {}
|
||||
@@ -3748,20 +3865,20 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@appwrite.io/console@1.4.1': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@1.0.0-next.5(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-icons-svelte@1.0.0-next.7(svelte@4.2.19)':
|
||||
dependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
'@appwrite.io/pink-icons@0.25.0': {}
|
||||
|
||||
'@appwrite.io/pink-svelte@1.0.0-next.54(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
'@appwrite.io/pink-svelte@1.0.0-next.82(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': 1.0.0-next.5(svelte@4.2.19)
|
||||
'@appwrite.io/pink-icons-svelte': 1.0.0-next.7(svelte@4.2.19)
|
||||
'@floating-ui/dom': 1.6.11
|
||||
'@melt-ui/svelte': 0.83.0(svelte@4.2.19)
|
||||
pretty-bytes: 6.1.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
shiki: 1.22.0
|
||||
svelte: 4.2.19
|
||||
|
||||
'@appwrite.io/pink@0.25.0':
|
||||
@@ -4833,6 +4950,33 @@ snapshots:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@shikijs/core@1.22.0':
|
||||
dependencies:
|
||||
'@shikijs/engine-javascript': 1.22.0
|
||||
'@shikijs/engine-oniguruma': 1.22.0
|
||||
'@shikijs/types': 1.22.0
|
||||
'@shikijs/vscode-textmate': 9.3.0
|
||||
'@types/hast': 3.0.4
|
||||
hast-util-to-html: 9.0.3
|
||||
|
||||
'@shikijs/engine-javascript@1.22.0':
|
||||
dependencies:
|
||||
'@shikijs/types': 1.22.0
|
||||
'@shikijs/vscode-textmate': 9.3.0
|
||||
oniguruma-to-js: 0.4.3
|
||||
|
||||
'@shikijs/engine-oniguruma@1.22.0':
|
||||
dependencies:
|
||||
'@shikijs/types': 1.22.0
|
||||
'@shikijs/vscode-textmate': 9.3.0
|
||||
|
||||
'@shikijs/types@1.22.0':
|
||||
dependencies:
|
||||
'@shikijs/vscode-textmate': 9.3.0
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
'@shikijs/vscode-textmate@9.3.0': {}
|
||||
|
||||
'@sinclair/typebox@0.27.8': {}
|
||||
|
||||
'@sinonjs/commons@3.0.1':
|
||||
@@ -4968,6 +5112,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 22.7.5
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||
|
||||
'@types/istanbul-lib-report@3.0.3':
|
||||
@@ -4978,6 +5126,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/istanbul-lib-report': 3.0.3
|
||||
|
||||
'@types/mdast@4.0.4':
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
'@types/mysql@2.15.26':
|
||||
dependencies:
|
||||
'@types/node': 22.7.5
|
||||
@@ -5004,6 +5156,8 @@ snapshots:
|
||||
|
||||
'@types/stack-utils@2.0.3': {}
|
||||
|
||||
'@types/unist@3.0.3': {}
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
'@types/yargs@17.0.33':
|
||||
@@ -5260,6 +5414,8 @@ snapshots:
|
||||
|
||||
ansi-styles@5.2.0: {}
|
||||
|
||||
ansicolor@2.0.3: {}
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
@@ -5403,6 +5559,8 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001667: {}
|
||||
|
||||
ccount@2.0.1: {}
|
||||
|
||||
chai@4.5.0:
|
||||
dependencies:
|
||||
assertion-error: 1.1.0
|
||||
@@ -5431,6 +5589,10 @@ snapshots:
|
||||
|
||||
char-regex@1.0.2: {}
|
||||
|
||||
character-entities-html4@2.1.0: {}
|
||||
|
||||
character-entities-legacy@3.0.0: {}
|
||||
|
||||
check-error@1.0.3:
|
||||
dependencies:
|
||||
get-func-name: 2.0.2
|
||||
@@ -5489,6 +5651,8 @@ snapshots:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
comma-separated-tokens@2.0.3: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
confbox@0.1.8: {}
|
||||
@@ -5604,6 +5768,10 @@ snapshots:
|
||||
|
||||
devalue@5.1.1: {}
|
||||
|
||||
devlop@1.1.0:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
diff-sequences@29.6.3: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
@@ -6010,12 +6178,32 @@ snapshots:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
hast-util-to-html@9.0.3:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/unist': 3.0.3
|
||||
ccount: 2.0.1
|
||||
comma-separated-tokens: 2.0.3
|
||||
hast-util-whitespace: 3.0.0
|
||||
html-void-elements: 3.0.0
|
||||
mdast-util-to-hast: 13.2.0
|
||||
property-information: 6.5.0
|
||||
space-separated-tokens: 2.0.2
|
||||
stringify-entities: 4.0.4
|
||||
zwitch: 2.0.4
|
||||
|
||||
hast-util-whitespace@3.0.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
html-encoding-sniffer@3.0.0:
|
||||
dependencies:
|
||||
whatwg-encoding: 2.0.0
|
||||
|
||||
html-escaper@2.0.2: {}
|
||||
|
||||
html-void-elements@3.0.0: {}
|
||||
|
||||
http-proxy-agent@5.0.0:
|
||||
dependencies:
|
||||
'@tootallnate/once': 2.0.0
|
||||
@@ -6582,6 +6770,8 @@ snapshots:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
||||
khushboo-console@0.0.9: {}
|
||||
|
||||
kleur@3.0.3: {}
|
||||
|
||||
kleur@4.1.5: {}
|
||||
@@ -6662,12 +6852,41 @@ snapshots:
|
||||
dependencies:
|
||||
tmpl: 1.0.5
|
||||
|
||||
mdast-util-to-hast@13.2.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/mdast': 4.0.4
|
||||
'@ungap/structured-clone': 1.2.0
|
||||
devlop: 1.1.0
|
||||
micromark-util-sanitize-uri: 2.0.0
|
||||
trim-lines: 3.0.1
|
||||
unist-util-position: 5.0.0
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.3
|
||||
|
||||
mdn-data@2.0.30: {}
|
||||
|
||||
merge-stream@2.0.0: {}
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
micromark-util-character@2.1.0:
|
||||
dependencies:
|
||||
micromark-util-symbol: 2.0.0
|
||||
micromark-util-types: 2.0.0
|
||||
|
||||
micromark-util-encode@2.0.0: {}
|
||||
|
||||
micromark-util-sanitize-uri@2.0.0:
|
||||
dependencies:
|
||||
micromark-util-character: 2.1.0
|
||||
micromark-util-encode: 2.0.0
|
||||
micromark-util-symbol: 2.0.0
|
||||
|
||||
micromark-util-symbol@2.0.0: {}
|
||||
|
||||
micromark-util-types@2.0.0: {}
|
||||
|
||||
micromatch@4.0.8:
|
||||
dependencies:
|
||||
braces: 3.0.3
|
||||
@@ -6780,6 +6999,10 @@ snapshots:
|
||||
dependencies:
|
||||
mimic-fn: 4.0.0
|
||||
|
||||
oniguruma-to-js@0.4.3:
|
||||
dependencies:
|
||||
regex: 4.3.3
|
||||
|
||||
optionator@0.9.4:
|
||||
dependencies:
|
||||
deep-is: 0.1.4
|
||||
@@ -6961,6 +7184,8 @@ snapshots:
|
||||
kleur: 3.0.3
|
||||
sisteransi: 1.0.5
|
||||
|
||||
property-information@6.5.0: {}
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
psl@1.9.0: {}
|
||||
@@ -7008,6 +7233,8 @@ snapshots:
|
||||
|
||||
regenerator-runtime@0.14.1: {}
|
||||
|
||||
regex@4.3.3: {}
|
||||
|
||||
regexp.prototype.flags@1.5.3:
|
||||
dependencies:
|
||||
call-bind: 1.0.7
|
||||
@@ -7142,6 +7369,15 @@ snapshots:
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
shiki@1.22.0:
|
||||
dependencies:
|
||||
'@shikijs/core': 1.22.0
|
||||
'@shikijs/engine-javascript': 1.22.0
|
||||
'@shikijs/engine-oniguruma': 1.22.0
|
||||
'@shikijs/types': 1.22.0
|
||||
'@shikijs/vscode-textmate': 9.3.0
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
shimmer@1.2.1: {}
|
||||
|
||||
side-channel@1.0.6:
|
||||
@@ -7200,6 +7436,8 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
space-separated-tokens@2.0.2: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
sswr@2.0.0(svelte@4.2.19):
|
||||
@@ -7230,6 +7468,11 @@ snapshots:
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
dependencies:
|
||||
character-entities-html4: 2.1.0
|
||||
character-entities-legacy: 3.0.0
|
||||
|
||||
strip-ansi@6.0.1:
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
@@ -7423,6 +7666,8 @@ snapshots:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
trim-lines@3.0.1: {}
|
||||
|
||||
ts-api-utils@1.3.0(typescript@5.6.3):
|
||||
dependencies:
|
||||
typescript: 5.6.3
|
||||
@@ -7449,6 +7694,29 @@ snapshots:
|
||||
|
||||
undici-types@6.19.8: {}
|
||||
|
||||
unist-util-is@6.0.0:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
unist-util-position@5.0.0:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
unist-util-stringify-position@4.0.0:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
unist-util-visit-parents@6.0.1:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
unist-util-is: 6.0.0
|
||||
|
||||
unist-util-visit@5.0.0:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
unist-util-is: 6.0.0
|
||||
unist-util-visit-parents: 6.0.1
|
||||
|
||||
universalify@0.2.0: {}
|
||||
|
||||
unplugin@1.0.1:
|
||||
@@ -7485,6 +7753,16 @@ snapshots:
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
convert-source-map: 2.0.0
|
||||
|
||||
vfile-message@4.0.2:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
unist-util-stringify-position: 4.0.0
|
||||
|
||||
vfile@6.0.3:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
vfile-message: 4.0.2
|
||||
|
||||
vite-node@1.6.0(@types/node@22.7.5)(sass@1.79.4):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
@@ -7675,3 +7953,5 @@ snapshots:
|
||||
zrender@5.6.0:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
|
||||
zwitch@2.0.4: {}
|
||||
|
||||
@@ -323,5 +323,16 @@ export enum Submit {
|
||||
MessagingTopicSubscriberDelete = 'submit_messaging_topic_subscriber_delete',
|
||||
ApplyQuickFilter = 'submit_apply_quick_filter',
|
||||
RequestBAA = 'submit_request_baa',
|
||||
RequestSoc2 = 'submit_request_soc2'
|
||||
RequestSoc2 = 'submit_request_soc2',
|
||||
SiteCreate = 'submit_site_create',
|
||||
SiteDelete = 'submit_site_delete',
|
||||
SiteUpdateName = 'submit_site_update_name',
|
||||
SiteUpdatePermissions = 'submit_site_update_permissions',
|
||||
SiteUpdateSchedule = 'submit_site_update_schedule',
|
||||
SiteUpdateConfiguration = 'submit_site_update_configuration',
|
||||
SiteUpdateLogging = 'submit_site_update_logging',
|
||||
SiteUpdateTimeout = 'submit_site_update_timeout',
|
||||
SiteUpdateEvents = 'submit_site_update_events',
|
||||
SiteUpdateScopes = 'submit_site_key_update_scopes',
|
||||
SiteConnectRepo = 'submit_site_connect_repo'
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ const groups = [
|
||||
'buckets',
|
||||
'files',
|
||||
'misc',
|
||||
'settings'
|
||||
'settings',
|
||||
'sites'
|
||||
] as const;
|
||||
|
||||
export type CommandGroup = (typeof groups)[number];
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
<Button round text on:click={removeCoupon}><span class="icon-x"></span></Button>
|
||||
<Button icon text on:click={removeCoupon}><span class="icon-x"></span></Button>
|
||||
</div>
|
||||
{/if}
|
||||
</FormList>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
value={BillingPlan.FREE}
|
||||
tooltipShow={anyOrgFree}
|
||||
tooltipText="You are limited to 1 Free organization per account."
|
||||
padding={1.5}>
|
||||
padding="m">
|
||||
<svelte:fragment slot="custom" let:disabled>
|
||||
<div
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
@@ -55,7 +55,7 @@
|
||||
disabled={!selfService}
|
||||
bind:group={billingPlan}
|
||||
value={BillingPlan.PRO}
|
||||
padding={1.5}>
|
||||
padding="m">
|
||||
<svelte:fragment slot="custom" let:disabled>
|
||||
<div
|
||||
class="u-flex u-flex-vertical u-gap-4 u-width-full-line"
|
||||
@@ -82,7 +82,7 @@
|
||||
name="plan"
|
||||
bind:group={billingPlan}
|
||||
value={BillingPlan.SCALE}
|
||||
padding={1.5}>
|
||||
padding="m">
|
||||
<svelte:fragment slot="custom">
|
||||
<div class="u-flex u-flex-vertical u-gap-4 u-width-full-line">
|
||||
<h4 class="body-text-2 u-bold">
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Card, Layout } from '@appwrite.io/pink-svelte';
|
||||
import type Base from '@appwrite.io/pink-svelte/dist/card/Base.svelte';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
type BaseProps = {
|
||||
isTile?: boolean;
|
||||
@@ -19,7 +21,7 @@
|
||||
isButton?: never;
|
||||
};
|
||||
|
||||
type $$Props = BaseProps & (ButtonProps | AnchorProps | BaseProps);
|
||||
type $$Props = BaseProps & (ButtonProps | AnchorProps | BaseProps) & ComponentProps<Base>;
|
||||
|
||||
export let isTile = false;
|
||||
export let isDashed = false;
|
||||
@@ -29,24 +31,33 @@
|
||||
let classes = '';
|
||||
export { classes as class };
|
||||
export let style = '';
|
||||
export let padding: $$Props['padding'] = 'm';
|
||||
export let radius: $$Props['radius'] = 'm';
|
||||
export let variant: $$Props['variant'] = 'primary';
|
||||
|
||||
$: resolvedClasses = [!isTile && 'common-section', classes].filter(Boolean).join(' ');
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<Card.Link class={resolvedClasses} on:click {href} {style}>
|
||||
<Card.Link class={resolvedClasses} {href} {style} {padding} {radius} {variant} on:click>
|
||||
<Layout.Stack gap="xl">
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
</Card.Link>
|
||||
{:else if isButton}
|
||||
<Card.Button class={resolvedClasses} {style} on:click>
|
||||
<Card.Button class={resolvedClasses} {style} {padding} {radius} {variant} on:click>
|
||||
<Layout.Stack gap="xl">
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
</Card.Button>
|
||||
{:else}
|
||||
<Card.Base class={resolvedClasses} {style}>
|
||||
<Card.Base
|
||||
class={resolvedClasses}
|
||||
{style}
|
||||
border={isDashed ? 'dashed' : 'solid'}
|
||||
{padding}
|
||||
{radius}
|
||||
{variant}>
|
||||
<Layout.Stack gap="xl">
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<li class="collapsible-item" class:is-info={isInfo}>
|
||||
{#if noContent}
|
||||
<div class="collapsible-wrapper">
|
||||
<div class={`collapsible-button u-gap-${gap}`}>
|
||||
<div class={`collapsible-button u-gap-${gap}`} style="padding: 0">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,6 +21,7 @@
|
||||
<!-- svelte-ignore a11y-no-redundant-roles -->
|
||||
<summary
|
||||
class={`collapsible-button u-position-relative u-gap-${gap}`}
|
||||
style="padding: 0"
|
||||
on:keyup={clickOnEnter}
|
||||
on:click
|
||||
role="button"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
{#if single}
|
||||
<Card.Base padding="none">
|
||||
<slot name="media" />
|
||||
<Empty
|
||||
title={`Create your first ${target}`}
|
||||
description="Need a hand? Learn more in our documentation.">
|
||||
@@ -34,9 +35,15 @@
|
||||
{href}
|
||||
text
|
||||
event="empty_documentation"
|
||||
size="small"
|
||||
size="s"
|
||||
ariaLabel="create {target}">Documentation</Button>
|
||||
<Button secondary on:mousedown on:click on:click={track} disabled={!allowCreate} size="small">
|
||||
<Button
|
||||
secondary
|
||||
on:mousedown
|
||||
on:click
|
||||
on:click={track}
|
||||
disabled={!allowCreate}
|
||||
size="s">
|
||||
Create {target}
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { Card, Empty, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { EmptySearch } from '.';
|
||||
import { queries } from './filters';
|
||||
|
||||
export let resource: string;
|
||||
</script>
|
||||
|
||||
<EmptySearch hidePages>
|
||||
<div class="common-section">
|
||||
<div class="u-text-center common-section">
|
||||
<b class="body-text-2 u-bold">Sorry, we couldn't find any {resource}.</b>
|
||||
<p>There are no {resource} that match your filters.</p>
|
||||
</div>
|
||||
<div class="u-flex common-section u-main-center">
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => {
|
||||
queries.clearAll();
|
||||
queries.apply();
|
||||
}}>
|
||||
Clear filters
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</EmptySearch>
|
||||
<Card.Base padding="none">
|
||||
<Empty
|
||||
title={`Sorry, we couldn't find any ${resource}`}
|
||||
description={`There are no ${resource} that match your filters.`}
|
||||
type="secondary">
|
||||
<svelte:fragment slot="actions">
|
||||
<Layout.Stack direction="row" justifyContent="center">
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => {
|
||||
queries.clearAll();
|
||||
queries.apply();
|
||||
}}>
|
||||
Clear filters
|
||||
</Button>
|
||||
<slot />
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<Card.Base padding="none">
|
||||
<Empty
|
||||
title={`Sorry, we couldn't find ‘${search}’`}
|
||||
title={`Sorry, we couldn't find ${search ? `‘${search}’` : `any ${target}`}`}
|
||||
description={`There are no ${target} that match your search.`}
|
||||
type="secondary">
|
||||
<svelte:fragment slot="actions">
|
||||
|
||||
@@ -158,13 +158,13 @@
|
||||
{/if}
|
||||
<div class="u-flex u-gap-8">
|
||||
{#if singleCondition}
|
||||
<Button text on:click={toggleDropdown}>Cancel</Button>
|
||||
<Button size="s" text on:click={toggleDropdown}>Cancel</Button>
|
||||
{:else}
|
||||
<Button disabled={applied === 0} text on:click={clearAll}>
|
||||
<Button size="s" disabled={applied === 0} text on:click={clearAll}>
|
||||
Clear all
|
||||
</Button>
|
||||
{/if}
|
||||
<Button on:click={apply} disabled={isButtonDisabled}>Apply</Button>
|
||||
<Button size="s" on:click={apply} disabled={isButtonDisabled}>Apply</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,7 +174,7 @@
|
||||
|
||||
<div class="is-only-mobile">
|
||||
<slot name="mobile" {disabled} toggle={toggleMobileModal}>
|
||||
<Button secondary on:click={toggleMobileModal} {fullWidthMobile}>
|
||||
<Button size="s" secondary on:click={toggleMobileModal} {fullWidthMobile}>
|
||||
<i class="icon-filter u-opacity-50" />
|
||||
Filters
|
||||
{#if applied > 0}
|
||||
@@ -219,11 +219,12 @@
|
||||
{/if}
|
||||
<div class="u-flex u-gap-8">
|
||||
{#if singleCondition}
|
||||
<Button text on:click={() => (showFiltersMobile = false)}>Cancel</Button>
|
||||
<Button size="s" text on:click={() => (showFiltersMobile = false)}
|
||||
>Cancel</Button>
|
||||
{:else}
|
||||
<Button text on:click={clearAll}>Clear all</Button>
|
||||
<Button size="s" text on:click={clearAll}>Clear all</Button>
|
||||
{/if}
|
||||
<Button on:click={apply} disabled={isButtonDisabled}>Apply</Button>
|
||||
<Button size="s" on:click={apply} disabled={isButtonDisabled}>Apply</Button>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { base } from '$app/paths';
|
||||
import { Card } from '@appwrite.io/pink-svelte';
|
||||
import { type ComponentProps } from 'svelte';
|
||||
import type Selector from '@appwrite.io/pink-svelte/dist/card/Selector.svelte';
|
||||
|
||||
type Props = ComponentProps<Selector>;
|
||||
|
||||
export let name: string;
|
||||
export let group: string;
|
||||
export let value: string | number | boolean;
|
||||
export let disabled = false;
|
||||
export let padding = 1;
|
||||
export let icon: string = null;
|
||||
export let imageIcon: string = null;
|
||||
export let fullHeight = true;
|
||||
@@ -16,21 +18,45 @@
|
||||
export let backgroundColorHover: string = null;
|
||||
export let tooltipText: string = null;
|
||||
export let tooltipShow = false;
|
||||
export let src: string = null;
|
||||
export let alt: string = null;
|
||||
|
||||
enum Radius {
|
||||
xsmall = '--border-radius-xsmall',
|
||||
small = '--border-radius-small',
|
||||
medium = '--border-radius-medium',
|
||||
large = '--border-radius-large'
|
||||
}
|
||||
// Pink v2
|
||||
export let radius: Props['radius'] = 's';
|
||||
export let imageRadius: Props['imageRadius'] = 'xxs';
|
||||
export let padding: Props['padding'] = 's';
|
||||
export let variant: Props['variant'] = 'primary';
|
||||
//temporarily unefined
|
||||
export let title: Props['title'] = undefined;
|
||||
|
||||
// TODO: remove after label card migration
|
||||
let slotTitle: HTMLSpanElement;
|
||||
</script>
|
||||
|
||||
<label
|
||||
<div use:tooltip={{ content: tooltipText, disabled: !tooltipText || !tooltipShow }}>
|
||||
<Card.Selector
|
||||
{padding}
|
||||
{src}
|
||||
{imageRadius}
|
||||
{alt}
|
||||
{variant}
|
||||
{value}
|
||||
{disabled}
|
||||
title={title ?? slotTitle?.innerText}
|
||||
bind:group>
|
||||
<p>
|
||||
<slot />
|
||||
</p>
|
||||
</Card.Selector>
|
||||
</div>
|
||||
<span style="display: none" bind:this={slotTitle}>
|
||||
<slot name="title" />
|
||||
</span>
|
||||
<!-- <label
|
||||
class="card u-cursor-pointer"
|
||||
class:is-allow-focus={!disabled}
|
||||
class:is-disabled={disabled}
|
||||
class:u-height-100-percent={fullHeight}
|
||||
style:--card-padding={`${padding}rem`}
|
||||
style:--card-border-radius={`var(${Radius[borderRadius]})`}
|
||||
style:--p-card-bg-color-default={backgroundColor}
|
||||
style:--p-card-bg-color-hover={backgroundColorHover}
|
||||
@@ -70,4 +96,4 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</label>
|
||||
</label> -->
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Alert, ModalWrapper } from '$lib/components';
|
||||
import { Alert } from '$lib/components';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { Form } from '$lib/elements/forms';
|
||||
import { disableCommands } from '$lib/commandCenter';
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import { Layout, Modal } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let show = false;
|
||||
export let size: 'small' | 'big' | 'huge' = null;
|
||||
@@ -12,6 +13,7 @@
|
||||
export let error: string = null;
|
||||
export let closable = true;
|
||||
export let headerDivider = true;
|
||||
export let open = false;
|
||||
export let onSubmit: (e: SubmitEvent) => Promise<void> | void = function () {
|
||||
return;
|
||||
};
|
||||
@@ -31,76 +33,25 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModalWrapper bind:show {size} {headerDivider} let:close>
|
||||
<Modal {title} {description} bind:open={show}>
|
||||
<Form isModal {onSubmit}>
|
||||
<header class="modal-header">
|
||||
<div class="u-flex u-main-space-between u-cross-center u-gap-16">
|
||||
<div class="u-flex u-cross-center u-gap-16">
|
||||
{#if icon}
|
||||
<div
|
||||
class="avatar is-medium"
|
||||
class:is-success={state === 'success'}
|
||||
class:is-warning={state === 'warning'}
|
||||
class:is-danger={state === 'error'}
|
||||
class:is-info={state === 'info'}>
|
||||
<span class={`icon-${icon}`} aria-hidden="true" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<h4 class="modal-title heading-level-5">
|
||||
<slot name="title">
|
||||
{title}
|
||||
</slot>
|
||||
</h4>
|
||||
</div>
|
||||
{#if closable}
|
||||
<button
|
||||
type="button"
|
||||
class="button is-text is-only-icon"
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="Close Modal"
|
||||
title="Close Modal"
|
||||
on:mousedown={() =>
|
||||
trackEvent('click_close_modal', {
|
||||
from: 'button'
|
||||
})}
|
||||
on:mousedown={close}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if description.length > 0}
|
||||
<p class="modal-description u-margin-block-start-4">
|
||||
<slot name="description">
|
||||
{description}
|
||||
</slot>
|
||||
</p>
|
||||
{/if}
|
||||
</header>
|
||||
<div class="modal-content">
|
||||
<div class="modal-content-spacer u-flex-vertical u-gap-24 u-width-full-line">
|
||||
{#if error}
|
||||
<div bind:this={alert}>
|
||||
<Alert
|
||||
dismissible
|
||||
type="warning"
|
||||
on:dismiss={() => {
|
||||
error = null;
|
||||
}}>
|
||||
{error}
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $$slots.footer}
|
||||
<div class="modal-footer">
|
||||
<div class="u-flex u-main-end u-gap-16">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
{#if error}
|
||||
<div bind:this={alert}>
|
||||
<Alert
|
||||
dismissible
|
||||
type="warning"
|
||||
on:dismiss={() => {
|
||||
error = null;
|
||||
}}>
|
||||
{error}
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</Form>
|
||||
</ModalWrapper>
|
||||
<svelte:fragment slot="footer">
|
||||
<Layout.Stack direction="row" justifyContent="flex-end">
|
||||
<slot name="footer" />
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
@@ -232,7 +232,7 @@
|
||||
bind:showUser
|
||||
{groups}
|
||||
on:create={create}>
|
||||
<Button secondary round on:click={() => (showDropdown = !showDropdown)}>
|
||||
<Button secondary icon on:click={() => (showDropdown = !showDropdown)}>
|
||||
<i class="icon-plus" />
|
||||
</Button>
|
||||
</Actions>
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
bind:showUser
|
||||
{groups}
|
||||
on:create={create}>
|
||||
<Button secondary round on:click={() => (showDropdown = !showDropdown)}>
|
||||
<Button secondary icon on:click={() => (showDropdown = !showDropdown)}>
|
||||
<i class="icon-plus" />
|
||||
</Button>
|
||||
</Actions>
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { EmptySearch } from '$lib/components';
|
||||
import { Button, InputSearch, InputSelect } from '$lib/elements/forms';
|
||||
import { timeFromNow } from '$lib/helpers/date';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { repositories } from '$routes/(console)/project-[project]/functions/function-[function]/store';
|
||||
import { installation, installations, repository } from '$lib/stores/vcs';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Layout, Table, Typography, Icon, Avatar } from '@appwrite.io/pink-svelte';
|
||||
import { IconLockClosed } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let callbackState: Record<string, string> = null;
|
||||
export let selectedRepository: string = null;
|
||||
export let hasInstallations = false;
|
||||
export let action: 'button' | 'select' = 'select';
|
||||
|
||||
$: {
|
||||
hasInstallations = $installations?.total > 0;
|
||||
}
|
||||
|
||||
let selectedInstallation = null;
|
||||
async function loadInstallations() {
|
||||
const { installations } = await sdk.forProject.vcs.listInstallations();
|
||||
if (installations.length) {
|
||||
selectedInstallation = installations[0].$id;
|
||||
installation.set(installations.find((entry) => entry.$id === selectedInstallation));
|
||||
}
|
||||
return installations;
|
||||
}
|
||||
|
||||
let search = '';
|
||||
async function loadRepositories(installationId: string, search: string) {
|
||||
if (
|
||||
!$repositories ||
|
||||
$repositories.installationId !== installationId ||
|
||||
$repositories.search !== search
|
||||
) {
|
||||
$repositories.repositories = (
|
||||
await sdk.forProject.vcs.listRepositories(installationId, search || undefined)
|
||||
).providerRepositories;
|
||||
}
|
||||
|
||||
$repositories.search = search;
|
||||
$repositories.installationId = installationId;
|
||||
|
||||
if ($repositories.repositories.length) {
|
||||
selectedRepository = $repositories.repositories[0].id;
|
||||
$repository = $repositories.repositories[0];
|
||||
}
|
||||
|
||||
return $repositories.repositories.slice(0, 4);
|
||||
}
|
||||
|
||||
function connectGitHub() {
|
||||
const redirect = new URL($page.url);
|
||||
if (callbackState) {
|
||||
Object.keys(callbackState).forEach((key) => {
|
||||
redirect.searchParams.append(key, callbackState[key]);
|
||||
});
|
||||
}
|
||||
const target = new URL(`${sdk.forProject.client.config.endpoint}/vcs/github/authorize`);
|
||||
target.searchParams.set('project', $page.params.project);
|
||||
target.searchParams.set('success', redirect.toString());
|
||||
target.searchParams.set('failure', redirect.toString());
|
||||
target.searchParams.set('mode', 'admin');
|
||||
return target;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if hasInstallations}
|
||||
<Layout.Stack>
|
||||
<Layout.Stack gap="s">
|
||||
{#await loadInstallations()}
|
||||
<Layout.Stack direction="row">
|
||||
<InputSelect
|
||||
disabled
|
||||
id="installation"
|
||||
options={[
|
||||
{
|
||||
label: 'Loading...',
|
||||
value: null
|
||||
}
|
||||
]}
|
||||
value={null} />
|
||||
<InputSearch placeholder="Search repositories" disabled />
|
||||
</Layout.Stack>
|
||||
{:then installations}
|
||||
<Layout.Stack direction="row">
|
||||
<InputSelect
|
||||
id="installation"
|
||||
options={installations.map((entry) => {
|
||||
return {
|
||||
label: entry.organization,
|
||||
value: entry.$id
|
||||
};
|
||||
})}
|
||||
on:change={() => {
|
||||
search = '';
|
||||
installation.set(
|
||||
installations.find((entry) => entry.$id === selectedInstallation)
|
||||
);
|
||||
}}
|
||||
bind:value={selectedInstallation} />
|
||||
<InputSearch placeholder="Search repositories" bind:value={search} />
|
||||
</Layout.Stack>
|
||||
{/await}
|
||||
<p>
|
||||
Manage organization configuration in your <a
|
||||
class="link"
|
||||
href={`${base}/project-${$page.params.project}/settings`}>project settings</a
|
||||
>.
|
||||
</p>
|
||||
</Layout.Stack>
|
||||
{#if selectedInstallation}
|
||||
{#await loadRepositories(selectedInstallation, search)}
|
||||
<Layout.Stack justifyContent="center" alignContent="center" alignItems="center">
|
||||
<div class="loader u-margin-32" />
|
||||
</Layout.Stack>
|
||||
{:then response}
|
||||
{#if response?.length}
|
||||
<Table.Root>
|
||||
{#each response as repo}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
{#if action === 'select'}
|
||||
<input
|
||||
class="is-small u-margin-inline-end-8"
|
||||
type="radio"
|
||||
name="repositories"
|
||||
bind:group={selectedRepository}
|
||||
on:change={() => repository.set(repo)}
|
||||
value={repo.id} />
|
||||
{/if}
|
||||
|
||||
<Avatar
|
||||
size="xs"
|
||||
src={repo?.runtime
|
||||
? `${base}/icons/${$app.themeInUse}/color/${
|
||||
repo.runtime.split('-')[0]
|
||||
}.svg`
|
||||
: ''}
|
||||
alt={repo.name} />
|
||||
|
||||
<Layout.Stack gap="s" direction="row" alignItems="center">
|
||||
<Typography.Text truncate>
|
||||
{repo.name}
|
||||
</Typography.Text>
|
||||
{#if repo.private}
|
||||
<Icon
|
||||
size="s"
|
||||
icon={IconLockClosed}
|
||||
color="--color-fgcolor-neutral-tertiary" />
|
||||
{/if}
|
||||
<time datetime={repo.pushedAt}>
|
||||
<Typography.Text
|
||||
truncate
|
||||
color="--color-fgcolor-neutral-tertiary">
|
||||
{timeFromNow(repo.pushedAt)}
|
||||
</Typography.Text>
|
||||
</time>
|
||||
</Layout.Stack>
|
||||
{#if action === 'button'}
|
||||
<div class="u-margin-inline-start-auto">
|
||||
<Button
|
||||
size="s"
|
||||
secondary
|
||||
on:click={() => dispatch('connect', repo)}>
|
||||
Connect
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{:else if search}
|
||||
<EmptySearch hidePages>
|
||||
<div class="common-section">
|
||||
<div class="u-text-center common-section">
|
||||
<b class="body-text-2 u-bold">Sorry we couldn't find "{search}"</b>
|
||||
<p>There are no repositories that match your search.</p>
|
||||
</div>
|
||||
<div class="u-flex u-gap-16 common-section u-main-center">
|
||||
<Button secondary on:click={() => (search = '')}>
|
||||
Clear search
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</EmptySearch>
|
||||
{:else}
|
||||
<EmptySearch hidePages />
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{:else}
|
||||
<div class="u-flex u-cross-center u-flex-vertical u-gap-16">
|
||||
<Button href={connectGitHub().toString()} fullWidth secondary>
|
||||
<span class="icon-github" aria-hidden="true" />
|
||||
<span class="text">GitHub</span>
|
||||
</Button>
|
||||
<Button disabled fullWidth secondary>
|
||||
<span class="icon-gitlab" aria-hidden="true" />
|
||||
<span class="text">GitLab (coming soon)</span>
|
||||
</Button>
|
||||
<Button disabled fullWidth secondary>
|
||||
<span class="icon-bitBucket" aria-hidden="true" />
|
||||
<span class="text">BitBucket (coming soon)</span>
|
||||
</Button>
|
||||
<Button disabled fullWidth secondary>
|
||||
<span class="icon-azure" aria-hidden="true" />
|
||||
<span class="text">Azure (coming soon)</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -111,7 +111,7 @@
|
||||
<li>
|
||||
<Button
|
||||
href="https://appwrite.io/discord"
|
||||
round
|
||||
icon
|
||||
text
|
||||
noMargin
|
||||
external
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { last } from '$lib/helpers/array';
|
||||
import { getElementDir } from '$lib/helpers/style';
|
||||
import { waitUntil } from '$lib/helpers/waitUntil';
|
||||
import { Tabs } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let selected = false;
|
||||
export let href: string = null;
|
||||
@@ -79,27 +80,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<li class="tabs-item" role="tab">
|
||||
{#if href}
|
||||
<a
|
||||
class="tabs-button"
|
||||
{href}
|
||||
class:is-selected={selected}
|
||||
on:click={handleClick}
|
||||
tabindex={selected ? 0 : -1}
|
||||
on:keydown={handleKeyDown}>
|
||||
<span class="text"><slot /></span>
|
||||
</a>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="tabs-button"
|
||||
class:is-selected={selected}
|
||||
on:click|preventDefault
|
||||
on:click={handleClick}
|
||||
tabindex={selected ? 0 : -1}
|
||||
on:keydown={handleKeyDown}>
|
||||
<span class="text"><slot /></span>
|
||||
</button>
|
||||
{/if}
|
||||
</li>
|
||||
{#if href}
|
||||
<Tabs.Item.Link {href} bind:active={selected} on:click={handleClick} on:keydown={handleKeyDown}>
|
||||
<slot />
|
||||
</Tabs.Item.Link>
|
||||
{:else}
|
||||
<Tabs.Item.Button bind:active={selected} on:click={handleClick} on:keydown={handleKeyDown}>
|
||||
<slot />
|
||||
</Tabs.Item.Button>
|
||||
{/if}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { throttle } from '$lib/helpers/functions';
|
||||
import { Tabs } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let showLeft = false;
|
||||
let showRight = false;
|
||||
let tabsList: HTMLUListElement;
|
||||
let tabsList: HTMLDivElement;
|
||||
|
||||
function slide(direction: 'left' | 'right') {
|
||||
let scrollCompleted = 0;
|
||||
@@ -32,7 +33,7 @@
|
||||
|
||||
<svelte:window on:resize={throttle(onScroll, 25)} />
|
||||
|
||||
<div class="tabs">
|
||||
<div style="margin-block-start: auto; ">
|
||||
{#if showLeft}
|
||||
<button
|
||||
class="tabs-button-scroll is-start"
|
||||
@@ -49,11 +50,9 @@
|
||||
<span class="icon-cheveron-right" aria-hidden="true" />
|
||||
</button>
|
||||
{/if}
|
||||
<ul
|
||||
class="tabs-list scroll-shadow-horizontal"
|
||||
role="tablist"
|
||||
bind:this={tabsList}
|
||||
on:scroll={throttle(onScroll, 25)}>
|
||||
<slot />
|
||||
</ul>
|
||||
<div class=" scroll-shadow-horizontal" bind:this={tabsList} on:scroll={throttle(onScroll, 25)}>
|
||||
<Tabs.Root>
|
||||
<slot />
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
{#if $columns?.length}
|
||||
<DropList bind:show={showSelectColumns} scrollable wrapperFullWidth={fullWidthMobile}>
|
||||
<Button
|
||||
size="s"
|
||||
secondary
|
||||
on:click={() => (showSelectColumns = !showSelectColumns)}
|
||||
{fullWidthMobile}>
|
||||
|
||||
+18
-2
@@ -59,7 +59,9 @@ export enum Dependencies {
|
||||
MESSAGING_MESSAGE = 'dependency:messaging_message',
|
||||
MESSAGING_TOPICS = 'dependency:messaging_topics',
|
||||
MESSAGING_TOPIC = 'dependency:messaging_topic',
|
||||
MESSAGING_TOPIC_SUBSCRIBERS = 'dependency:messaging_topic_subscribers'
|
||||
MESSAGING_TOPIC_SUBSCRIBERS = 'dependency:messaging_topic_subscribers',
|
||||
SITE = 'dependency:site',
|
||||
SITES = 'dependency:sites'
|
||||
}
|
||||
|
||||
export const defaultScopes: string[] = [
|
||||
@@ -116,7 +118,9 @@ export const defaultScopes: string[] = [
|
||||
'topics.read',
|
||||
'topics.write',
|
||||
'billing.read',
|
||||
'billing.write'
|
||||
'billing.write',
|
||||
'sites:read',
|
||||
'sites:write'
|
||||
];
|
||||
|
||||
export const defaultRoles: string[] = ['owner'];
|
||||
@@ -358,6 +362,18 @@ export const scopes: {
|
||||
description: 'Access to create migrations',
|
||||
category: 'Other',
|
||||
icon: 'globe'
|
||||
},
|
||||
{
|
||||
scope: 'sites:read',
|
||||
description: "Access to read your project's sites and deployments",
|
||||
category: 'Sites',
|
||||
icon: 'globe'
|
||||
},
|
||||
{
|
||||
scope: 'sites:write',
|
||||
description: "Access to create, update, and delete your project's sites and deployments",
|
||||
category: 'Sites',
|
||||
icon: 'globe'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
export let secondary = false;
|
||||
export let text = false;
|
||||
export let danger = false;
|
||||
export let round = false;
|
||||
export let icon = false;
|
||||
export let link = false;
|
||||
export let size: Props['size'] = 'medium';
|
||||
export let size: Props['size'] = 'm';
|
||||
export let disabled = false;
|
||||
export let external = false;
|
||||
export let href: string = null;
|
||||
@@ -23,6 +23,7 @@
|
||||
export let fullWidthMobile = false;
|
||||
export let ariaLabel: string = null;
|
||||
export let event: string = null;
|
||||
export let eventData: Record<string, unknown> = {};
|
||||
let classes: string = '';
|
||||
export { classes as class };
|
||||
export let submissionLoader = false;
|
||||
@@ -39,7 +40,8 @@
|
||||
}
|
||||
|
||||
trackEvent(`click_${event}`, {
|
||||
from: 'button'
|
||||
from: 'button',
|
||||
...eventData
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,6 +62,7 @@
|
||||
{href}
|
||||
{download}
|
||||
{size}
|
||||
{icon}
|
||||
disabled={internalDisabled}
|
||||
variant={secondary ? 'secondary' : text ? 'text' : 'primary'}
|
||||
target={external ? '_blank' : ''}
|
||||
@@ -75,6 +78,7 @@
|
||||
on:mousedown
|
||||
on:click={track}
|
||||
{size}
|
||||
{icon}
|
||||
disabled={internalDisabled}
|
||||
variant={secondary ? 'secondary' : text ? 'text' : 'primary'}
|
||||
class={resolvedClasses}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { tooltip as tooltipAction } from '$lib/actions/tooltip';
|
||||
import { Input, Selector } from '@appwrite.io/pink-svelte';
|
||||
import { Helper } from '.';
|
||||
|
||||
export let type: 'checkbox' | 'switchbox' = 'checkbox';
|
||||
@@ -30,17 +31,28 @@
|
||||
</script>
|
||||
|
||||
<label class="choice-item" for={id}>
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{required}
|
||||
type="checkbox"
|
||||
class:switch={type === 'switchbox'}
|
||||
aria-checked={value}
|
||||
bind:this={element}
|
||||
bind:checked={value}
|
||||
on:change
|
||||
on:invalid={handleInvalid} />
|
||||
{#if type === 'switchbox'}
|
||||
<input
|
||||
{id}
|
||||
{disabled}
|
||||
{required}
|
||||
type="checkbox"
|
||||
class:switch={type === 'switchbox'}
|
||||
aria-checked={value}
|
||||
bind:this={element}
|
||||
bind:checked={value}
|
||||
on:change
|
||||
on:invalid={handleInvalid} />
|
||||
{:else}
|
||||
<Selector.Checkbox
|
||||
{id}
|
||||
{disabled}
|
||||
size="s"
|
||||
{required}
|
||||
bind:value
|
||||
bind:checked={value}
|
||||
on:change />
|
||||
{/if}
|
||||
|
||||
<div class="choice-item-content" class:u-width-full-line={fullWidth}>
|
||||
{#if (label && showLabel) || tooltip}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { FormItem, Helper, Label } from '.';
|
||||
import { Selector } from '@appwrite.io/pink-svelte';
|
||||
import type Radio from '@appwrite.io/pink-svelte/dist/selector/Radio.svelte';
|
||||
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
type Props = ComponentProps<Radio>;
|
||||
|
||||
export let label: string = null;
|
||||
export let showLabel = true;
|
||||
@@ -9,6 +14,7 @@
|
||||
export let name: string;
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let size: Props['size'] = 'medium';
|
||||
|
||||
let element: HTMLInputElement;
|
||||
let error: string;
|
||||
@@ -28,45 +34,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormItem>
|
||||
<div class="input-text-wrapper">
|
||||
<input
|
||||
{id}
|
||||
{name}
|
||||
{disabled}
|
||||
{required}
|
||||
{value}
|
||||
type="radio"
|
||||
bind:group
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
<Label {required} hide={!showLabel} for={id}>
|
||||
{#if label}
|
||||
{label}
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
</Label>
|
||||
<!-- <label class="choice-item" for={id}>
|
||||
<input
|
||||
{id}
|
||||
{name}
|
||||
{disabled}
|
||||
{required}
|
||||
{value}
|
||||
type="radio"
|
||||
bind:group
|
||||
bind:this={element}
|
||||
on:invalid={handleInvalid} />
|
||||
<div
|
||||
class="choice-item-content u-cross-child-center"
|
||||
class:u-width-full-line={fullWidth}>
|
||||
<div class="choice-item-title">{label}</div>
|
||||
<slot name="description" />
|
||||
</div>
|
||||
</label> -->
|
||||
</div>
|
||||
{#if error}
|
||||
<Helper type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</FormItem>
|
||||
<Selector.Radio
|
||||
{id}
|
||||
{name}
|
||||
{disabled}
|
||||
{required}
|
||||
{value}
|
||||
{label}
|
||||
{size}
|
||||
bind:group
|
||||
on:invalid={handleInvalid} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { IconX } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Input } from '@appwrite.io/pink-svelte';
|
||||
import { IconX, IconSearch } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Input, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
export let autofocus = false;
|
||||
export let isWithEndButton = true;
|
||||
export let style: string = '';
|
||||
export let label: string = '';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let element: HTMLInputElement;
|
||||
@@ -49,9 +50,13 @@
|
||||
{disabled}
|
||||
{required}
|
||||
{autofocus}
|
||||
{label}
|
||||
type="search"
|
||||
bind:value
|
||||
on:input={valueChange}>
|
||||
<svelte:fragment slot="start">
|
||||
<Icon icon={IconSearch} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="end">
|
||||
{#if value}
|
||||
<Input.Action icon={IconX} on:click={clearSearch} />
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
$: if (value || search) error = null;
|
||||
</script>
|
||||
|
||||
<li
|
||||
<div
|
||||
class="u-position-relative form-item"
|
||||
class:u-width-full-line={fullWidth}
|
||||
class:u-stretch={stretch}>
|
||||
@@ -235,7 +235,7 @@
|
||||
{#if error}
|
||||
<Helper class="u-position-relative" type="warning">{error}</Helper>
|
||||
{/if}
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.form-item :global(.drop) {
|
||||
|
||||
@@ -50,10 +50,15 @@
|
||||
{maxlength}
|
||||
{label}
|
||||
{nullable}
|
||||
{readonly}
|
||||
autofocus={autofocus || undefined}
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
helper={error}
|
||||
state={error ? 'error' : 'default'}
|
||||
on:invalid={handleInvalid}
|
||||
on:input
|
||||
bind:value />
|
||||
bind:value>
|
||||
<svelte:fragment slot="end">
|
||||
<slot name="end" />
|
||||
</svelte:fragment>
|
||||
</Input.Text>
|
||||
|
||||
@@ -3,3 +3,4 @@ export { default as SelectSearchItem } from './selectSearchItem.svelte';
|
||||
export { default as Flag } from './flag.svelte';
|
||||
export { default as SelectSearchCheckbox } from './selectSearchCheckbox.svelte';
|
||||
export { default as SelectSearchRadio } from './selectSearchRadio.svelte';
|
||||
export { default as Link } from './link.svelte';
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { Link } from '@appwrite.io/pink-svelte';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
type Props = ComponentProps<Link.Anchor> & ComponentProps<Link.Button>;
|
||||
|
||||
export let event: string = null;
|
||||
export let eventData: Record<string, unknown> = {};
|
||||
export let href: string = null;
|
||||
export let disabled: Props['disabled'] = false;
|
||||
export let type: Props['type'] = 'button';
|
||||
export let variant: Props['variant'] = 'default';
|
||||
export let size: Props['size'] = 'medium';
|
||||
|
||||
function track() {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
trackEvent(`click_${event}`, {
|
||||
from: 'link',
|
||||
...eventData
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<Link.Anchor on:click on:mousedown on:click={track} {href} {disabled} {variant} {size}>
|
||||
<slot />
|
||||
</Link.Anchor>
|
||||
{:else}
|
||||
<Link.Button on:click on:mousedown on:click={track} {type} {disabled} {variant} {size}>
|
||||
<slot />
|
||||
</Link.Button>
|
||||
{/if}
|
||||
@@ -28,6 +28,7 @@
|
||||
disabled: !disabled
|
||||
}}>
|
||||
<Button
|
||||
size="s"
|
||||
text={buttonType === 'text'}
|
||||
secondary={buttonType === 'secondary'}
|
||||
on:click={buttonMethod}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Alert, DropList, Heading } from '$lib/components';
|
||||
import { BillingPlan } from '$lib/constants';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Link, Pill } from '$lib/elements';
|
||||
import {
|
||||
checkForProjectLimitation,
|
||||
checkForUsageFees,
|
||||
@@ -17,7 +16,6 @@
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { ContainerButton } from '.';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
|
||||
@@ -98,22 +96,19 @@
|
||||
<Alert type="info" isStandalone>
|
||||
<span class="text">
|
||||
You've reached the {services} limit for the {tier} plan.
|
||||
<Button link on:click={() => ($showUsageRatesModal = true)}
|
||||
>Excess usage fees will apply</Button
|
||||
<Link on:mousedown={() => ($showUsageRatesModal = true)}
|
||||
>Excess usage fees will apply</Link
|
||||
>.
|
||||
</span>
|
||||
</Alert>
|
||||
{:else}
|
||||
<Alert type={alertType} isStandalone>
|
||||
<span class="text">
|
||||
You've reached the {services} limit for the {tier} plan. <Button
|
||||
link
|
||||
You've reached the {services} limit for the {tier} plan. <Link
|
||||
href={$upgradeURL}
|
||||
on:click={() =>
|
||||
trackEvent('click_organization_upgrade', {
|
||||
from: 'button',
|
||||
source: 'inline_alert'
|
||||
})}>Upgrade</Button> your organization for additional resources.
|
||||
event="organization_upgrade"
|
||||
eventData={{ from: 'event', source: 'inline_alert' }}>Upgrade</Link> your
|
||||
organization for additional resources.
|
||||
</span>
|
||||
</Alert>
|
||||
{/if}
|
||||
@@ -121,68 +116,67 @@
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<Layout.Stack direction="row">
|
||||
<Heading tag={titleTag} size={titleSize}>{title}</Heading>
|
||||
{#if isCloud && isLimited}
|
||||
<DropList bind:show={showDropdown} width="16">
|
||||
{#if hasProjectLimitation}
|
||||
<Pill button on:click={() => (showDropdown = !showDropdown)}>
|
||||
<span class="icon-info" />{total}/{limit} created
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill button on:click={() => (showDropdown = !showDropdown)}>
|
||||
<span class="icon-info" />Limits applied
|
||||
</Pill>
|
||||
{/if}
|
||||
<svelte:fragment slot="list">
|
||||
<slot name="tooltip" {limit} {tier} {title} {upgradeMethod} {hasUsageFees}>
|
||||
{#if hasProjectLimitation}
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per project on the {tier} plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}<Button
|
||||
link
|
||||
href={$upgradeURL}
|
||||
on:click={() =>
|
||||
trackEvent('click_organization_upgrade', {
|
||||
from: 'button',
|
||||
source: 'resource_limit_tag'
|
||||
})}>Upgrade</Button>
|
||||
for additional {title.toLocaleLowerCase()}.
|
||||
{/if}
|
||||
</p>
|
||||
{:else if hasUsageFees}
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per organization on the {tier} plan.
|
||||
<Button link on:click={() => ($showUsageRatesModal = true)}
|
||||
>Excess usage fees will apply</Button
|
||||
>.
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per organization on the {tier} plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<Button link href={$upgradeURL}>Upgrade</Button>
|
||||
for additional {title.toLocaleLowerCase()}.
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
</slot>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" alignContent="center">
|
||||
<Layout.Stack direction="row">
|
||||
<Heading tag={titleTag} size={titleSize}>{title}</Heading>
|
||||
{#if isCloud && isLimited}
|
||||
<DropList bind:show={showDropdown} width="16">
|
||||
{#if hasProjectLimitation}
|
||||
<Pill button on:click={() => (showDropdown = !showDropdown)}>
|
||||
<span class="icon-info" />{total}/{limit} created
|
||||
</Pill>
|
||||
{:else}
|
||||
<Pill button on:click={() => (showDropdown = !showDropdown)}>
|
||||
<span class="icon-info" />Limits applied
|
||||
</Pill>
|
||||
{/if}
|
||||
<svelte:fragment slot="list">
|
||||
<slot name="tooltip" {limit} {tier} {title} {upgradeMethod} {hasUsageFees}>
|
||||
{#if hasProjectLimitation}
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per project on the {tier} plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}<Link
|
||||
href={$upgradeURL}
|
||||
event="organization_upgrade"
|
||||
eventData={{ from: 'button', source: 'resource_limit_tag' }}
|
||||
>Upgrade</Link>
|
||||
for additional {title.toLocaleLowerCase()}.
|
||||
{/if}
|
||||
</p>
|
||||
{:else if hasUsageFees}
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per organization on the {tier} plan.
|
||||
<Link on:mousedown={() => ($showUsageRatesModal = true)}
|
||||
>Excess usage fees will apply</Link
|
||||
>.
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text">
|
||||
You are limited to {limit}
|
||||
{title.toLocaleLowerCase()} per organization on the {tier} plan.
|
||||
{#if $organization?.billingPlan === BillingPlan.FREE}
|
||||
<Link href={$upgradeURL}>Upgrade</Link>
|
||||
for additional {title.toLocaleLowerCase()}.
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
</slot>
|
||||
</svelte:fragment>
|
||||
</DropList>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
<slot {isButtonDisabled}>
|
||||
{#if buttonText}
|
||||
<ContainerButton
|
||||
{title}
|
||||
disabled={isButtonDisabled}
|
||||
{buttonText}
|
||||
{buttonEvent}
|
||||
{buttonMethod}
|
||||
{buttonHref} />
|
||||
{/if}
|
||||
</slot>
|
||||
<slot {isButtonDisabled}>
|
||||
{#if buttonText}
|
||||
<ContainerButton
|
||||
{title}
|
||||
disabled={isButtonDisabled}
|
||||
{buttonText}
|
||||
{buttonEvent}
|
||||
{buttonMethod}
|
||||
{buttonHref} />
|
||||
{/if}
|
||||
</slot>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -8,7 +8,7 @@ export { default as Notification } from './notification.svelte';
|
||||
export { default as Notifications } from './notifications.svelte';
|
||||
export { default as Shell } from './shell.svelte';
|
||||
export { default as Logs } from './logs.svelte';
|
||||
export { default as Wizard } from './wizard.svelte';
|
||||
export { default as WizardWithSteps } from './wizardWithSteps.svelte';
|
||||
export { default as WizardStep } from './wizardStep.svelte';
|
||||
export { default as Breadcrumbs } from './breadcrumbs.svelte';
|
||||
export { default as Unauthenticated } from './unauthenticated.svelte';
|
||||
@@ -22,3 +22,4 @@ export { default as ContainerButton } from './containerButton.svelte';
|
||||
export { default as WizardSecondaryContainer } from './wizardSecondaryContainer.svelte';
|
||||
export { default as WizardSecondaryContent } from './wizardSecondaryContent.svelte';
|
||||
export { default as WizardSecondaryFooter } from './wizardSecondaryFooter.svelte';
|
||||
export { default as Wizard } from './wizard.svelte';
|
||||
|
||||
@@ -149,6 +149,23 @@
|
||||
<span class="text">Messaging</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="drop-list-item">
|
||||
<a
|
||||
class="drop-button"
|
||||
class:is-selected={$page.url.pathname.startsWith(
|
||||
`${projectPath}/sites`
|
||||
)}
|
||||
on:click={() => trackEvent('click_menu_sites')}
|
||||
href={`${projectPath}/sites`}
|
||||
use:tooltip={{
|
||||
content: 'Sites',
|
||||
placement: 'right',
|
||||
disabled: !narrow
|
||||
}}>
|
||||
<span class="icon-globe-alt" aria-hidden="true" />
|
||||
<span class="text">Sites</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="drop-list-item">
|
||||
<a
|
||||
class="drop-button"
|
||||
|
||||
+80
-215
@@ -1,242 +1,107 @@
|
||||
<script context="module" lang="ts">
|
||||
export type WizardStepsType = Map<
|
||||
number,
|
||||
{
|
||||
label: string;
|
||||
component: typeof SvelteComponent<unknown>;
|
||||
optional?: boolean;
|
||||
disabled?: boolean;
|
||||
actions?: {
|
||||
label: string;
|
||||
onClick: () => Promise<void>;
|
||||
}[];
|
||||
}
|
||||
>;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
import { Steps } from '$lib/components';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { createEventDispatcher, type SvelteComponent } from 'svelte';
|
||||
import WizardExitModal from './wizardExitModal.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let title: string;
|
||||
export let steps: WizardStepsType;
|
||||
export let confirmExit = false;
|
||||
export let finalAction = 'Create';
|
||||
export let finalMethod: () => Promise<void> = null;
|
||||
type $$Props =
|
||||
| {
|
||||
title: string;
|
||||
confirmExit: boolean;
|
||||
showExitModal: boolean;
|
||||
href?: string;
|
||||
invertColumns?: boolean;
|
||||
hideAside?: boolean;
|
||||
hideFooter?: boolean;
|
||||
}
|
||||
| {
|
||||
title: string;
|
||||
href: string;
|
||||
confirmExit?: boolean;
|
||||
showExitModal?: boolean;
|
||||
invertColumns?: boolean;
|
||||
hideAside?: boolean;
|
||||
hideFooter?: boolean;
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let showExitModal = false;
|
||||
export let title: $$Props['title'] = '';
|
||||
export let confirmExit: $$Props['confirmExit'] = false;
|
||||
export let href: $$Props['href'] = '';
|
||||
export let showExitModal: $$Props['showExitModal'] = false;
|
||||
export let invertColumns: $$Props['invertColumns'] = false;
|
||||
export let hideAside = false;
|
||||
export let hideFooter = false;
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
const openDialog = document.querySelectorAll('dialog[open]');
|
||||
if (event.key === 'Escape' && !showExitModal && !openDialog?.length) {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'escape'
|
||||
});
|
||||
dispatch('exit');
|
||||
wizard.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleExit() {
|
||||
if (confirmExit) {
|
||||
showExitModal = true;
|
||||
} else {
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'button'
|
||||
});
|
||||
dispatch('exit');
|
||||
wizard.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleStepClick(e: CustomEvent<number>) {
|
||||
const step = e.detail;
|
||||
if (step < $wizard.step) {
|
||||
$wizard.step = step;
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if ($wizard.interceptor) {
|
||||
$wizard.nextDisabled = true;
|
||||
try {
|
||||
await $wizard.interceptor();
|
||||
} catch (error) {
|
||||
if (!$wizard.interceptorNotificationEnabled) return;
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
if (confirmExit) {
|
||||
showExitModal = true;
|
||||
} else {
|
||||
goto(href);
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'escape'
|
||||
});
|
||||
return;
|
||||
} finally {
|
||||
$wizard.nextDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
wizard.setInterceptor(null);
|
||||
if (isLastStep) {
|
||||
if ($wizard.finalAction) {
|
||||
$wizard.nextDisabled = true;
|
||||
try {
|
||||
await $wizard.finalAction();
|
||||
trackEvent('wizard_finish');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
$wizard.nextDisabled = false;
|
||||
}
|
||||
} else {
|
||||
$wizard.nextDisabled = true;
|
||||
if (finalMethod) {
|
||||
await finalMethod();
|
||||
trackEvent('wizard_finish');
|
||||
$wizard.nextDisabled = false;
|
||||
} else {
|
||||
trackEvent('wizard_finish');
|
||||
dispatch('finish');
|
||||
|
||||
setTimeout(() => {
|
||||
$wizard.nextDisabled = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (steps.get($wizard.step + 1)?.disabled) {
|
||||
$wizard.step++;
|
||||
while (steps.get($wizard.step)?.disabled) {
|
||||
$wizard.step++;
|
||||
}
|
||||
} else {
|
||||
$wizard.step++;
|
||||
}
|
||||
trackEvent('wizard_next');
|
||||
}
|
||||
}
|
||||
|
||||
function previousStep() {
|
||||
if (steps.get($wizard.step - 1)?.disabled) {
|
||||
$wizard.step--;
|
||||
while (steps.get($wizard.step)?.disabled) {
|
||||
$wizard.step--;
|
||||
}
|
||||
} else {
|
||||
$wizard.step--;
|
||||
}
|
||||
wizard.setInterceptor(null);
|
||||
trackEvent('wizard_back');
|
||||
}
|
||||
|
||||
$: sortedSteps = [...steps].sort(([a], [b]) => (a > b ? 1 : -1));
|
||||
$: isLastStep = (() => {
|
||||
if ($wizard.step === steps.size) {
|
||||
return true;
|
||||
}
|
||||
let lastStep = true;
|
||||
steps.forEach((step, i) => {
|
||||
if (i > $wizard.step && !step.disabled) {
|
||||
lastStep = false;
|
||||
}
|
||||
});
|
||||
return lastStep;
|
||||
})();
|
||||
$: currentStep = steps.get($wizard.step);
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<section class="wizard">
|
||||
<div class="wizard-header-strip" />
|
||||
<div class="wizard-start-bg" />
|
||||
<div class="wizard-end-bg" />
|
||||
|
||||
<header class="wizard-header">
|
||||
<div class="body-text-1 u-bold">{title}</div>
|
||||
|
||||
<slot name="header" />
|
||||
<button
|
||||
class="button is-text is-only-icon u-margin-inline-start-auto"
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="close wizard"
|
||||
on:click={handleExit}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<aside class="wizard-side">
|
||||
<slot name="aside">
|
||||
<Steps
|
||||
on:step={handleStepClick}
|
||||
steps={sortedSteps.map(([, { label, optional, disabled }]) => ({
|
||||
text: label,
|
||||
optional,
|
||||
disabled
|
||||
}))}
|
||||
currentStep={$wizard.step} />
|
||||
</slot>
|
||||
</aside>
|
||||
<div class="wizard-media">
|
||||
{#if $wizard.media}
|
||||
<img src={$wizard.media} alt="wizard media" loading="lazy" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="wizard-main">
|
||||
<Form noStyle onSubmit={submit}>
|
||||
{#each sortedSteps as [step, { component }]}
|
||||
{#if $wizard.step === step}
|
||||
<svelte:component this={component} />
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="u-z-index-5 form-footer">
|
||||
<div class="u-flex u-main-end u-gap-12">
|
||||
{#if !isLastStep && currentStep?.optional}
|
||||
<Button text on:click={() => dispatch('finish')}>
|
||||
Skip optional steps
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
{#if $wizard.step === 1}
|
||||
<Button secondary on:click={handleExit}>Cancel</Button>
|
||||
{:else}
|
||||
<Button secondary on:click={previousStep}>Back</Button>
|
||||
{/if}
|
||||
|
||||
{#if currentStep?.actions}
|
||||
{#each currentStep.actions as action}
|
||||
<Button secondary on:click={action.onClick}>
|
||||
{action.label}</Button>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<Button submit disabled={$wizard.nextDisabled}>
|
||||
{isLastStep ? finalAction : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</section>
|
||||
<div class="wizard-position">
|
||||
<Layout.Wizard
|
||||
{title}
|
||||
{invertColumns}
|
||||
{hideAside}
|
||||
{hideFooter}
|
||||
href={confirmExit ? null : href}
|
||||
buttonMethod={() => {
|
||||
if (confirmExit) {
|
||||
showExitModal = true;
|
||||
} else {
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'button'
|
||||
});
|
||||
}
|
||||
}}>
|
||||
<slot />
|
||||
<svelte:fragment slot="aside">
|
||||
<slot name="aside" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<slot name="footer" />
|
||||
</svelte:fragment>
|
||||
</Layout.Wizard>
|
||||
</div>
|
||||
|
||||
{#if showExitModal}
|
||||
<WizardExitModal
|
||||
{href}
|
||||
bind:show={showExitModal}
|
||||
on:exit={() => {
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'prompt'
|
||||
});
|
||||
wizard.hide();
|
||||
}}>
|
||||
<slot name="exit">this process</slot>
|
||||
<slot name="exit">
|
||||
Are you sure you want to exit from this process? All data will be deleted. This action
|
||||
is irreversible.
|
||||
</slot>
|
||||
</WizardExitModal>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.wizard-position {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100dvh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
|
||||
export let previousPage: string = null;
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (previousPage) {
|
||||
goto(previousPage);
|
||||
} else {
|
||||
wizard.hide();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +22,7 @@
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<section class="cover-frame is-color-header">
|
||||
<section class="cover-frame is-color-header" data-is-page={previousPage}>
|
||||
<header class="cover-frame-header">
|
||||
<slot name="header">
|
||||
<div
|
||||
@@ -21,7 +32,7 @@
|
||||
<div class="body-text-1 u-bold"><slot name="title" /></div>
|
||||
</div>
|
||||
<button
|
||||
on:click={wizard.hide}
|
||||
on:click={close}
|
||||
class="button is-text is-only-icon"
|
||||
style:--button-size="1.5rem"
|
||||
aria-label="close popup">
|
||||
@@ -35,3 +46,20 @@
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
//style for cover with is-page data attribute
|
||||
.cover-frame[data-is-page] {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 30;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
background: var(--color-bgcolor-neutral-primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={() => (show = false)}>Cancel</Button>
|
||||
{#if href}
|
||||
<Button secondary {href}>Exit</Button>
|
||||
<Button secondary size="s" {href}>Exit</Button>
|
||||
{:else}
|
||||
<Button secondary submit>Exit</Button>
|
||||
<Button secondary size="s" submit>Exit</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<Heading size={5} tag="h1"><slot name="title" /></Heading>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
icon
|
||||
ariaLabel="close modal"
|
||||
href={confirmExit ? null : href}
|
||||
on:click={() => {
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
<div class="wizard-secondary-content-1">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="wizard-secondary-content-sep"></div>
|
||||
<div class="wizard-secondary-content-2">
|
||||
<slot name="aside" />
|
||||
</div>
|
||||
{#if $$slots.aside}
|
||||
<div class="wizard-secondary-content-sep" />
|
||||
<div class="wizard-secondary-content-2">
|
||||
<slot name="aside" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
<script context="module" lang="ts">
|
||||
export type WizardStepsType = Map<
|
||||
number,
|
||||
{
|
||||
label: string;
|
||||
component: typeof SvelteComponent<unknown>;
|
||||
optional?: boolean;
|
||||
disabled?: boolean;
|
||||
actions?: {
|
||||
label: string;
|
||||
onClick: () => Promise<void>;
|
||||
}[];
|
||||
}
|
||||
>;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
import { Steps } from '$lib/components';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { createEventDispatcher, type SvelteComponent } from 'svelte';
|
||||
import WizardExitModal from './wizardExitModal.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let steps: WizardStepsType;
|
||||
export let confirmExit = false;
|
||||
export let finalAction = 'Create';
|
||||
export let finalMethod: () => Promise<void> = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let showExitModal = false;
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
const openDialog = document.querySelectorAll('dialog[open]');
|
||||
if (event.key === 'Escape' && !showExitModal && !openDialog?.length) {
|
||||
event.preventDefault();
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'escape'
|
||||
});
|
||||
dispatch('exit');
|
||||
wizard.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleExit() {
|
||||
if (confirmExit) {
|
||||
showExitModal = true;
|
||||
} else {
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'button'
|
||||
});
|
||||
dispatch('exit');
|
||||
wizard.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleStepClick(e: CustomEvent<number>) {
|
||||
const step = e.detail;
|
||||
if (step < $wizard.step) {
|
||||
$wizard.step = step;
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if ($wizard.interceptor) {
|
||||
$wizard.nextDisabled = true;
|
||||
try {
|
||||
await $wizard.interceptor();
|
||||
} catch (error) {
|
||||
if (!$wizard.interceptorNotificationEnabled) return;
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
} finally {
|
||||
$wizard.nextDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
wizard.setInterceptor(null);
|
||||
if (isLastStep) {
|
||||
if ($wizard.finalAction) {
|
||||
$wizard.nextDisabled = true;
|
||||
try {
|
||||
await $wizard.finalAction();
|
||||
trackEvent('wizard_finish');
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
} finally {
|
||||
$wizard.nextDisabled = false;
|
||||
}
|
||||
} else {
|
||||
$wizard.nextDisabled = true;
|
||||
if (finalMethod) {
|
||||
await finalMethod();
|
||||
trackEvent('wizard_finish');
|
||||
$wizard.nextDisabled = false;
|
||||
} else {
|
||||
trackEvent('wizard_finish');
|
||||
dispatch('finish');
|
||||
|
||||
setTimeout(() => {
|
||||
$wizard.nextDisabled = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (steps.get($wizard.step + 1)?.disabled) {
|
||||
$wizard.step++;
|
||||
while (steps.get($wizard.step)?.disabled) {
|
||||
$wizard.step++;
|
||||
}
|
||||
} else {
|
||||
$wizard.step++;
|
||||
}
|
||||
trackEvent('wizard_next');
|
||||
}
|
||||
}
|
||||
|
||||
function previousStep() {
|
||||
if (steps.get($wizard.step - 1)?.disabled) {
|
||||
$wizard.step--;
|
||||
while (steps.get($wizard.step)?.disabled) {
|
||||
$wizard.step--;
|
||||
}
|
||||
} else {
|
||||
$wizard.step--;
|
||||
}
|
||||
wizard.setInterceptor(null);
|
||||
trackEvent('wizard_back');
|
||||
}
|
||||
|
||||
$: sortedSteps = [...steps].sort(([a], [b]) => (a > b ? 1 : -1));
|
||||
$: isLastStep = (() => {
|
||||
if ($wizard.step === steps.size) {
|
||||
return true;
|
||||
}
|
||||
let lastStep = true;
|
||||
steps.forEach((step, i) => {
|
||||
if (i > $wizard.step && !step.disabled) {
|
||||
lastStep = false;
|
||||
}
|
||||
});
|
||||
return lastStep;
|
||||
})();
|
||||
$: currentStep = steps.get($wizard.step);
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} />
|
||||
|
||||
<section class="wizard">
|
||||
<div class="wizard-header-strip" />
|
||||
<div class="wizard-start-bg" />
|
||||
<div class="wizard-end-bg" />
|
||||
|
||||
<header class="wizard-header">
|
||||
<div class="body-text-1 u-bold">{title}</div>
|
||||
|
||||
<slot name="header" />
|
||||
<button
|
||||
class="button is-text is-only-icon u-margin-inline-start-auto"
|
||||
style="--button-size:1.5rem;"
|
||||
aria-label="close wizard"
|
||||
on:click={handleExit}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<aside class="wizard-side">
|
||||
<slot name="aside">
|
||||
<Steps
|
||||
on:step={handleStepClick}
|
||||
steps={sortedSteps.map(([, { label, optional, disabled }]) => ({
|
||||
text: label,
|
||||
optional,
|
||||
disabled
|
||||
}))}
|
||||
currentStep={$wizard.step} />
|
||||
</slot>
|
||||
</aside>
|
||||
<div class="wizard-media">
|
||||
{#if $wizard.media}
|
||||
<img src={$wizard.media} alt="wizard media" loading="lazy" />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="wizard-main">
|
||||
<Form noStyle onSubmit={submit}>
|
||||
{#each sortedSteps as [step, { component }]}
|
||||
{#if $wizard.step === step}
|
||||
<svelte:component this={component} />
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="u-z-index-5 form-footer">
|
||||
<div class="u-flex u-main-end u-gap-12">
|
||||
{#if !isLastStep && currentStep?.optional}
|
||||
<Button text on:click={() => dispatch('finish')}>
|
||||
Skip optional steps
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
{#if $wizard.step === 1}
|
||||
<Button secondary on:click={handleExit}>Cancel</Button>
|
||||
{:else}
|
||||
<Button secondary on:click={previousStep}>Back</Button>
|
||||
{/if}
|
||||
|
||||
{#if currentStep?.actions}
|
||||
{#each currentStep.actions as action}
|
||||
<Button secondary on:click={action.onClick}>
|
||||
{action.label}</Button>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<Button submit disabled={$wizard.nextDisabled}>
|
||||
{isLastStep ? finalAction : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{#if showExitModal}
|
||||
<WizardExitModal
|
||||
bind:show={showExitModal}
|
||||
on:exit={() => {
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'prompt'
|
||||
});
|
||||
wizard.hide();
|
||||
}}>
|
||||
<slot name="exit">this process</slot>
|
||||
</WizardExitModal>
|
||||
{/if}
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import Wizard from '$lib/layout/wizard.svelte';
|
||||
import Wizard from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Step1 from './wizard.step1.test.svelte';
|
||||
import Step2 from './wizard.step2.test.svelte';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { dependencyStore, domain } from './wizard/store';
|
||||
import Step1 from './wizard/step1.svelte';
|
||||
import Step2 from './wizard/step2.svelte';
|
||||
@@ -34,7 +34,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
<WizardWithSteps
|
||||
title="Create domain"
|
||||
steps={stepsComponents}
|
||||
finalAction="Go to console"
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
noArrow>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
icon
|
||||
ariaLabel="more options"
|
||||
on:click={() =>
|
||||
(showDomainsDropdown[i] = !showDomainsDropdown[i])}>
|
||||
|
||||
@@ -126,7 +126,8 @@ export type PlanServices =
|
||||
| 'teams'
|
||||
| 'users'
|
||||
| 'usersAddon'
|
||||
| 'webhooks';
|
||||
| 'webhooks'
|
||||
| 'sites';
|
||||
|
||||
export function getServiceLimit(serviceId: PlanServices, tier: Tier = null): number {
|
||||
if (!isCloud) return 0;
|
||||
|
||||
@@ -22,6 +22,8 @@ export const canWriteTeams = derived(scopes, ($scopes) => $scopes.includes('team
|
||||
export const canWriteCollections = derived(scopes, ($scopes) =>
|
||||
$scopes.includes('collections.write')
|
||||
);
|
||||
export const canWriteSites = derived(scopes, ($scopes) => $scopes.includes('sites.write'));
|
||||
|
||||
export const canWriteDocuments = derived(scopes, ($scopes) => $scopes.includes('documents.write'));
|
||||
export const canWriteExecutions = derived(scopes, ($scopes) =>
|
||||
$scopes.includes('executions.write')
|
||||
@@ -46,3 +48,4 @@ export const canSeeFunctions = derived(scopes, ($scopes) => $scopes.includes('fu
|
||||
export const canSeeTeams = derived(scopes, ($scopes) => $scopes.includes('teams.read'));
|
||||
export const canSeeBuckets = derived(scopes, ($scopes) => $scopes.includes('buckets.read'));
|
||||
export const canSeeMessages = derived(scopes, ($scopes) => $scopes.includes('messages.read'));
|
||||
export const canSeeSites = derived(scopes, ($scopes) => $scopes.includes('sites.read'));
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
Storage,
|
||||
Teams,
|
||||
Users,
|
||||
Vcs
|
||||
Vcs,
|
||||
Sites
|
||||
} from '@appwrite.io/console';
|
||||
import { Billing } from '../sdk/billing';
|
||||
import { Backups } from '../sdk/backups';
|
||||
@@ -55,7 +56,8 @@ const sdkForProject = {
|
||||
users: new Users(clientProject),
|
||||
vcs: new Vcs(clientProject),
|
||||
proxy: new Proxy(clientProject),
|
||||
migrations: new Migrations(clientProject)
|
||||
migrations: new Migrations(clientProject),
|
||||
sites: new Sites(clientProject)
|
||||
};
|
||||
|
||||
export const getSdkForProject = (projectId: string) => {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { page } from '$app/stores';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
|
||||
export const installations = derived(
|
||||
page,
|
||||
($page) => $page.data.installations as Models.InstallationList
|
||||
);
|
||||
export const repository = writable<Models.ProviderRepository>();
|
||||
export const installation = writable<Models.Installation>();
|
||||
|
||||
export function sortBranches(branches: Models.Branch[]) {
|
||||
return branches.sort((a, b) => {
|
||||
if (a.name === 'main' || a.name === 'master') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return a.name > b.name ? 1 : -1;
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import type { SvelteComponent } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import SelectRepository from './steps/selectRepository.svelte';
|
||||
import GitConfiguration from './steps/gitConfiguration.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { func } from '$routes/(console)/project-[project]/functions/function-[function]/store';
|
||||
import { choices, installation, repository } from './store';
|
||||
@@ -74,7 +74,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
<WizardWithSteps
|
||||
title="Connect Git"
|
||||
steps={stepsComponents}
|
||||
on:finish={createGitHubInstallation}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { ID, Runtime } from '@appwrite.io/console';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { goto } from '$app/navigation';
|
||||
@@ -84,4 +84,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create Function" steps={stepsComponents} on:finish={create} on:exit={resetState} />
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={stepsComponents}
|
||||
on:finish={create}
|
||||
on:exit={resetState} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { ID, Runtime } from '@appwrite.io/console';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { goto } from '$app/navigation';
|
||||
@@ -91,4 +91,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create Function" steps={stepsComponents} on:finish={create} on:exit={resetState} />
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={stepsComponents}
|
||||
on:finish={create}
|
||||
on:exit={resetState} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ID, Runtime } from '@appwrite.io/console';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { goto } from '$app/navigation';
|
||||
@@ -123,7 +123,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={$templateStepsComponents}
|
||||
on:finish={create}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import InputSelectSearch from '$lib/elements/forms/inputSelectSearch.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { sortBranches } from '$routes/(console)/project-[project]/functions/function-[function]/settings/updateConfiguration.svelte';
|
||||
import { sortBranches } from '$lib/stores/vcs';
|
||||
import { choices, installation, repository } from '../store';
|
||||
|
||||
$choices.rootDir ??= '';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { page } from '$app/stores';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { derived, writable, type Writable } from 'svelte/store';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto, invalidateAll } from '$app/navigation';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import Wizard from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Wizard from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { migrationFormToResources } from '$lib/stores/migration';
|
||||
import { getSdkForProject } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
ariaLabel="More options"
|
||||
on:click={() => {
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
on:click={() => {
|
||||
showDropdown[i] = !showDropdown[i];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { onDestroy } from 'svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { feedbackData } from '$lib/stores/feedback';
|
||||
import Step1 from './wizard/feedback/step1.svelte';
|
||||
import Step2 from './wizard/feedback/step2.svelte';
|
||||
@@ -22,4 +22,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Feedback" steps={stepsComponents} finalAction="Close" />
|
||||
<WizardWithSteps title="Feedback" steps={stepsComponents} finalAction="Close" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { invalidate } from '$app/navigation';
|
||||
@@ -60,7 +60,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
<WizardWithSteps
|
||||
title="Add credits"
|
||||
steps={$addCreditWizardSteps}
|
||||
finalAction="Add credits"
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
ariaLabel="More options"
|
||||
on:click={() => {
|
||||
@@ -121,7 +121,7 @@
|
||||
<DropList bind:show={showDropdown} placement="bottom-start">
|
||||
<Button
|
||||
secondary
|
||||
round
|
||||
icon
|
||||
on:click={() => {
|
||||
if ($addressList.total) {
|
||||
showDropdown = !showDropdown;
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<TableCell>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
icon
|
||||
ariaLabel="remove alert"
|
||||
on:click={() =>
|
||||
(alerts = alerts.filter((a) => a !== alert))}>
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
placement="bottom-start"
|
||||
noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
noMargin
|
||||
ariaLabel="More options"
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<CreditCardInfo isBox paymentMethod={defaultPaymentMethod}>
|
||||
<DropList bind:show={showDropdown} placement="bottom-start" noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
ariaLabel="More options"
|
||||
on:click={() => {
|
||||
@@ -155,7 +155,7 @@
|
||||
<DropList bind:show={showDropdown} placement="bottom-start">
|
||||
<Button
|
||||
secondary
|
||||
round
|
||||
icon
|
||||
on:click={() => {
|
||||
if (filteredPaymentMethods.length) {
|
||||
showDropdown = !showDropdown;
|
||||
@@ -202,7 +202,7 @@
|
||||
<CreditCardInfo isBox paymentMethod={backupPaymentMethod}>
|
||||
<DropList bind:show={showDropdownBackup} placement="bottom-start" noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
ariaLabel="More options"
|
||||
on:click={() => {
|
||||
@@ -253,7 +253,7 @@
|
||||
<DropList bind:show={showDropdownBackup} placement="bottom-start">
|
||||
<Button
|
||||
secondary
|
||||
round
|
||||
icon
|
||||
on:click={() => {
|
||||
if (filteredPaymentMethods.length) {
|
||||
showDropdownBackup = !showDropdownBackup;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { page } from '$app/stores';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import type { AggregationList, Invoice } from '$lib/sdk/billing';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import Step1 from './wizard/step1.svelte';
|
||||
import Step2 from './wizard/step2.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { page } from '$app/stores';
|
||||
@@ -69,4 +69,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create project" steps={stepsComponents} finalMethod={create} on:exit={onFinish} />
|
||||
<WizardWithSteps
|
||||
title="Create project"
|
||||
steps={stepsComponents}
|
||||
finalMethod={create}
|
||||
on:exit={onFinish} />
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
{#if user.status}
|
||||
{@const success = user.emailVerification || user.phoneVerification}
|
||||
<Badge
|
||||
size="x-small"
|
||||
size="xs"
|
||||
variant="secondary"
|
||||
type={success ? 'success' : undefined}
|
||||
content={user.emailVerification && user.phoneVerification
|
||||
@@ -107,17 +107,13 @@
|
||||
? 'verified phone'
|
||||
: 'unverified'} />
|
||||
{:else}
|
||||
<Badge
|
||||
size="x-small"
|
||||
variant="secondary"
|
||||
type="error"
|
||||
content="blocked" />
|
||||
<Badge size="xs" variant="secondary" type="error" content="blocked" />
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Copy value={user.$id} event="user">
|
||||
<Tag size="small">
|
||||
<Icon size="small" icon={IconDuplicate} />
|
||||
<Tag size="s">
|
||||
<Icon size="s" icon={IconDuplicate} />
|
||||
User ID
|
||||
</Tag>
|
||||
</Copy>
|
||||
@@ -142,7 +138,7 @@
|
||||
total={data.users.total} />
|
||||
{:else if data.search}
|
||||
<EmptySearch target="users" hidePagination>
|
||||
<Button href={`${base}/project-${projectId}/auth`} size="small" secondary
|
||||
<Button href={`${base}/project-${projectId}/auth`} size="s" secondary
|
||||
>Clear Search</Button>
|
||||
</EmptySearch>
|
||||
{:else}
|
||||
|
||||
@@ -101,10 +101,7 @@
|
||||
total={data.teams.total} />
|
||||
{:else if data.search}
|
||||
<EmptySearch target="teams" search={data.search} hidePagination={data.teams.total === 0}>
|
||||
<Button
|
||||
secondary
|
||||
size="small"
|
||||
href={`${base}/project-${$page.params.project}/auth/teams`}>
|
||||
<Button secondary size="s" href={`${base}/project-${$page.params.project}/auth/teams`}>
|
||||
Clear Search
|
||||
</Button>
|
||||
</EmptySearch>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
<TableCell>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
icon
|
||||
ariaLabel="Delete item"
|
||||
on:click={() => {
|
||||
selectedSessionId = session.$id;
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<TableCell>
|
||||
<Button
|
||||
on:click={() => (showDelete = true)}
|
||||
round
|
||||
icon
|
||||
text
|
||||
ariaLabel="Delete authenticator">
|
||||
<span class="icon-trash" aria-hidden="true" />
|
||||
|
||||
+1
-1
@@ -322,7 +322,7 @@
|
||||
{#each restoreOptions as restoreOption}
|
||||
<div class="u-width-full-line">
|
||||
<LabelCard
|
||||
padding={1}
|
||||
padding="small"
|
||||
name="restore"
|
||||
value={restoreOption.id}
|
||||
bind:group={selectedRestoreOption}>
|
||||
|
||||
+3
-3
@@ -3,8 +3,8 @@
|
||||
import { page } from '$app/stores';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
@@ -80,4 +80,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create document" steps={stepsComponents} on:finish={create} />
|
||||
<WizardWithSteps title="Create document" steps={stepsComponents} on:finish={create} />
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@
|
||||
<Button
|
||||
noMargin
|
||||
text
|
||||
round
|
||||
icon
|
||||
on:click={() => removeArrayItem(attribute.key, index)}>
|
||||
<span class="icon-x" aria-hidden="true" />
|
||||
</Button>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
title="Functions"
|
||||
buttonText={$canWriteFunctions ? 'Create function' : ''}
|
||||
buttonEvent="create_function"
|
||||
buttonMethod={openWizard}
|
||||
buttonHref={`${base}/project-${project}/functions/create-function`}
|
||||
total={data.functions.total} />
|
||||
|
||||
{#if data.functions.total}
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
<script context="module" lang="ts">
|
||||
import CreateTemplate from './wizard/createTemplate.svelte';
|
||||
|
||||
export function connectTemplate(
|
||||
template: Models.TemplateFunction,
|
||||
runtime: string | null = null
|
||||
) {
|
||||
const variables: Record<string, string> = {};
|
||||
template.variables.forEach((variable) => {
|
||||
variables[variable.name] = variable.value ?? '';
|
||||
});
|
||||
|
||||
templateStore.set(template);
|
||||
templateConfig.set({
|
||||
$id: null,
|
||||
runtime,
|
||||
name: template.name,
|
||||
variables,
|
||||
repositoryBehaviour: 'new',
|
||||
repositoryName: template.id,
|
||||
repositoryPrivate: true,
|
||||
repositoryId: null
|
||||
});
|
||||
wizard.start(CreateTemplate);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { AvatarGroup, Box, Heading } from '$lib/components';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { templateConfig, template as templateStore } from './store';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { page } from '$app/stores';
|
||||
import { baseRuntimesList } from '$lib/stores/runtimes';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import WizardCover from '$lib/layout/wizardCover.svelte';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import { isSelfHosted } from '$lib/system';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
import { featuredTemplatesList, starterTemplate } from '$lib/stores/templates';
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import CreateGit from './wizard/createGit.svelte';
|
||||
import CreateManual from './wizard/createManual.svelte';
|
||||
import Repositories from '../../../../../lib/components/repositories.svelte';
|
||||
import { repository } from '$lib/stores/vcs';
|
||||
|
||||
const isVcsEnabled = $consoleVariables?._APP_VCS_ENABLED === true;
|
||||
let hasInstallations: boolean;
|
||||
let selectedRepository: string;
|
||||
|
||||
function connect(event: CustomEvent<Models.ProviderRepository>) {
|
||||
trackEvent('click_connect_repository', {
|
||||
from: 'cover'
|
||||
});
|
||||
repository.set(event.detail);
|
||||
wizard.start(CreateGit);
|
||||
}
|
||||
|
||||
let previousPage: string = base;
|
||||
afterNavigate(({ from }) => {
|
||||
previousPage = from?.url?.pathname || previousPage;
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardCover bind:previousPage>
|
||||
<svelte:fragment slot="title">Create Function</svelte:fragment>
|
||||
<div class="wizard-container container">
|
||||
<div class="git-container u-position-relative">
|
||||
<div class="grid-1-1 u-gap-24">
|
||||
<div class="card u-cross-child-start u-height-100-percent">
|
||||
<Heading size="6" tag="h6">Connect Git repository</Heading>
|
||||
<p class="u-margin-block-start-8">
|
||||
Create and deploy a function with a connected git repository.
|
||||
</p>
|
||||
<div class="u-margin-block-start-24">
|
||||
<Repositories
|
||||
bind:hasInstallations
|
||||
bind:selectedRepository
|
||||
action="button"
|
||||
callbackState={{
|
||||
from: 'github',
|
||||
to: 'cover'
|
||||
}}
|
||||
on:connect={connect} />
|
||||
</div>
|
||||
{#if isSelfHosted && !isVcsEnabled}
|
||||
<div
|
||||
class="overlay u-flex-vertical u-position-absolute u-height-100-percent u-width-full-line u-z-index-1 u-text-center u-inset-0"
|
||||
style="border-radius: var(--border-radius-medium)">
|
||||
<div
|
||||
class="u-flex-vertical u-height-100-percent u-main-center u-cross-center u-gap-16 u-padding-inline-24">
|
||||
<Heading size="7" tag="h6" trimmed={false}>
|
||||
Connect your self-hosted instance to Git
|
||||
</Heading>
|
||||
<p>
|
||||
Configure your self-hosted instance to connect your function to
|
||||
a Git repository.
|
||||
<a
|
||||
href="https://appwrite.io/docs/advanced/self-hosting/functions#git"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="card u-height-100-percent">
|
||||
<section class="common-section">
|
||||
<Heading size="6" tag="h6">Quick start</Heading>
|
||||
<p class="u-margin-block-start-8">Use a starter template.</p>
|
||||
<ul
|
||||
class="grid-box u-margin-block-start-16"
|
||||
style:--grid-item-size="8rem"
|
||||
style:--grid-item-size-small-screens="9rem"
|
||||
style:--grid-gap=".5rem">
|
||||
{#await Promise.all([$baseRuntimesList, $starterTemplate])}
|
||||
{#each Array(6) as _i}
|
||||
<li>
|
||||
<button
|
||||
disabled
|
||||
class="box u-width-full-line u-flex u-cross-center u-main-center"
|
||||
style:--box-padding="1rem"
|
||||
style:--box-border-radius="var(--border-radius-small)">
|
||||
<div class="avatar is-size-small">
|
||||
<div class="loader u-margin-16" />
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
{:then [response, quickStart]}
|
||||
{@const runtimes = new Map(
|
||||
response.runtimes.map((r) => [r.$id, r])
|
||||
)}
|
||||
{@const templates = quickStart.runtimes.filter((template) =>
|
||||
runtimes.has(template.name)
|
||||
)}
|
||||
{#each templates.slice(0, 6) as template}
|
||||
{@const runtimeDetail = runtimes.get(template.name)}
|
||||
<li>
|
||||
<button
|
||||
on:click={() => {
|
||||
trackEvent('click_connect_template', {
|
||||
from: 'cover',
|
||||
template: quickStart.id,
|
||||
runtime: template.name
|
||||
});
|
||||
}}
|
||||
on:click={() =>
|
||||
connectTemplate(quickStart, template.name)}
|
||||
class="box u-width-full-line u-flex u-cross-center u-gap-8"
|
||||
style:--box-padding="1rem"
|
||||
style:--box-border-radius="var(--border-radius-small)">
|
||||
<div class="avatar is-size-small">
|
||||
<img
|
||||
style:--p-text-size="1.25rem"
|
||||
src={`${base}/icons/${$app.themeInUse}/color/${
|
||||
template.name.split('-')[0]
|
||||
}.svg`}
|
||||
alt={template.name} />
|
||||
</div>
|
||||
<div class="body-text-2">
|
||||
{runtimeDetail.name}
|
||||
{#if runtimeDetail.name.toLowerCase() === 'deno'}
|
||||
<span class="inline-tag">New</span>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
{#if templates.length < 6}
|
||||
<li
|
||||
use:tooltip={{
|
||||
content: 'More runtimes coming soon'
|
||||
}}>
|
||||
<Box
|
||||
class="u-width-full-line u-flex u-cross-center u-gap-8"
|
||||
padding={16}
|
||||
radius="small">
|
||||
<AvatarGroup
|
||||
icons={['dotnet', 'deno']}
|
||||
total={4}
|
||||
avatarSize="small"
|
||||
color="u-color-text-gray"
|
||||
bordered />
|
||||
</Box>
|
||||
</li>
|
||||
{/if}
|
||||
{/await}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<Button
|
||||
text
|
||||
class="u-margin-block-start-24 u-margin-inline-start-auto"
|
||||
href={`${base}/project-${$page.params.project}/functions/templates?useCase=starter`}>
|
||||
All starter templates <span class="icon-cheveron-right" />
|
||||
</Button>
|
||||
<div class="u-sep-block-start common-section" />
|
||||
<section class="common-section">
|
||||
<Heading size="6" tag="h6">Templates</Heading>
|
||||
<p class="text u-margin-block-start-8">
|
||||
Find the right template for your use case.
|
||||
</p>
|
||||
|
||||
<ul class="clickable-list u-margin-block-start-16">
|
||||
{#await $featuredTemplatesList}
|
||||
{#each Array(3) as _i}
|
||||
<li>
|
||||
<button
|
||||
disabled
|
||||
class="clickable-list-button u-width-full-line u-flex u-gap-12">
|
||||
<div class="avatar is-size-small">
|
||||
<div class="loader" />
|
||||
</div>
|
||||
<div class="u-flex u-flex-vertical u-gap-4">
|
||||
<div class="body-text-2 u-bold u-trim">
|
||||
<div class="loader" />
|
||||
</div>
|
||||
<div class="u-trim-1 u-color-text-gray">
|
||||
<div class="loader" />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
{:then templatesListWithoutStarter}
|
||||
{#each templatesListWithoutStarter.templates as template}
|
||||
<li class="clickable-list-item">
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
trackEvent('click_connect_template', {
|
||||
from: 'cover',
|
||||
template: template.id
|
||||
});
|
||||
}}
|
||||
on:click={() => connectTemplate(template)}
|
||||
class="clickable-list-button u-width-full-line u-flex u-gap-12">
|
||||
<div
|
||||
class="avatar is-size-small"
|
||||
style:--p-text-size="1.25rem">
|
||||
<span class={template.icon} />
|
||||
</div>
|
||||
<div class="u-flex u-flex-vertical u-gap-4">
|
||||
<div class="body-text-2 u-bold u-trim">
|
||||
{template.name}
|
||||
</div>
|
||||
<div class="u-trim-1 u-color-text-gray">
|
||||
{template.tagline}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
{/await}
|
||||
</ul>
|
||||
</section>
|
||||
<Button
|
||||
text
|
||||
class="u-margin-inline-start-auto u-margin-block-start-16"
|
||||
href={`${base}/project-${$page.params.project}/functions/templates`}>
|
||||
<span> All templates </span>
|
||||
<span class="icon-cheveron-right" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="u-margin-block-start-16">
|
||||
You can also create a function <button
|
||||
class="link"
|
||||
on:click={() => {
|
||||
trackEvent('click_create_function_manual', {
|
||||
from: 'cover'
|
||||
});
|
||||
}}
|
||||
on:click={() => wizard.start(CreateManual)}>manually</button>
|
||||
or using the CLI.
|
||||
<a
|
||||
href="https://appwrite.io/docs/products/functions/deployment"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">Learn more</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</WizardCover>
|
||||
|
||||
<style lang="scss">
|
||||
.git-container .overlay {
|
||||
background: linear-gradient(
|
||||
0,
|
||||
hsl(var(--p-card-bg-color)) 68.91%,
|
||||
hsl(var(--p-card-bg-color) / 0.5) 92.8%
|
||||
);
|
||||
}
|
||||
|
||||
.inline-tag {
|
||||
line-height: 140%;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
|
||||
export const template = writable<Models.TemplateFunction>();
|
||||
export const templateConfig = writable<{
|
||||
$id: string;
|
||||
name: string;
|
||||
runtime: string;
|
||||
variables: { [key: string]: unknown };
|
||||
repositoryBehaviour: 'new' | 'existing' | 'manual';
|
||||
repositoryName?: string;
|
||||
repositoryPrivate?: boolean;
|
||||
repositoryId: string;
|
||||
execute?: boolean;
|
||||
scopes?: string[];
|
||||
}>();
|
||||
|
||||
export const choices = writable<{
|
||||
branch: string;
|
||||
rootDir: string;
|
||||
silentMode: boolean;
|
||||
}>({
|
||||
branch: null,
|
||||
rootDir: null,
|
||||
silentMode: null
|
||||
});
|
||||
|
||||
const initialCreateFunction: Partial<Models.Function> = {
|
||||
$id: null,
|
||||
name: null,
|
||||
entrypoint: null,
|
||||
execute: [],
|
||||
runtime: null,
|
||||
commands: null
|
||||
};
|
||||
|
||||
function createFunctionStore() {
|
||||
const store = writable<Partial<Models.Function>>({
|
||||
...initialCreateFunction
|
||||
});
|
||||
|
||||
const reset = () => {
|
||||
store.set({ ...initialCreateFunction });
|
||||
};
|
||||
|
||||
return {
|
||||
...store,
|
||||
reset
|
||||
};
|
||||
}
|
||||
export const createFunction = createFunctionStore();
|
||||
|
||||
export const createFunctionDeployment = writable<FileList>();
|
||||
|
||||
export const templateStepsComponents: Writable<WizardStepsType> = writable(new Map());
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { func } from '$routes/(console)/project-[project]/functions/function-[function]/store';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { Runtime } from '@appwrite.io/console';
|
||||
import { choices } from '../store';
|
||||
import SelectRepository from './steps/selectRepository.svelte';
|
||||
import GitConfiguration from './steps/gitConfiguration.svelte';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
|
||||
async function createGitHubInstallation() {
|
||||
try {
|
||||
if (!isValueOfStringEnum(Runtime, $func.runtime)) {
|
||||
throw new Error(`Invalid runtime: ${$func.runtime}`);
|
||||
}
|
||||
await sdk.forProject.functions.update(
|
||||
$func.$id,
|
||||
$func.name,
|
||||
$func.runtime,
|
||||
$func.execute || undefined,
|
||||
$func.events || undefined,
|
||||
$func.schedule || undefined,
|
||||
$func.timeout || undefined,
|
||||
$func.enabled || undefined,
|
||||
$func.logging || undefined,
|
||||
$func.entrypoint || undefined,
|
||||
$func.commands || undefined,
|
||||
$func.scopes || undefined,
|
||||
$installation.$id,
|
||||
$repository.id,
|
||||
$choices.branch,
|
||||
$choices.silentMode || undefined,
|
||||
$choices.rootDir
|
||||
);
|
||||
trackEvent(Submit.FunctionConnectRepo, {
|
||||
customId: !!$func.$id
|
||||
});
|
||||
await invalidate(Dependencies.FUNCTION);
|
||||
resetState();
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.FunctionConnectRepo);
|
||||
}
|
||||
}
|
||||
|
||||
function resetState() {
|
||||
choices.set({
|
||||
branch: null,
|
||||
silentMode: false,
|
||||
rootDir: null
|
||||
});
|
||||
installation.set(null);
|
||||
repository.set(null);
|
||||
}
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Repository',
|
||||
component: SelectRepository
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Git',
|
||||
component: GitConfiguration
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardWithSteps
|
||||
title="Connect Git"
|
||||
steps={stepsComponents}
|
||||
on:finish={createGitHubInstallation}
|
||||
on:exit={resetState} />
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<script lang="ts">
|
||||
import { ID, Runtime } from '@appwrite.io/console';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { goto } from '$app/navigation';
|
||||
import { choices, createFunction } from '../store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import ExecuteAccess from './steps/executeAccess.svelte';
|
||||
import GitConfiguration from './steps/gitConfiguration.svelte';
|
||||
import FunctionConfiguration from './steps/functionConfiguration.svelte';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
if (!isValueOfStringEnum(Runtime, $createFunction.runtime)) {
|
||||
throw new Error(`Invalid runtime: ${$createFunction.runtime}`);
|
||||
}
|
||||
const response = await sdk.forProject.functions.create(
|
||||
$createFunction.$id || ID.unique(),
|
||||
$createFunction.name,
|
||||
$createFunction.runtime,
|
||||
$createFunction.execute || undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
$createFunction.entrypoint,
|
||||
$createFunction.commands || undefined,
|
||||
undefined,
|
||||
$installation.$id,
|
||||
$repository.id,
|
||||
$choices.branch,
|
||||
$choices.silentMode || undefined,
|
||||
$choices.rootDir || undefined
|
||||
);
|
||||
goto(`${base}/project-${$page.params.project}/functions/function-${response.$id}`);
|
||||
addNotification({
|
||||
message: `${$createFunction.name} has been created`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.FunctionCreate, {
|
||||
customId: !!$createFunction.$id
|
||||
});
|
||||
resetState();
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.FunctionCreate);
|
||||
}
|
||||
}
|
||||
|
||||
function resetState() {
|
||||
createFunction.set({
|
||||
$id: null,
|
||||
name: null,
|
||||
entrypoint: null,
|
||||
execute: [],
|
||||
runtime: null
|
||||
});
|
||||
}
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Git',
|
||||
component: GitConfiguration
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Configuration',
|
||||
component: FunctionConfiguration
|
||||
});
|
||||
stepsComponents.set(3, {
|
||||
label: 'Execute access',
|
||||
component: ExecuteAccess,
|
||||
optional: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={stepsComponents}
|
||||
on:finish={create}
|
||||
on:exit={resetState} />
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<script lang="ts">
|
||||
import { ID, Runtime } from '@appwrite.io/console';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { goto } from '$app/navigation';
|
||||
import { choices, createFunction, createFunctionDeployment } from '../store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import Details from './steps/manualDetails.svelte';
|
||||
import Configuration from './steps/manualConfiguration.svelte';
|
||||
import ExecuteAccess from './steps/executeAccess.svelte';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
if (!isValueOfStringEnum(Runtime, $createFunction.runtime)) {
|
||||
throw new Error(`Invalid runtime: ${$createFunction.runtime}`);
|
||||
}
|
||||
const response = await sdk.forProject.functions.create(
|
||||
$createFunction.$id || ID.unique(),
|
||||
$createFunction.name,
|
||||
$createFunction.runtime,
|
||||
$createFunction.execute || undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
$createFunction.entrypoint,
|
||||
$createFunction.commands || undefined
|
||||
);
|
||||
await sdk.forProject.functions.createDeployment(
|
||||
response.$id,
|
||||
$createFunctionDeployment[0],
|
||||
true
|
||||
);
|
||||
goto(`${base}/project-${$page.params.project}/functions/function-${response.$id}`);
|
||||
addNotification({
|
||||
message: `${$createFunction.name} has been created`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.FunctionCreate, {
|
||||
customId: !!$createFunction.$id
|
||||
});
|
||||
resetState();
|
||||
wizard.hide();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.FunctionCreate);
|
||||
}
|
||||
}
|
||||
|
||||
function resetState() {
|
||||
createFunction.reset();
|
||||
choices.set({
|
||||
branch: null,
|
||||
silentMode: false,
|
||||
rootDir: null
|
||||
});
|
||||
installation.set(null);
|
||||
repository.set(null);
|
||||
createFunctionDeployment.set(null);
|
||||
}
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Details',
|
||||
component: Details
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Configuration',
|
||||
component: Configuration
|
||||
});
|
||||
stepsComponents.set(3, {
|
||||
label: 'Execute access',
|
||||
component: ExecuteAccess,
|
||||
optional: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={stepsComponents}
|
||||
on:finish={create}
|
||||
on:exit={resetState} />
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<script lang="ts">
|
||||
import { ID, Runtime } from '@appwrite.io/console';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { goto } from '$app/navigation';
|
||||
import { choices, template, templateConfig, templateStepsComponents } from '../store';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import TemplateConfiguration from './steps/templateConfiguration.svelte';
|
||||
import TemplatePermissions from './steps/templatePermissions.svelte';
|
||||
import TemplateVariables from './steps/templateVariables.svelte';
|
||||
import TemplateDeployment from './steps/templateDeployment.svelte';
|
||||
import CreateRepository from './steps/createRepository.svelte';
|
||||
import GitConfiguration from './steps/gitConfiguration.svelte';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
if (!isValueOfStringEnum(Runtime, $templateConfig.runtime)) {
|
||||
throw new Error(`Invalid runtime: ${$templateConfig.runtime}`);
|
||||
}
|
||||
const runtimeDetail = $template.runtimes.find(
|
||||
(r) => r.name === $templateConfig.runtime
|
||||
);
|
||||
|
||||
const response = await sdk.forProject.functions.create(
|
||||
$templateConfig.$id || ID.unique(),
|
||||
$templateConfig.name,
|
||||
$templateConfig.runtime,
|
||||
$templateConfig?.execute ? $template.permissions || undefined : undefined,
|
||||
$template.events || undefined,
|
||||
$template.cron || undefined,
|
||||
$template.timeout || undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
runtimeDetail.entrypoint,
|
||||
runtimeDetail.commands || undefined,
|
||||
$templateConfig?.scopes?.length ? $templateConfig.scopes : undefined,
|
||||
$templateConfig.repositoryBehaviour === 'manual' ? undefined : $installation.$id,
|
||||
$templateConfig.repositoryBehaviour === 'manual' ? undefined : $repository.id,
|
||||
$templateConfig.repositoryBehaviour === 'manual' ? undefined : $choices.branch,
|
||||
$templateConfig.repositoryBehaviour === 'manual'
|
||||
? undefined
|
||||
: $choices.silentMode || undefined,
|
||||
$templateConfig.repositoryBehaviour === 'manual'
|
||||
? undefined
|
||||
: $choices.rootDir || undefined,
|
||||
$template.providerRepositoryId || undefined,
|
||||
$template.providerOwner || undefined,
|
||||
runtimeDetail.providerRootDirectory || undefined,
|
||||
$template.providerVersion || undefined
|
||||
);
|
||||
|
||||
if ($templateConfig.variables) {
|
||||
const promises = Object.entries($templateConfig.variables).map(([key, value]) =>
|
||||
sdk.forProject.functions.createVariable(response.$id, key, value?.toString())
|
||||
);
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
goto(`${base}/project-${$page.params.project}/functions/function-${response.$id}`);
|
||||
addNotification({
|
||||
message: `${response.name} has been created`,
|
||||
type: 'success'
|
||||
});
|
||||
trackEvent(Submit.FunctionCreate, {
|
||||
customId: !!response.$id,
|
||||
runtime: response.runtime,
|
||||
deployment_type: $templateConfig.repositoryBehaviour,
|
||||
scopes: $templateConfig.scopes
|
||||
});
|
||||
resetState();
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.FunctionCreate);
|
||||
}
|
||||
}
|
||||
function resetState() {
|
||||
wizard.hide();
|
||||
templateConfig.set(null);
|
||||
template.set(null);
|
||||
installation.set(null);
|
||||
}
|
||||
|
||||
$templateStepsComponents.set(1, {
|
||||
label: 'Configuration',
|
||||
component: TemplateConfiguration
|
||||
});
|
||||
$templateStepsComponents.set(2, {
|
||||
label: 'Permissions',
|
||||
component: TemplatePermissions
|
||||
});
|
||||
$templateStepsComponents.set(3, {
|
||||
label: 'Variables',
|
||||
component: TemplateVariables,
|
||||
disabled: !$template?.variables?.length
|
||||
});
|
||||
$templateStepsComponents.set(4, {
|
||||
label: 'Deployment',
|
||||
component: TemplateDeployment
|
||||
});
|
||||
$templateStepsComponents.set(5, {
|
||||
label: 'Repository',
|
||||
component: CreateRepository
|
||||
});
|
||||
$templateStepsComponents.set(6, {
|
||||
label: 'Branch',
|
||||
component: GitConfiguration
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={$templateStepsComponents}
|
||||
on:finish={create}
|
||||
on:exit={resetState} />
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Button from '$lib/elements/forms/button.svelte';
|
||||
import FormList from '$lib/elements/forms/formList.svelte';
|
||||
import InputChoice from '$lib/elements/forms/inputChoice.svelte';
|
||||
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
|
||||
import InputText from '$lib/elements/forms/inputText.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import Repositories from '$lib/components/repositories.svelte';
|
||||
import { template, templateConfig } from '../../store';
|
||||
import { Box } from '$lib/components';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
|
||||
let selectedInstallationId: string;
|
||||
let hasInstallations: boolean;
|
||||
let selectedRepository: string;
|
||||
|
||||
onMount(() => {
|
||||
$templateConfig.repositoryPrivate = true;
|
||||
});
|
||||
|
||||
async function beforeSubmit() {
|
||||
if (!hasInstallations || !$installation) {
|
||||
throw new Error('Please connect a Git provider');
|
||||
}
|
||||
|
||||
if ($templateConfig.repositoryBehaviour === 'new') {
|
||||
const repo = await sdk.forProject.vcs.createRepository(
|
||||
$installation.$id,
|
||||
$templateConfig.repositoryName,
|
||||
$templateConfig.repositoryPrivate
|
||||
);
|
||||
$repository = repo;
|
||||
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Repository successfully created.'
|
||||
});
|
||||
}
|
||||
|
||||
if (!$repository) {
|
||||
throw new Error('Please select a repository');
|
||||
}
|
||||
}
|
||||
|
||||
function connectGitHub() {
|
||||
const redirect = new URL($page.url);
|
||||
const callbackState = {
|
||||
from: 'github',
|
||||
to: 'template',
|
||||
step: '5',
|
||||
template: $template.id,
|
||||
templateConfig: JSON.stringify($templateConfig)
|
||||
};
|
||||
|
||||
if (callbackState) {
|
||||
Object.keys(callbackState).forEach((key) => {
|
||||
redirect.searchParams.append(key, callbackState[key]);
|
||||
});
|
||||
}
|
||||
const target = new URL(`${sdk.forProject.client.config.endpoint}/vcs/github/authorize`);
|
||||
target.searchParams.set('project', $page.params.project);
|
||||
target.searchParams.set('success', redirect.toString());
|
||||
target.searchParams.set('failure', redirect.toString());
|
||||
target.searchParams.set('mode', 'admin');
|
||||
return target;
|
||||
}
|
||||
|
||||
async function loadInstallations() {
|
||||
const { installations } = await sdk.forProject.vcs.listInstallations();
|
||||
if (installations.length) {
|
||||
$installation = installations[0];
|
||||
hasInstallations = true;
|
||||
}
|
||||
|
||||
return installations;
|
||||
}
|
||||
|
||||
function getProviderIcon(provider: string) {
|
||||
switch (provider) {
|
||||
case 'github':
|
||||
return 'icon-github';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep {beforeSubmit}>
|
||||
<svelte:fragment slot="title">Repository</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Select a Git repository that will trigger your function deployments when updated.
|
||||
</svelte:fragment>
|
||||
|
||||
{#if $templateConfig.repositoryBehaviour === 'existing'}
|
||||
<Repositories bind:hasInstallations bind:selectedRepository />
|
||||
{:else}
|
||||
{#await loadInstallations()}
|
||||
<div class="u-flex u-gap-8 u-cross-center u-main-center">
|
||||
<div class="loader u-margin-32" />
|
||||
</div>
|
||||
{:then installations}
|
||||
{#if hasInstallations}
|
||||
<FormList>
|
||||
<InputSelect
|
||||
id="installation"
|
||||
label="Git organization"
|
||||
options={installations.map((entry) => {
|
||||
return {
|
||||
label: entry.organization,
|
||||
value: entry.$id
|
||||
};
|
||||
})}
|
||||
on:change={() => {
|
||||
$installation = installations.find(
|
||||
(entry) => entry.$id === selectedInstallationId
|
||||
);
|
||||
}}
|
||||
bind:value={selectedInstallationId} />
|
||||
</FormList>
|
||||
{:else}
|
||||
<div class="u-flex u-cross-center u-flex-vertical u-gap-16">
|
||||
<Button href={connectGitHub().toString()} fullWidth secondary>
|
||||
<span class="icon-github" aria-hidden="true" />
|
||||
<span class="text">GitHub</span>
|
||||
</Button>
|
||||
<Button disabled fullWidth secondary>
|
||||
<span class="icon-gitlab" aria-hidden="true" />
|
||||
<span class="text">GitLab (coming soon)</span>
|
||||
</Button>
|
||||
<Button disabled fullWidth secondary>
|
||||
<span class="icon-bitBucket" aria-hidden="true" />
|
||||
<span class="text">BitBucket (coming soon)</span>
|
||||
</Button>
|
||||
<Button disabled fullWidth secondary>
|
||||
<span class="icon-azure" aria-hidden="true" />
|
||||
<span class="text">Azure (coming soon)</span>
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $installation}
|
||||
<Box radius="small" class="u-margin-block-start-20">
|
||||
<div class="u-flex u-gap-16">
|
||||
<div class="avatar is-size-x-small">
|
||||
<span class={getProviderIcon($installation.provider)} />
|
||||
</div>
|
||||
<div class="u-cross-child-center u-line-height-1-5">
|
||||
<h6 class="u-bold u-trim-1">
|
||||
{$installation.organization}/{$templateConfig.repositoryName}
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="u-margin-block-start-24">
|
||||
<FormList>
|
||||
<InputText
|
||||
id="repositoryName"
|
||||
label="Repository name"
|
||||
placeholder="my-repository"
|
||||
bind:value={$templateConfig.repositoryName} />
|
||||
<InputChoice
|
||||
id="repositoryPrivate"
|
||||
label="Keep repository private"
|
||||
bind:value={$templateConfig.repositoryPrivate} />
|
||||
</FormList>
|
||||
</div>
|
||||
</Box>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</WizardStep>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { Roles } from '$lib/components/permissions';
|
||||
import { createFunction } from '../../store';
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Permissions</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Choose who can execute this function using the client API. For more information, visit our
|
||||
<a
|
||||
href="https://appwrite.io/docs/advanced/platform/permissions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">
|
||||
Permissions guide</a
|
||||
>.
|
||||
</svelte:fragment>
|
||||
|
||||
<Roles bind:roles={$createFunction.execute} />
|
||||
</WizardStep>
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
<script lang="ts">
|
||||
import { Collapsible, CollapsibleItem, CustomId } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import InputTextarea from '$lib/elements/forms/inputTextarea.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { onMount } from 'svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { choices, createFunction } from '../../store';
|
||||
import { runtimesList } from '$lib/stores/runtimes';
|
||||
import { installation, repository } from '$lib/stores/vcs';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
let detectingRuntime = true;
|
||||
async function loadDetection(
|
||||
installationId: string,
|
||||
repositoryId: string,
|
||||
rootDirectory: string
|
||||
) {
|
||||
const detection = await sdk.forProject.vcs.createRepositoryDetection(
|
||||
installationId,
|
||||
repositoryId,
|
||||
rootDirectory
|
||||
);
|
||||
|
||||
return detection;
|
||||
}
|
||||
|
||||
let options = [];
|
||||
|
||||
onMount(async () => {
|
||||
options = (await $runtimesList).runtimes.map((runtime) => ({
|
||||
label: `${runtime.name} - ${runtime.version}`,
|
||||
value: runtime.$id
|
||||
}));
|
||||
|
||||
try {
|
||||
const detection = await loadDetection(
|
||||
$installation.$id,
|
||||
$repository.id,
|
||||
$choices.rootDir
|
||||
);
|
||||
$createFunction.runtime = detection.runtime;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
detectingRuntime = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Configuration</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Set your deployment configuration and any build commands here.
|
||||
</svelte:fragment>
|
||||
<FormList>
|
||||
<InputText
|
||||
label="Name"
|
||||
id="name"
|
||||
placeholder="Function name"
|
||||
autofocus
|
||||
bind:value={$createFunction.name}
|
||||
required />
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" />
|
||||
<span class="text">Function ID </span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$createFunction.$id}
|
||||
fullWidth />
|
||||
{/if}
|
||||
|
||||
{#if detectingRuntime}
|
||||
<InputSelect
|
||||
disabled
|
||||
label="Runtime"
|
||||
id="runtime"
|
||||
placeholder="Detecting runtime"
|
||||
value={null}
|
||||
options={[]} />
|
||||
{:else}
|
||||
<InputSelect
|
||||
label="Runtime"
|
||||
id="runtime"
|
||||
placeholder="Select runtime"
|
||||
bind:value={$createFunction.runtime}
|
||||
{options}
|
||||
required />
|
||||
{/if}
|
||||
|
||||
<InputText
|
||||
label="Entrypoint"
|
||||
id="entrypoint"
|
||||
placeholder="Entrypoint"
|
||||
bind:value={$createFunction.entrypoint}
|
||||
required />
|
||||
<Collapsible>
|
||||
<CollapsibleItem>
|
||||
<svelte:fragment slot="title">Build commands</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">(optional)</svelte:fragment>
|
||||
<FormList>
|
||||
<InputTextarea
|
||||
label="Commands"
|
||||
placeholder="Enter a build command (e.g. 'npm install')"
|
||||
id="build"
|
||||
tooltip="Enter a single command or chain multiple commands with the && operator"
|
||||
bind:value={$createFunction.commands} />
|
||||
</FormList>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<script lang="ts">
|
||||
import { Box, SvgIcon } from '$lib/components';
|
||||
import { FormList, InputChoice, InputText } from '$lib/elements/forms';
|
||||
import InputSelectSearch from '$lib/elements/forms/inputSelectSearch.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { choices } from '../../store';
|
||||
import { installation, repository, sortBranches } from '$lib/stores/vcs';
|
||||
|
||||
$choices.rootDir ??= '';
|
||||
|
||||
async function loadBranches() {
|
||||
const { branches } = await sdk.forProject.vcs.listRepositoryBranches(
|
||||
$installation.$id,
|
||||
$repository.id
|
||||
);
|
||||
const sorted = sortBranches(branches);
|
||||
$choices.branch = sorted[0]?.name ?? null;
|
||||
|
||||
if (!$choices.branch) {
|
||||
$choices.branch = 'main';
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Branch</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Choose the Git branch that will trigger your function deployments when updated.
|
||||
</svelte:fragment>
|
||||
|
||||
<Box radius="small">
|
||||
<div class="u-flex u-gap-8 u-cross-center">
|
||||
<SvgIcon name={$repository.provider} iconSize="large" />
|
||||
<h6 class="u-bold u-trim-1">{$installation.organization}/{$repository.name}</h6>
|
||||
</div>
|
||||
{#await loadBranches()}
|
||||
<div class="u-flex u-gap-8 u-cross-center u-main-center">
|
||||
<div class="loader u-margin-32" />
|
||||
</div>
|
||||
{:then branches}
|
||||
{@const options =
|
||||
branches
|
||||
?.map((branch) => {
|
||||
return {
|
||||
value: branch.name,
|
||||
label: branch.name
|
||||
};
|
||||
})
|
||||
?.sort((a, b) => {
|
||||
return a.label > b.label ? 1 : -1;
|
||||
}) ?? []}
|
||||
<div class="u-margin-block-start-24">
|
||||
<FormList>
|
||||
<InputSelectSearch
|
||||
required={true}
|
||||
id="branch"
|
||||
label="Production branch"
|
||||
placeholder="Select branch"
|
||||
tooltip="Every commit pushed to this branch will activate the deployment after a successful build"
|
||||
hideRequired
|
||||
bind:value={$choices.branch}
|
||||
bind:search={$choices.branch}
|
||||
on:select={(event) => {
|
||||
$choices.branch = event.detail.value;
|
||||
}}
|
||||
interactiveOutput
|
||||
name="branch"
|
||||
{options} />
|
||||
<InputText
|
||||
id="root"
|
||||
label="Root directory"
|
||||
placeholder="functions/my-function"
|
||||
bind:value={$choices.rootDir} />
|
||||
<InputChoice
|
||||
id="silent"
|
||||
label="Silent mode"
|
||||
tooltip="When enabled, comments will not be made on pull requests in this repository"
|
||||
bind:value={$choices.silentMode} />
|
||||
</FormList>
|
||||
</div>
|
||||
{/await}
|
||||
</Box>
|
||||
<p class="text u-margin-block-start-8">
|
||||
Visit your repository on <a
|
||||
href={`https://github.com/${$repository.organization}/${$repository.name}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="link">GitHub</a
|
||||
>.
|
||||
</p>
|
||||
</WizardStep>
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import { Collapsible, CollapsibleItem } from '$lib/components';
|
||||
import { FormList, InputFile, InputText } from '$lib/elements/forms';
|
||||
import InputTextarea from '$lib/elements/forms/inputTextarea.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { createFunction, createFunctionDeployment } from '../../store';
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Configuration</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Set your deployment configuration and any build commands here.
|
||||
</svelte:fragment>
|
||||
<FormList>
|
||||
<InputFile
|
||||
label="Upload a zip file (tar.gz) containing your function source code"
|
||||
allowedFileExtensions={['gz']}
|
||||
bind:files={$createFunctionDeployment}
|
||||
required />
|
||||
|
||||
<InputText
|
||||
label="Entrypoint"
|
||||
id="entrypoint"
|
||||
placeholder="Entrypoint"
|
||||
bind:value={$createFunction.entrypoint}
|
||||
required />
|
||||
<Collapsible>
|
||||
<CollapsibleItem>
|
||||
<svelte:fragment slot="title">Build commands</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">(optional)</svelte:fragment>
|
||||
<FormList>
|
||||
<InputTextarea
|
||||
label="Commands"
|
||||
placeholder="Enter a build command (e.g. 'npm install')"
|
||||
id="build"
|
||||
tooltip="Enter a single command or chain multiple commands with the && operator"
|
||||
bind:value={$createFunction.commands} />
|
||||
</FormList>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { CustomId } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { InputText, InputSelect, FormList } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { onMount } from 'svelte';
|
||||
import { createFunction } from '../../store';
|
||||
import { runtimesList } from '$lib/stores/runtimes';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
let options = [];
|
||||
|
||||
onMount(async () => {
|
||||
options = (await $runtimesList).runtimes.map((runtime) => ({
|
||||
label: `${runtime.name} - ${runtime.version}`,
|
||||
value: runtime.$id
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Details</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">Create and deploy your function manually.</svelte:fragment>
|
||||
<FormList>
|
||||
<InputText
|
||||
label="Name"
|
||||
id="name"
|
||||
placeholder="Function name"
|
||||
autofocus
|
||||
bind:value={$createFunction.name}
|
||||
required />
|
||||
|
||||
<InputSelect
|
||||
label="Runtime"
|
||||
id="runtime"
|
||||
placeholder="Select runtime"
|
||||
bind:value={$createFunction.runtime}
|
||||
{options}
|
||||
required />
|
||||
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" />
|
||||
<span class="text">Function ID </span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$createFunction.$id}
|
||||
fullWidth />
|
||||
{/if}
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import Repositories from '$lib/components/repositories.svelte';
|
||||
|
||||
let hasInstallations: boolean;
|
||||
let selectedRepository: string;
|
||||
async function beforeSubmit() {
|
||||
if (!hasInstallations) {
|
||||
throw new Error('Please connect a Git provider');
|
||||
}
|
||||
if (!selectedRepository) {
|
||||
throw new Error('Please select a repository');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep {beforeSubmit}>
|
||||
<svelte:fragment slot="title">Repository</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Select a Git repository that will trigger your function deployments when updated.
|
||||
</svelte:fragment>
|
||||
<Repositories bind:hasInstallations bind:selectedRepository />
|
||||
</WizardStep>
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { CustomId } from '$lib/components';
|
||||
import { Pill } from '$lib/elements';
|
||||
import { FormList, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { runtimesList } from '$lib/stores/runtimes';
|
||||
import { template, templateConfig } from '../../store';
|
||||
|
||||
let showCustomId = false;
|
||||
|
||||
async function beforeSubmit() {
|
||||
if (!$templateConfig.runtime) {
|
||||
throw new Error('Please select a runtime.');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRuntimes() {
|
||||
const options = (await $runtimesList).runtimes
|
||||
.map((runtime) => ({
|
||||
label: `${runtime.name} - ${runtime.version}`,
|
||||
value: runtime.$id
|
||||
}))
|
||||
.filter((runtime) => {
|
||||
const allowedRuntimes = $template.runtimes.map((r) => r.name);
|
||||
return allowedRuntimes.includes(runtime.value);
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep {beforeSubmit}>
|
||||
<svelte:fragment slot="title">{$template.name}</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
{$template.tagline}
|
||||
</svelte:fragment>
|
||||
<FormList>
|
||||
<InputText
|
||||
label="Name"
|
||||
id="name"
|
||||
placeholder="Function name"
|
||||
autofocus
|
||||
bind:value={$templateConfig.name}
|
||||
required />
|
||||
{#await loadRuntimes()}
|
||||
<InputSelect
|
||||
label="Runtime"
|
||||
id="runtime"
|
||||
placeholder="Loading runtimes..."
|
||||
required
|
||||
disabled
|
||||
options={[]}
|
||||
value={null} />
|
||||
{:then options}
|
||||
<InputSelect
|
||||
label="Runtime"
|
||||
id="runtime"
|
||||
placeholder="Select runtime"
|
||||
required
|
||||
disabled={options.length < 1}
|
||||
{options}
|
||||
bind:value={$templateConfig.runtime} />
|
||||
{/await}
|
||||
</FormList>
|
||||
|
||||
<FormList class="u-margin-block-start-24">
|
||||
{#if !showCustomId}
|
||||
<div>
|
||||
<Pill button on:click={() => (showCustomId = !showCustomId)}>
|
||||
<span class="icon-pencil" aria-hidden="true" />
|
||||
<span class="text">Function ID</span>
|
||||
</Pill>
|
||||
</div>
|
||||
{:else}
|
||||
<CustomId
|
||||
bind:show={showCustomId}
|
||||
name="Function"
|
||||
bind:id={$templateConfig.$id}
|
||||
fullWidth />
|
||||
{/if}
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import { LabelCard } from '$lib/components';
|
||||
import { FormList } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
import { onMount } from 'svelte';
|
||||
import { templateConfig, templateStepsComponents } from '../../store';
|
||||
|
||||
const isVcsEnabled = $consoleVariables?._APP_VCS_ENABLED === true;
|
||||
|
||||
onMount(() => {
|
||||
if (!isVcsEnabled) {
|
||||
$templateConfig.repositoryBehaviour = 'manual';
|
||||
}
|
||||
});
|
||||
|
||||
async function beforeSubmit() {
|
||||
if (!$templateConfig.repositoryBehaviour) {
|
||||
throw new Error('Please select repository behaviour.');
|
||||
}
|
||||
}
|
||||
|
||||
$: if ($templateConfig.repositoryBehaviour === 'manual') {
|
||||
$templateStepsComponents.get(5).disabled = true;
|
||||
$templateStepsComponents.get(6).disabled = true;
|
||||
$templateStepsComponents = $templateStepsComponents;
|
||||
} else {
|
||||
$templateStepsComponents.get(5).disabled = false;
|
||||
$templateStepsComponents.get(6).disabled = false;
|
||||
$templateStepsComponents = $templateStepsComponents;
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep {beforeSubmit}>
|
||||
<svelte:fragment slot="title">Deployment</svelte:fragment>
|
||||
|
||||
<h3>Connect with Git <span class="inline-code">Recommended</span></h3>
|
||||
<FormList gap={16} class="u-margin-block-start-8">
|
||||
<LabelCard
|
||||
name="behaviour"
|
||||
value="new"
|
||||
bind:group={$templateConfig.repositoryBehaviour}
|
||||
disabled={!isVcsEnabled}>
|
||||
<svelte:fragment slot="title">Create a new repository</svelte:fragment>
|
||||
Clone the template to a newly created repository in your organization.
|
||||
</LabelCard>
|
||||
<LabelCard
|
||||
name="behaviour"
|
||||
value="existing"
|
||||
bind:group={$templateConfig.repositoryBehaviour}
|
||||
disabled={!isVcsEnabled}>
|
||||
<svelte:fragment slot="title">Add to existing repository</svelte:fragment>
|
||||
Clone the template to an existing repository in your organization.
|
||||
</LabelCard>
|
||||
</FormList>
|
||||
|
||||
<h3 class="u-margin-block-start-24">Quick start</h3>
|
||||
<ul class="u-margin-block-start-8">
|
||||
<LabelCard name="behaviour" value="manual" bind:group={$templateConfig.repositoryBehaviour}>
|
||||
<svelte:fragment slot="title">Connect later</svelte:fragment>
|
||||
Deploy now and continue development via CLI, or connect Git from your function settings.
|
||||
</LabelCard>
|
||||
</ul>
|
||||
</WizardStep>
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<script lang="ts">
|
||||
import { scopes } from '$lib/constants';
|
||||
import { FormList, InputChoice } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { onMount } from 'svelte';
|
||||
import { template, templateConfig } from '../../store';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
|
||||
let templateScopes = [];
|
||||
onMount(() => {
|
||||
$templateConfig.execute = $template.permissions.includes('any');
|
||||
templateScopes = scopes
|
||||
.filter((scope) => $template.scopes.includes(scope.scope))
|
||||
.map((scope) => ({
|
||||
...scope,
|
||||
active: true
|
||||
}));
|
||||
});
|
||||
|
||||
$: $templateConfig.scopes = templateScopes
|
||||
?.filter((scope) => scope.active)
|
||||
?.map((scope) => scope.scope);
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Permissions</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Enable recommended scopes and execute access for when your function is deployed.
|
||||
</svelte:fragment>
|
||||
<h3 class="label">Execute permissions</h3>
|
||||
<FormList class="u-margin-block-start-16">
|
||||
<div class="user-profile">
|
||||
<span class="avatar" style:--p-text-size="1rem">
|
||||
<span class="icon-lock-open" />
|
||||
</span>
|
||||
<span class="user-profile-info u-flex u-main-space-between u-gap-16">
|
||||
<div>
|
||||
<p class="name u-bold">
|
||||
Public (anyone can execute) <span
|
||||
class="icon-info"
|
||||
use:tooltip={{
|
||||
content:
|
||||
'You can further customize execute permissions in your function settings.'
|
||||
}} />
|
||||
</p>
|
||||
<p class="text u-margin-block-start-4">
|
||||
This could include unauthorized users and bots.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<InputChoice
|
||||
type="switchbox"
|
||||
id="execute"
|
||||
label="Execute"
|
||||
showLabel={false}
|
||||
bind:value={$templateConfig.execute}></InputChoice>
|
||||
</span>
|
||||
</div>
|
||||
</FormList>
|
||||
{#if templateScopes.length > 0}
|
||||
<h3 class="label u-margin-block-start-48">Function scopes</h3>
|
||||
<FormList class="u-margin-block-start-16">
|
||||
{#each templateScopes as scope, i}
|
||||
<div class="user-profile">
|
||||
<span class="avatar" style:--p-text-size="1rem">
|
||||
<span class={`icon-${scope.icon}`} />
|
||||
</span>
|
||||
<span class="user-profile-info u-flex u-main-space-between u-gap-16">
|
||||
<div>
|
||||
<p class="name u-bold">{scope.scope}</p>
|
||||
<p class="text u-margin-block-start-4">{scope.description}</p>
|
||||
</div>
|
||||
<InputChoice
|
||||
type="switchbox"
|
||||
id={scope.scope}
|
||||
label={scope.scope}
|
||||
showLabel={false}
|
||||
bind:value={scope.active}>
|
||||
</InputChoice>
|
||||
</span>
|
||||
</div>
|
||||
{#if i < templateScopes.length - 1}
|
||||
<div class="with-separators"></div>
|
||||
{/if}
|
||||
{/each}
|
||||
</FormList>
|
||||
{/if}
|
||||
</WizardStep>
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<script lang="ts">
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { template, templateConfig } from '../../store';
|
||||
import {
|
||||
FormList,
|
||||
Helper,
|
||||
InputPassword,
|
||||
InputText,
|
||||
InputURL,
|
||||
InputPhone,
|
||||
InputEmail,
|
||||
InputNumber
|
||||
} from '$lib/elements/forms';
|
||||
import { Card, Collapsible, CollapsibleItem } from '$lib/components';
|
||||
import type { SvelteComponent } from 'svelte';
|
||||
|
||||
async function beforeSubmit() {
|
||||
for (const variable of $template.variables) {
|
||||
if (!variable.required) {
|
||||
continue;
|
||||
}
|
||||
if (!$templateConfig.variables[variable.name]) {
|
||||
throw new Error(`Please set ${variable.name} variable.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: requiredVariables = $template?.variables?.filter((v) => v.required);
|
||||
$: optionalVariables = $template?.variables?.filter((v) => !v.required);
|
||||
|
||||
function selectComponent(variableType: string): typeof SvelteComponent<unknown> {
|
||||
switch (variableType) {
|
||||
case 'password':
|
||||
return InputPassword;
|
||||
case 'text':
|
||||
return InputText;
|
||||
case 'url':
|
||||
return InputURL;
|
||||
case 'phone':
|
||||
return InputPhone;
|
||||
case 'email':
|
||||
return InputEmail;
|
||||
case 'number':
|
||||
return InputNumber;
|
||||
default:
|
||||
return InputPassword;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep {beforeSubmit}>
|
||||
<svelte:fragment slot="title">Variables</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
Edit the values of the environment variables that will be passed to your function at
|
||||
runtime.
|
||||
</svelte:fragment>
|
||||
{#if $template?.variables?.length}
|
||||
{#if requiredVariables?.length}
|
||||
<Collapsible>
|
||||
<CollapsibleItem open={true}>
|
||||
<svelte:fragment slot="title">Required variables</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
<span class="inline-tag">{requiredVariables.length}</span>
|
||||
</svelte:fragment>
|
||||
|
||||
<FormList>
|
||||
{#each requiredVariables as variable}
|
||||
<div>
|
||||
<svelte:component
|
||||
this={selectComponent(variable.type)}
|
||||
id={variable.name}
|
||||
label={variable.name}
|
||||
placeholder={variable.placeholder ?? 'Enter value'}
|
||||
required={variable.required}
|
||||
autocomplete={false}
|
||||
minlength={variable.type === 'password' ? 0 : null}
|
||||
showPasswordButton={variable.type === 'password'}
|
||||
bind:value={$templateConfig.variables[variable.name]} />
|
||||
<Helper type="neutral">
|
||||
{@html variable.description}
|
||||
</Helper>
|
||||
</div>
|
||||
{/each}
|
||||
</FormList>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
{/if}
|
||||
|
||||
{#if optionalVariables?.length}
|
||||
<Collapsible>
|
||||
<CollapsibleItem open={!requiredVariables?.length}>
|
||||
<svelte:fragment slot="title">Optional variables</svelte:fragment>
|
||||
<svelte:fragment slot="subtitle">
|
||||
<span class="inline-tag">{optionalVariables.length}</span>
|
||||
</svelte:fragment>
|
||||
|
||||
<FormList>
|
||||
{#each optionalVariables as variable}
|
||||
<div>
|
||||
<svelte:component
|
||||
this={selectComponent(variable.type)}
|
||||
id={variable.name}
|
||||
label={variable.name}
|
||||
placeholder={variable.placeholder ?? 'Enter value'}
|
||||
required={variable.required}
|
||||
autocomplete={false}
|
||||
showPasswordButton={variable.type === 'password'}
|
||||
bind:value={$templateConfig.variables[variable.name]} />
|
||||
|
||||
<Helper type="neutral">
|
||||
{@html variable.description}
|
||||
</Helper>
|
||||
</div>
|
||||
{/each}
|
||||
</FormList>
|
||||
</CollapsibleItem>
|
||||
</Collapsible>
|
||||
{/if}
|
||||
{:else}
|
||||
<Card isDashed>
|
||||
<p class="text u-text-center">There are no environment variables to configure.</p>
|
||||
</Card>
|
||||
{/if}
|
||||
</WizardStep>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
@@ -10,7 +10,7 @@
|
||||
import Step4 from './wizard/step4.svelte';
|
||||
import Step5 from './wizard/step5.svelte';
|
||||
import { createFunction } from './wizard/store';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { base } from '$app/paths';
|
||||
@@ -107,4 +107,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create Function" steps={stepsComponents} on:finish={create} on:exit={onFinish} />
|
||||
<WizardWithSteps
|
||||
title="Create Function"
|
||||
steps={stepsComponents}
|
||||
on:finish={create}
|
||||
on:exit={onFinish} />
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<DropList bind:show placement="bottom-end">
|
||||
{#if $canWriteFunctions}
|
||||
<Button {secondary} {round} on:click={() => (show = !show)} event="create_deployment">
|
||||
<Button {secondary} icon={round} on:click={() => (show = !show)} event="create_deployment">
|
||||
<slot>
|
||||
{#if !secondary}
|
||||
<span class="icon-plus" aria-hidden="true" />
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@
|
||||
<TableCell width={40} showOverflow>
|
||||
<DropList bind:show={showDropdown[index]} placement="bottom-start" noArrow>
|
||||
<Button
|
||||
round
|
||||
icon
|
||||
text
|
||||
ariaLabel="More options"
|
||||
on:click={() => {
|
||||
|
||||
+1
-1
@@ -12,9 +12,9 @@
|
||||
import { func, repositories } from '../store';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import InputSelectSearch from '$lib/elements/forms/inputSelectSearch.svelte';
|
||||
import { sortBranches } from './updateConfiguration.svelte';
|
||||
import { installations } from '$lib/wizards/functions/store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { sortBranches } from '$lib/stores/vcs';
|
||||
|
||||
export let show: boolean;
|
||||
|
||||
|
||||
+1
-12
@@ -1,15 +1,3 @@
|
||||
<script context="module" lang="ts">
|
||||
export function sortBranches(branches: Models.Branch[]) {
|
||||
return branches.sort((a, b) => {
|
||||
if (a.name === 'main' || a.name === 'master') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return a.name > b.name ? 1 : -1;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
@@ -51,6 +39,7 @@
|
||||
import { isSelfHosted } from '$lib/system';
|
||||
import { consoleVariables } from '$routes/(console)/store';
|
||||
import { isValueOfStringEnum } from '$lib/helpers/types';
|
||||
import { sortBranches } from '$lib/stores/vcs';
|
||||
|
||||
const functionId = $page.params.function;
|
||||
const isVcsEnabled = $consoleVariables?._APP_VCS_ENABLED === true;
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<DropList bind:show={showDropdown[i]} placement="bottom-start" noArrow>
|
||||
<Button
|
||||
text
|
||||
round
|
||||
icon
|
||||
ariaLabel="more options"
|
||||
on:click={() => (showDropdown[i] = !showDropdown[i])}>
|
||||
<span class="icon-dots-horizontal" aria-hidden="true" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Step1 from './wizard/step1.svelte';
|
||||
import Step2 from './wizard/step2.svelte';
|
||||
import Step3 from './wizard/step3.svelte';
|
||||
@@ -152,4 +152,4 @@
|
||||
$wizard.finalAction = create;
|
||||
</script>
|
||||
|
||||
<Wizard title="Create message" steps={stepsComponents} finalAction="Send" />
|
||||
<WizardWithSteps title="Create message" steps={stepsComponents} finalAction="Send" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Provider from './wizard/provider.svelte';
|
||||
import Settings from './wizard/settings.svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
@@ -165,4 +165,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create provider" steps={stepsComponents} on:finish={create} />
|
||||
<WizardWithSteps title="Create provider" steps={stepsComponents} on:finish={create} />
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
bind:showTopics
|
||||
on:addTargets={addTargets}
|
||||
on:addTopics={addTopics}>
|
||||
<Button secondary round on:click={() => (showDropdown = !showDropdown)}>
|
||||
<Button secondary icon on:click={() => (showDropdown = !showDropdown)}>
|
||||
<i class="icon-plus" />
|
||||
</Button>
|
||||
</Actions>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import type { WizardStepsType } from '$lib/layout/wizard.svelte';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Step1 from './wizard/step1.svelte';
|
||||
import Step2 from './wizard/step2.svelte';
|
||||
import { key } from './wizard/store';
|
||||
@@ -53,4 +53,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Wizard title="Create an API key" steps={stepsComponents} on:finish={onFinish} />
|
||||
<WizardWithSteps title="Create an API key" steps={stepsComponents} on:finish={onFinish} />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user