fix: more screens

This commit is contained in:
Torsten Dittmann
2025-02-26 17:00:38 +04:00
parent 14cad5ec01
commit 1cd9e17cda
51 changed files with 911 additions and 1463 deletions
+2 -2
View File
@@ -21,9 +21,9 @@
"dependencies": {
"@appwrite.io/console": "https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@08ec153",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca7",
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca",
"@appwrite.io/pink-legacy": "^1.0.1",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@50ea3ca7",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
+7 -447
View File
@@ -15,14 +15,14 @@ importers:
specifier: 0.25.0
version: 0.25.0
'@appwrite.io/pink-icons-svelte':
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca7
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca7(svelte@4.2.19)
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca(svelte@4.2.19)
'@appwrite.io/pink-legacy':
specifier: ^1.0.1
version: 1.0.1
'@appwrite.io/pink-svelte':
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@50ea3ca7
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@50ea3ca7(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca
version: '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca(svelte@4.2.19)'
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -215,14 +215,8 @@ packages:
resolution: {tarball: https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@08ec153}
version: 1.2.1
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca7':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca7}
version: 1.0.0-next.7
peerDependencies:
svelte: ^4.0.0
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca70a483ffd9040f8ce9cb40a8d5f1ee30e':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca70a483ffd9040f8ce9cb40a8d5f1ee30e}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca}
version: 1.0.0-next.7
peerDependencies:
svelte: ^4.0.0
@@ -236,13 +230,6 @@ packages:
'@appwrite.io/pink-legacy@1.0.1':
resolution: {integrity: sha512-e5IZx2drqSxgl2WXvmr0eLfg/WiOCzuqQE8oksD+9GoFBkcL9HWvXul5GjFni3RBAN9XFRL3nUExGk4T0H8sFw==}
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@50ea3ca7':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@50ea3ca7}
version: 1.0.0-next.85
peerDependencies:
react-dom: ^18.0.0
svelte: ^4.0.0
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
@@ -1232,27 +1219,6 @@ packages:
resolution: {integrity: sha512-zIieP1VLWQb3wUjFJlwOAoaaJygJhXeUoGd0e/Ha2RLb2eW2S+4gjf6y6NqyY71tZ74LYVZKg/4prB6FAZSMXQ==}
engines: {node: '>= 14'}
'@shikijs/core@1.29.2':
resolution: {integrity: sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==}
'@shikijs/engine-javascript@1.29.2':
resolution: {integrity: sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==}
'@shikijs/engine-oniguruma@1.29.2':
resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==}
'@shikijs/langs@1.29.2':
resolution: {integrity: sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==}
'@shikijs/themes@1.29.2':
resolution: {integrity: sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==}
'@shikijs/types@1.29.2':
resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
'@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
@@ -1362,9 +1328,6 @@ 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==}
@@ -1374,9 +1337,6 @@ 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==}
@@ -1392,15 +1352,9 @@ packages:
'@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
'@types/pug@2.0.10':
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
'@types/react@18.3.18':
resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==}
'@types/remarkable@2.0.8':
resolution: {integrity: sha512-eKXqPZfpQl1kOADjdKchHrp2gwn9qMnGXhH/AtZe0UrklzhGJkawJo/Y/D0AlWcdWoWamFNIum8+/nkAISQVGg==}
@@ -1413,9 +1367,6 @@ packages:
'@types/tedious@4.0.14':
resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
'@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==}
@@ -1604,9 +1555,6 @@ 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'}
@@ -1741,9 +1689,6 @@ packages:
caniuse-lite@1.0.30001700:
resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
chai@4.5.0:
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
engines: {node: '>=4'}
@@ -1760,12 +1705,6 @@ 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==}
@@ -1809,9 +1748,6 @@ 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==}
@@ -1931,9 +1867,6 @@ 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}
@@ -1978,9 +1911,6 @@ packages:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
engines: {node: '>=12'}
emoji-regex-xs@1.0.0:
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -2184,9 +2114,6 @@ packages:
forwarded-parse@2.1.2:
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
framesync@6.1.2:
resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==}
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@@ -2206,10 +2133,6 @@ packages:
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
fuse.js@7.1.0:
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
engines: {node: '>=10'}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@@ -2308,15 +2231,6 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hast-util-to-html@9.0.4:
resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==}
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
hey-listen@1.0.8:
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
html-encoding-sniffer@3.0.0:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
engines: {node: '>=12'}
@@ -2324,9 +2238,6 @@ 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'}
@@ -2801,9 +2712,6 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
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==}
@@ -2814,21 +2722,6 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
micromark-util-character@2.1.1:
resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
micromark-util-encode@2.0.1:
resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
micromark-util-sanitize-uri@2.0.1:
resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
micromark-util-symbol@2.0.1:
resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
micromark-util-types@2.0.1:
resolution: {integrity: sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==}
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -2977,9 +2870,6 @@ packages:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
oniguruma-to-es@2.3.0:
resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==}
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -3101,9 +2991,6 @@ packages:
engines: {node: '>=18'}
hasBin: true
popmotion@11.0.5:
resolution: {integrity: sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==}
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -3195,9 +3082,6 @@ 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==}
@@ -3217,11 +3101,6 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
react-dom@18.3.1:
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
peerDependencies:
react: ^18.3.1
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
@@ -3251,15 +3130,6 @@ packages:
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
regex-recursion@5.1.1:
resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==}
regex-utilities@2.3.0:
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
regex@5.1.1:
resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
@@ -3349,9 +3219,6 @@ packages:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -3390,9 +3257,6 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
shiki@1.29.2:
resolution: {integrity: sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==}
shimmer@1.2.1:
resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
@@ -3466,9 +3330,6 @@ 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==}
@@ -3499,9 +3360,6 @@ 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'}
@@ -3529,9 +3387,6 @@ packages:
strip-literal@2.1.1:
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
style-value-types@5.1.2:
resolution: {integrity: sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -3577,11 +3432,6 @@ packages:
jest: '>= 27'
svelte: '>= 3'
svelte-motion@0.12.2:
resolution: {integrity: sha512-7RrdRz9iVP55B9HT/C0hYW3pyrKlF61kAby/AkDtOAP0uHFQDrfd0qQetDC81cEsK9b40jt+jfcqSAXcA7LPEw==}
peerDependencies:
svelte: '>=3.35.0 || ^4.0.0 || ^5.0.0 || ^5.0.0-next.0'
svelte-preprocess@5.1.4:
resolution: {integrity: sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==}
engines: {node: '>= 16.0.0'}
@@ -3660,11 +3510,6 @@ packages:
resolution: {integrity: sha512-DIFm0kSNscVxtBmKkBiygAHB5otoqN1aVmJ3t57jZhJfCB7Np/lUSoTtSrvPFjmlBbMeOsb1VQ06cut1+rBYOg==}
engines: {node: '>=16'}
svelte-sonner@0.3.28:
resolution: {integrity: sha512-K3AmlySeFifF/cKgsYNv5uXqMVNln0NBAacOYgmkQStLa/UoU0LhfAACU6Gr+YYC8bOCHdVmFNoKuDbMEsppJg==}
peerDependencies:
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
svelte@4.2.19:
resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==}
engines: {node: '>=16'}
@@ -3744,9 +3589,6 @@ 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.4.3:
resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
engines: {node: '>=16'}
@@ -3756,9 +3598,6 @@ packages:
tslib@2.3.0:
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
tslib@2.4.0:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
tslib@2.7.0:
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
@@ -3796,21 +3635,6 @@ packages:
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
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'}
@@ -3842,12 +3666,6 @@ 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.1:
resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -4051,9 +3869,6 @@ packages:
zrender@5.6.1:
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
snapshots:
'@adobe/css-tools@4.4.2': {}
@@ -4099,11 +3914,7 @@ snapshots:
'@appwrite.io/console@https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@08ec153': {}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca7(svelte@4.2.19)':
dependencies:
svelte: 4.2.19
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca70a483ffd9040f8ce9cb40a8d5f1ee30e(svelte@4.2.19)':
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@67808bca(svelte@4.2.19)':
dependencies:
svelte: 4.2.19
@@ -4116,21 +3927,6 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@50ea3ca7(react-dom@18.3.1(react@18.3.1))(svelte@4.2.19)':
dependencies:
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@50ea3ca70a483ffd9040f8ce9cb40a8d5f1ee30e(svelte@4.2.19)
'@floating-ui/dom': 1.6.13
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.3(svelte@4.2.19))(svelte@4.2.19)
'@melt-ui/svelte': 0.86.3(svelte@4.2.19)
ansicolor: 2.0.3
fuse.js: 7.1.0
pretty-bytes: 6.1.1
react-dom: 18.3.1(react@18.3.1)
shiki: 1.29.2
svelte: 4.2.19
svelte-motion: 0.12.2(svelte@4.2.19)
svelte-sonner: 0.3.28(svelte@4.2.19)
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
@@ -5259,41 +5055,6 @@ snapshots:
- encoding
- supports-color
'@shikijs/core@1.29.2':
dependencies:
'@shikijs/engine-javascript': 1.29.2
'@shikijs/engine-oniguruma': 1.29.2
'@shikijs/types': 1.29.2
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.4
'@shikijs/engine-javascript@1.29.2':
dependencies:
'@shikijs/types': 1.29.2
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 2.3.0
'@shikijs/engine-oniguruma@1.29.2':
dependencies:
'@shikijs/types': 1.29.2
'@shikijs/vscode-textmate': 10.0.2
'@shikijs/langs@1.29.2':
dependencies:
'@shikijs/types': 1.29.2
'@shikijs/themes@1.29.2':
dependencies:
'@shikijs/types': 1.29.2
'@shikijs/types@1.29.2':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
'@shikijs/vscode-textmate@10.0.2': {}
'@sinclair/typebox@0.27.8': {}
'@sinonjs/commons@3.0.1':
@@ -5428,10 +5189,6 @@ snapshots:
dependencies:
'@types/node': 22.13.4
'@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':
@@ -5442,10 +5199,6 @@ 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.13.4
@@ -5466,15 +5219,8 @@ snapshots:
'@types/prismjs@1.26.5': {}
'@types/prop-types@15.7.14': {}
'@types/pug@2.0.10': {}
'@types/react@18.3.18':
dependencies:
'@types/prop-types': 15.7.14
csstype: 3.1.3
'@types/remarkable@2.0.8': {}
'@types/shimmer@1.2.0': {}
@@ -5485,8 +5231,6 @@ snapshots:
dependencies:
'@types/node': 22.13.4
'@types/unist@3.0.3': {}
'@types/yargs-parser@21.0.3': {}
'@types/yargs@17.0.33':
@@ -5739,8 +5483,6 @@ snapshots:
ansi-styles@5.2.0: {}
ansicolor@2.0.3: {}
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
@@ -5897,8 +5639,6 @@ snapshots:
caniuse-lite@1.0.30001700: {}
ccount@2.0.1: {}
chai@4.5.0:
dependencies:
assertion-error: 1.1.0
@@ -5921,10 +5661,6 @@ 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
@@ -5977,8 +5713,6 @@ snapshots:
dependencies:
delayed-stream: 1.0.0
comma-separated-tokens@2.0.3: {}
concat-map@0.0.1: {}
confbox@0.1.8: {}
@@ -6097,10 +5831,6 @@ snapshots:
devalue@5.1.1: {}
devlop@1.1.0:
dependencies:
dequal: 2.0.3
diff-sequences@29.6.3: {}
dir-glob@3.0.1:
@@ -6138,8 +5868,6 @@ snapshots:
emittery@0.13.1: {}
emoji-regex-xs@1.0.0: {}
emoji-regex@8.0.0: {}
entities@4.5.0: {}
@@ -6420,10 +6148,6 @@ snapshots:
forwarded-parse@2.1.2: {}
framesync@6.1.2:
dependencies:
tslib: 2.4.0
fs.realpath@1.0.0: {}
fsevents@2.3.2:
@@ -6436,8 +6160,6 @@ snapshots:
functions-have-names@1.2.3: {}
fuse.js@7.1.0: {}
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
@@ -6535,34 +6257,12 @@ snapshots:
dependencies:
function-bind: 1.1.2
hast-util-to-html@9.0.4:
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
hey-listen@1.0.8: {}
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
@@ -7221,41 +6921,12 @@ snapshots:
math-intrinsics@1.1.0: {}
mdast-util-to-hast@13.2.0:
dependencies:
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
'@ungap/structured-clone': 1.3.0
devlop: 1.1.0
micromark-util-sanitize-uri: 2.0.1
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.1:
dependencies:
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.1
micromark-util-encode@2.0.1: {}
micromark-util-sanitize-uri@2.0.1:
dependencies:
micromark-util-character: 2.1.1
micromark-util-encode: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-symbol@2.0.1: {}
micromark-util-types@2.0.1: {}
micromatch@4.0.8:
dependencies:
braces: 3.0.3
@@ -7373,12 +7044,6 @@ snapshots:
dependencies:
mimic-fn: 4.0.0
oniguruma-to-es@2.3.0:
dependencies:
emoji-regex-xs: 1.0.0
regex: 5.1.1
regex-recursion: 5.1.1
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -7492,13 +7157,6 @@ snapshots:
optionalDependencies:
fsevents: 2.3.2
popmotion@11.0.5:
dependencies:
framesync: 6.1.2
hey-listen: 1.0.8
style-value-types: 5.1.2
tslib: 2.4.0
possible-typed-array-names@1.1.0: {}
postcss-load-config@3.1.4(postcss@8.5.2):
@@ -7569,8 +7227,6 @@ snapshots:
kleur: 3.0.3
sisteransi: 1.0.5
property-information@6.5.0: {}
proxy-from-env@1.1.0: {}
psl@1.15.0:
@@ -7585,12 +7241,6 @@ snapshots:
queue-microtask@1.2.3: {}
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
react: 18.3.1
scheduler: 0.23.2
react-is@17.0.2: {}
react-is@18.3.1: {}
@@ -7620,17 +7270,6 @@ snapshots:
regenerator-runtime@0.14.1: {}
regex-recursion@5.1.1:
dependencies:
regex: 5.1.1
regex-utilities: 2.3.0
regex-utilities@2.3.0: {}
regex@5.1.1:
dependencies:
regex-utilities: 2.3.0
regexp.prototype.flags@1.5.4:
dependencies:
call-bind: 1.0.8
@@ -7745,10 +7384,6 @@ snapshots:
dependencies:
xmlchars: 2.2.0
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
semver@6.3.1: {}
semver@7.7.1: {}
@@ -7783,17 +7418,6 @@ snapshots:
shebang-regex@3.0.0: {}
shiki@1.29.2:
dependencies:
'@shikijs/core': 1.29.2
'@shikijs/engine-javascript': 1.29.2
'@shikijs/engine-oniguruma': 1.29.2
'@shikijs/langs': 1.29.2
'@shikijs/themes': 1.29.2
'@shikijs/types': 1.29.2
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
shimmer@1.2.1: {}
side-channel-list@1.0.0:
@@ -7879,8 +7503,6 @@ 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):
@@ -7912,11 +7534,6 @@ 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
@@ -7937,11 +7554,6 @@ snapshots:
dependencies:
js-tokens: 9.0.1
style-value-types@5.1.2:
dependencies:
hey-listen: 1.0.8
tslib: 2.4.0
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@@ -7995,15 +7607,6 @@ snapshots:
jest: 29.7.0(@types/node@22.13.4)
svelte: 4.2.19
svelte-motion@0.12.2(svelte@4.2.19):
dependencies:
'@types/react': 18.3.18
framesync: 6.1.2
popmotion: 11.0.5
style-value-types: 5.1.2
svelte: 4.2.19
tslib: 2.8.1
svelte-preprocess@5.1.4(@babel/core@7.26.9)(postcss-load-config@3.1.4(postcss@8.5.2))(postcss@8.5.2)(sass@1.85.0)(svelte@4.2.19)(typescript@5.7.3):
dependencies:
'@types/pug': 2.0.10
@@ -8034,10 +7637,6 @@ snapshots:
svelte: 4.2.19
tslib: 2.7.0
svelte-sonner@0.3.28(svelte@4.2.19):
dependencies:
svelte: 4.2.19
svelte@4.2.19:
dependencies:
'@ampproject/remapping': 2.3.0
@@ -8122,16 +7721,12 @@ snapshots:
dependencies:
punycode: 2.3.1
trim-lines@3.0.1: {}
ts-api-utils@1.4.3(typescript@5.7.3):
dependencies:
typescript: 5.7.3
tslib@2.3.0: {}
tslib@2.4.0: {}
tslib@2.7.0: {}
tslib@2.8.1: {}
@@ -8154,29 +7749,6 @@ snapshots:
undici-types@6.20.0: {}
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:
@@ -8213,16 +7785,6 @@ 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.1(@types/node@22.13.4)(sass@1.85.0):
dependencies:
cac: 6.7.14
@@ -8414,5 +7976,3 @@ snapshots:
zrender@5.6.1:
dependencies:
tslib: 2.3.0
zwitch@2.0.4: {}
+1 -1
View File
@@ -99,7 +99,7 @@
return ['INPUT', 'TEXTAREA', 'SELECT'].includes((event.target as HTMLElement).tagName);
}
const handleKeydown = (e) => {
const handleKeydown = (e: KeyboardEvent) => {
if (!$subPanels.length) {
if (isInputEvent(e)) return;
keys = [...keys, e.key].slice(-10);
+1 -1
View File
@@ -34,7 +34,7 @@
export let radius: $$Props['radius'] = 'm';
export let variant: $$Props['variant'] = 'primary';
$: resolvedClasses = [!isTile && 'common-section', classes].filter(Boolean).join(' ');
$: resolvedClasses = [classes].filter(Boolean).join(' ');
</script>
{#if href}
+2
View File
@@ -30,11 +30,13 @@
});
}
}
//TODO: remove this component
</script>
<Tooltip disabled={tooltipDisabled}>
<span
data-private
style:display="inline-flex"
role="button"
tabindex="0"
style:cursor="pointer"
+32 -14
View File
@@ -1,8 +1,10 @@
<script lang="ts">
import { trackEvent } from '$lib/actions/analytics';
import { InnerModal } from '$lib/components';
import { InputId } from '$lib/elements/forms';
import { InputProjectId } from '$lib/elements/forms';
import Button from '$lib/elements/forms/button.svelte';
import { IconX } from '@appwrite.io/pink-icons-svelte';
import { Card, Divider, Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
export let show = false;
export let name: string;
export let id: string;
@@ -22,16 +24,32 @@
}
</script>
<InnerModal bind:show {fullWidth}>
<svelte:fragment slot="title">{name} ID</svelte:fragment>
<svelte:fragment slot="subtitle">
Enter a custom {name} ID. Leave blank for a randomly generated one.
</svelte:fragment>
<svelte:fragment slot="content">
{#if isProject}
<InputProjectId bind:value={id} {autofocus} />
{:else}
<InputId bind:value={id} {autofocus} />
{/if}
</svelte:fragment>
</InnerModal>
{#if show}
<Card.Base
variant="secondary"
padding="s"
--input-background-color="var(--color-bgcolor-neutral-primary)">
<Layout.Stack gap="xl">
<Layout.Stack gap="s">
<Layout.Stack direction="row" justifyContent="space-between" alignContent="center">
<Typography.Text variant="m-600">{name} ID</Typography.Text>
<Button extraCompact on:click={() => (show = false)}>
<Icon icon={IconX} size="s" />
</Button>
</Layout.Stack>
<Typography.Text>
Enter a custom {name} ID. Leave blank for a randomly generated one.
</Typography.Text>
</Layout.Stack>
<span
style="margin-left: calc(-1* var(--space-7));margin-right: calc(-1* var(--space-7));width:auto;">
<Divider />
</span>
{#if isProject}
<InputProjectId bind:value={id} {autofocus} />
{:else}
<InputId required bind:value={id} {autofocus} />
{/if}
</Layout.Stack>
</Card.Base>
{/if}
+18 -18
View File
@@ -1,30 +1,30 @@
<script>
import { getNextTier, tierToPlan } from '$lib/stores/billing';
import { organization } from '$lib/stores/organization';
import Card from './card.svelte';
import { Card, Layout, Typography } from '@appwrite.io/pink-svelte';
export let source = 'empty_state_card';
export let noAspectRatio = false;
</script>
<Card style="--card-padding:1.5rem; --card-padding-mobile: 2rem">
<div class="u-flex u-gap-24 u-flex-vertical-mobile">
<Card.Base variant="secondary" padding="s">
<Layout.Stack direction="row">
{#if $$slots?.image}
<div class="u-stretch">
<div
style:--p-file-preview-border-color="transparent"
class="file-preview is-full-cover-image u-width-full-line u-height-100-percent"
style={noAspectRatio ? 'aspect-ratio: auto' : ''}>
<slot name="image" />
</div>
<div
style:--p-file-preview-border-color="transparent"
class="file-preview is-full-cover-image u-width-full-line u-height-100-percent"
style={noAspectRatio ? 'aspect-ratio: auto' : ''}>
<slot name="image" />
</div>
{/if}
<div class="u-stretch u-flex-vertical u-main-center">
<h3 class="body-text-2 u-bold"><slot name="title" /></h3>
<p class="u-margin-block-start-8">
<slot nextTier={tierToPlan(getNextTier($organization.billingPlan)).name} />
</p>
<Layout.Stack justifyContent="center">
<Layout.Stack gap="xxs">
<Typography.Text variant="m-600"><slot name="title" /></Typography.Text>
<Typography.Text>
<slot nextTier={tierToPlan(getNextTier($organization.billingPlan)).name} />
</Typography.Text>
</Layout.Stack>
<slot name="cta" {source} />
</div>
</div>
</Card>
</Layout.Stack>
</Layout.Stack>
</Card.Base>
+52 -101
View File
@@ -4,8 +4,6 @@
import { Pill } from '$lib/elements';
import { createEventDispatcher } from 'svelte';
import { at, empty } from '$lib/helpers/array';
import Copy from './copy.svelte';
import { selectionStart } from '$lib/actions/selectionStart';
import { singular } from '$lib/helpers/string';
import {
eventServices as services,
@@ -13,8 +11,9 @@
type EventResource,
type EventService
} from '$lib/constants';
import { Tooltip } from '@appwrite.io/pink-svelte';
import { Tooltip, Input, Tag, Layout, Typography } from '@appwrite.io/pink-svelte';
import { trackEvent } from '$lib/actions/analytics';
import { IconCheck, IconDuplicate, IconPencil, IconX } from '@appwrite.io/pink-icons-svelte';
// Props
export let show = false;
@@ -185,11 +184,9 @@
action: null as EventAction | null,
attribute: null as string | null
};
let helper: string = null;
let customInput: string = null;
let customInputCursor = -1;
let showInput = false;
let copyParent: HTMLElement;
$: if (show && initialValue) {
customInput = initialValue;
@@ -235,71 +232,63 @@
$: if (!show) {
resetSelected();
helper = null;
customInput = null;
showInput = false;
}
$: if (showInput) {
helper = getCustomInputHelperStr(customInput, customInputCursor);
}
</script>
<Modal title="Create event" bind:show onSubmit={create} size="big">
<Modal title="Create event" bind:show onSubmit={create}>
<slot />
<div>
<p class="u-text">Choose a service</p>
<div class="u-flex u-gap-8 u-margin-block-start-8 u-flex-wrap">
<Layout.Stack gap="s">
<Typography.Text variant="m-500">Choose a service</Typography.Text>
<Layout.Stack gap="s" wrap="wrap" direction="row">
{#each available.services as service}
<Pill
<Tag
disabled={showInput}
selected={selected.service?.name === service.name}
button
on:click={() => select('service', service)}>
{service.name}
</Pill>
</Tag>
{/each}
</div>
</div>
</Layout.Stack>
</Layout.Stack>
{#if !empty(available.resources)}
<div>
<p class="u-text">Choose a resource (optional)</p>
<div class="u-flex u-gap-8 u-margin-block-start-8">
<Layout.Stack gap="s">
<Typography.Text variant="m-500">Choose a resource (optional)</Typography.Text>
<Layout.Stack gap="s" wrap="wrap" direction="row">
{#each available.resources as resource}
<Pill
<Tag
disabled={showInput}
selected={selected.resource?.name === resource.name}
button
on:click={() => select('resource', resource)}>
{resource.name}
</Pill>
</Tag>
{/each}
</div>
</div>
</Layout.Stack>
</Layout.Stack>
{/if}
{#if !empty(available.actions)}
<div>
<p class="u-text">Choose an action (optional)</p>
<div class="u-flex u-gap-8 u-margin-block-start-8">
<Layout.Stack gap="s">
<Typography.Text variant="m-500">Choose an action (optional)</Typography.Text>
<Layout.Stack gap="s" wrap="wrap" direction="row">
{#each available.actions as action}
<Pill
<Tag
disabled={showInput}
selected={selected.action?.name === action.name}
button
on:click={() => select('action', action)}>
{action.name}
</Pill>
</Tag>
{/each}
</div>
</div>
</Layout.Stack>
</Layout.Stack>
{/if}
{#if !empty(available.attributes)}
<div>
<p class="u-text">Choose an attribute (optional)</p>
<div class="u-flex u-gap-8 u-margin-block-start-8">
<Layout.Stack gap="s">
<Typography.Text variant="m-500">Choose an attribute (optional)</Typography.Text>
<Layout.Stack gap="s" wrap="wrap" direction="row">
{#each available.attributes as attribute}
<Pill
disabled={showInput}
@@ -309,73 +298,35 @@
{attribute}
</Pill>
{/each}
</div>
</div>
</Layout.Stack>
</Layout.Stack>
{/if}
{#if showInput}
<div class="input-text-wrapper" style="--amount-of-buttons:2">
<input
type="text"
placeholder="Enter custom event"
bind:value={customInput}
use:selectionStart={{ onChange: (newValue) => (customInputCursor = newValue) }} />
<div class="options-list">
<button on:click={toggleShowInput} class="options-list-button" aria-label="confirm">
<span class="icon-check" aria-hidden="true" />
</button>
<button on:click={toggleShowInput} class="options-list-button" aria-label="cancel">
<span class="icon-x" aria-hidden="true" />
</button>
</div>
<p style="height: 2rem;">{helper ?? ''}</p>
</div>
<Input.Text bind:value={customInput} placeholder="Enter custom event">
<svelte:fragment slot="end">
<Input.Action icon={IconCheck} on:click={toggleShowInput} />
<Input.Action icon={IconX} />
</svelte:fragment>
</Input.Text>
{:else}
<div class="input-text-wrapper" style="--amount-of-buttons:2" bind:this={copyParent}>
<!--
This object syntax avoids TS erroring because 'type' isn't a valid HTMLDivElement attribute
(we need to set it to 'text' to add styling)
-->
<div {...{ type: 'text' }} style="min-height: 2.5rem;">
{#each eventString as route, i}
<span
role="tooltip"
class:u-opacity-0-5={helper !== route.description}
on:mouseenter={() => (helper = route.description)}
on:mouseleave={() => (helper = null)}>
{route.value}
</span>
<span class="u-opacity-0-5">
{i + 1 < eventString?.length ? '.' : ''}
</span>
{/each}
</div>
<div class="options-list">
{#key copyParent}
<Tooltip>
<button
on:click={(e) => {
customInput = inputValue;
toggleShowInput(e);
}}
class="options-list-button"
aria-label="edit event">
<span class="icon-pencil" aria-hidden="true" />
</button>
<div slot="tooltip">Edit event</div>
</Tooltip>
<button
disabled={!inputValue}
class="options-list-button"
aria-label="copy text">
<Copy value={inputValue} tooltipDisabled={!inputValue}>
<span class="icon-duplicate" aria-hidden="true" />
</Copy>
</button>
{/key}
</div>
<p style="height: 2rem;">{helper ?? ''}</p>
</div>
<Input.Text
value={eventString.map((e) => e.value).join('.')}
placeholder="Enter custom event"
readonly>
<svelte:fragment slot="end">
<Tooltip>
<Input.Action
icon={IconPencil}
on:click={(e) => {
customInput = inputValue;
toggleShowInput(e);
}} />
<div slot="tooltip">Edit event</div>
</Tooltip>
<Input.Action icon={IconDuplicate} />
</svelte:fragment>
</Input.Text>
{/if}
<svelte:fragment slot="footer">
+1 -1
View File
@@ -25,7 +25,7 @@
});
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter' && submitOnEnter) {
if (show && event.key === 'Enter' && submitOnEnter) {
event.preventDefault();
if (show) {
formComponent.triggerSubmit();
+4 -3
View File
@@ -5,12 +5,13 @@
export let value: string;
export let copyEvent: string = null;
export let label: string = undefined;
</script>
<Input.Password {value}>
<div slot="end">
<Input.Password {value} {label} required>
<svelte:fragment slot="end">
<Copy {value} event={copyEvent} eventContext="click_secret_copy">
<Input.Action icon={IconDuplicate} />
</Copy>
</div>
</svelte:fragment>
</Input.Password>
+1
View File
@@ -100,6 +100,7 @@
<Tabs.Item.Button
{root}
bind:active={selected}
on:click
on:click={handleClick}
on:keydown={handleKeyDown}>
<slot />
+2 -1
View File
@@ -2,8 +2,9 @@
import { Tabs } from '@appwrite.io/pink-svelte';
export let variant: 'primary' | 'secondary' = 'primary';
export let stretch: boolean | undefined = undefined;
</script>
<Tabs.Root {variant} let:root>
<Tabs.Root {variant} {stretch} let:root>
<slot {root} />
</Tabs.Root>
+2
View File
@@ -3,6 +3,7 @@
export let value = '';
export let autofocus = true;
export let required = false;
let error = false;
const pattern = String.raw`^[a-zA-Z0-9][a-zA-Z0-9._\-]*$`;
@@ -24,6 +25,7 @@
<Input.Text
{pattern}
{autofocus}
{required}
id="id"
placeholder="Enter ID"
maxlength={36}
+2 -1
View File
@@ -14,6 +14,7 @@
export let min: number = null;
export let max: number = null;
export let step: number | 'any' = 1;
export let helper: string = undefined;
let error: string;
@@ -57,7 +58,7 @@
{nullable}
bind:value
autofocus={autofocus || undefined}
helper={error}
helper={error || helper}
state={error ? 'error' : 'default'}
on:invalid={handleInvalid}
on:input>
+21 -57
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import { SvelteComponent, onMount } from 'svelte';
import { Helper, Label } from '.';
import { Drop } from '$lib/components';
import { Input } from '@appwrite.io/pink-svelte';
export let label: string = undefined;
export let optionalText: string | undefined = undefined;
@@ -26,7 +25,6 @@
let element: HTMLInputElement;
let error: string;
let show = false;
onMount(() => {
if (element && autofocus) {
@@ -59,57 +57,23 @@
};
</script>
{#if label}
<Label {required} {hideRequired} {tooltip} {optionalText} hide={!showLabel} for={id}>
{label}{#if popover}
<Drop isPopover bind:show display="inline-block">
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
&nbsp;<button
type="button"
on:click={() => (show = !show)}
aria-label="input tooltip">
<span
class="icon-info"
aria-hidden="true"
style:font-size="var(--icon-size-small)" />
</button>
<svelte:fragment slot="list">
<div
class="dropped card u-max-width-250"
style:--p-card-padding=".75rem"
style:--card-border-radius="var(--border-radius-small)"
style:box-shadow="var(--shadow-large)">
<svelte:component this={popover} {...popoverProps} />
</div>
</svelte:fragment>
</Drop>
{/if}
</Label>
{/if}
<div class="input-text-wrapper">
<input
{id}
{name}
{placeholder}
{disabled}
{readonly}
{required}
{maxlength}
{pattern}
type="text"
autocomplete={autocomplete ? 'on' : 'off'}
class="input-text"
bind:value
bind:this={element}
on:invalid={handleInvalid} />
{#if $$slots.options}
<div class="options-list">
<slot name="options" />
</div>
{/if}
<slot />
</div>
{#if error}
<Helper type="warning" class="u-line-height-1">{error}</Helper>
{/if}
<Input.Text
{id}
{name}
{placeholder}
{disabled}
{required}
{maxlength}
{label}
{readonly}
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
helper={error}
state={error ? 'error' : 'default'}
on:invalid={handleInvalid}
on:input
bind:value>
<slot name="start" slot="start" />
<slot name="info" slot="info" />
<slot name="end" slot="end" />
</Input.Text>
+4 -1
View File
@@ -62,4 +62,7 @@
state={error ? 'error' : 'default'}
autofocus={autofocus || undefined}
autocomplete={autocomplete ? 'on' : 'off'}
bind:value />
bind:value>
<slot name="start" slot="start" />
<slot name="end" slot="end" />
</Input.Text>
+2 -1
View File
@@ -5,6 +5,7 @@
export let label: string;
export let value = false;
export let disabled = false;
export let description: string = undefined;
</script>
<Selector.Switch {id} {label} {disabled} bind:checked={value} on:invalid on:change />
<Selector.Switch {id} {description} {label} {disabled} bind:checked={value} on:invalid on:change />
+3 -7
View File
@@ -48,11 +48,7 @@
on:invalid={handleInvalid}
on:input
bind:value>
<svelte:fragment slot="info">
<slot name="info" slot="info" />
</svelte:fragment>
<svelte:fragment slot="end">
<slot name="end" />
</svelte:fragment>
<slot name="start" slot="start" />
<slot name="info" slot="info" />
<slot name="end" slot="end" />
</Input.Text>
+45 -33
View File
@@ -64,6 +64,9 @@
import { Card, SecondaryTabs, SecondaryTabsItem, Heading } from '$lib/components';
import { ProjectUsageRange, type Models } from '@appwrite.io/console';
import { page } from '$app/stores';
import { Layout, Tag, Typography } from '@appwrite.io/pink-svelte';
import { goto } from '$app/navigation';
import { InputSelect } from '$lib/elements/forms';
type MetricMetadata = {
title: string;
@@ -78,39 +81,48 @@
</script>
<Container>
<div class="u-flex u-main-space-between common-section">
<Heading tag="h2" size="5">{title}</Heading>
<SecondaryTabs>
<SecondaryTabsItem href={`${path}/24h`} disabled={$page.params.period === '24h'}>
24h
</SecondaryTabsItem>
<SecondaryTabsItem
href={`${path}/30d`}
disabled={!$page.params.period || $page.params.period === '30d'}>
30d
</SecondaryTabsItem>
<SecondaryTabsItem href={`${path}/90d`} disabled={$page.params.period === '90d'}>
90d
</SecondaryTabsItem>
</SecondaryTabs>
</div>
<Card>
{#if count}
<Heading tag="h6" size="6">{formatNumberWithCommas(total)}</Heading>
<p>{countMetadata.title}</p>
<div class="u-margin-block-start-16" />
<div class="chart-container">
<BarChart
formatted={$page.params.period === '24h' ? 'hours' : 'days'}
series={[
{
name: countMetadata.legend,
data: accumulateFromEndingTotal(count, total)
}
]} />
</div>
{/if}
</Card>
<Layout.Stack gap="s">
<div
style:max-width="250px"
style:--input-background-color="var(--color-bgcolor-neutral-primary)">
<InputSelect
on:change={(e) => goto(`${path}/${e.detail}`)}
id="period"
options={[
{
label: '24 hours',
value: '24h'
},
{
label: '30 days',
value: '30d'
},
{
label: '90 days',
value: '90d'
}
]}
value={$page.params.period ?? '30d'} />
</div>
<Card>
{#if count}
<Layout.Stack gap="xs">
<Typography.Title>{formatNumberWithCommas(total)}</Typography.Title>
<Typography.Text>{countMetadata.title}</Typography.Text>
</Layout.Stack>
<div class="chart-container">
<BarChart
formatted={$page.params.period === '24h' ? 'hours' : 'days'}
series={[
{
name: countMetadata.legend,
data: accumulateFromEndingTotal(count, total)
}
]} />
</div>
{/if}
</Card>
</Layout.Stack>
</Container>
<style lang="scss">
@@ -1,12 +1,13 @@
<script lang="ts">
import { page } from '$app/stores';
import { Alert, CopyInput, Modal } from '$lib/components';
import { Button, FormList, InputSwitch, InputText, InputTextarea } from '$lib/elements/forms';
import { CopyInput, Modal } from '$lib/components';
import { Button, InputSwitch, InputText, InputTextarea } from '$lib/elements/forms';
import { oAuthProviders, type Provider } from '$lib/stores/oauth-providers';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { updateOAuth } from '../updateOAuth';
import type { Models } from '@appwrite.io/console';
import { Link, Alert } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
@@ -41,35 +42,36 @@
$: secret = keyID && teamID && p8 ? JSON.stringify({ keyID, teamID, p8 }) : provider.secret;
</script>
<Modal {error} onSubmit={update} bind:show on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 settings</svelte:fragment>
<FormList>
<p>
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer">
visit the docs.
</a>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="servicesID"
label="Services ID"
autofocus={true}
placeholder="com.company.appname"
bind:value={appId} />
<InputText id="keyID" label="Key ID" placeholder="SHAB13ROFN" bind:value={keyID} />
<InputText id="teamID" label="Team ID" placeholder="ELA2CD3AED" bind:value={teamID} />
<InputTextarea id="p8" label="P8 File" placeholder="" bind:value={p8} />
<Alert type="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
</FormList>
<Modal {error} onSubmit={update} bind:show on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<Link.Anchor
class="link"
href={oAuthProvider?.docs}
target="_blank"
rel="noopener noreferrer">
visit the docs.
</Link.Anchor>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="servicesID"
label="Services ID"
autofocus={true}
placeholder="com.company.appname"
bind:value={appId} />
<InputText id="keyID" label="Key ID" placeholder="SHAB13ROFN" bind:value={keyID} />
<InputText id="teamID" label="Team ID" placeholder="ELA2CD3AED" bind:value={teamID} />
<InputTextarea id="p8" label="P8 File" placeholder="" bind:value={p8} />
<Alert.Inline status="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert.Inline>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
@@ -1,12 +1,13 @@
<script lang="ts">
import { page } from '$app/stores';
import { Alert, CopyInput, Modal } from '$lib/components';
import { Button, FormList, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { CopyInput, Modal } from '$lib/components';
import { Button, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { oAuthProviders, type Provider } from '$lib/stores/oauth-providers';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { updateOAuth } from '../updateOAuth';
import type { Models } from '@appwrite.io/console';
import { Link, Alert } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
@@ -43,43 +44,39 @@
: provider.secret;
</script>
<Modal {error} bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 settings</svelte:fragment>
<FormList>
<p>
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="clientID"
label="Client ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
placeholder="Enter Client Secret"
minlength={0}
showPasswordButton
bind:value={clientSecret} />
<InputText
id="domain"
label="Auth0 Domain"
placeholder="Your Auth0 domain"
bind:value={auth0Domain} />
<Alert type="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
</FormList>
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<svelte:fragment slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<Link.Anchor href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</Link.Anchor>
</svelte:fragment>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="clientID"
label="Client ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
placeholder="Enter Client Secret"
minlength={0}
bind:value={clientSecret} />
<InputText
id="domain"
label="Auth0 Domain"
placeholder="Your Auth0 domain"
bind:value={auth0Domain} />
<Alert.Inline status="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert.Inline>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
@@ -1,12 +1,13 @@
<script lang="ts">
import { page } from '$app/stores';
import { Alert, CopyInput, Modal } from '$lib/components';
import { Button, FormList, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { CopyInput, Modal } from '$lib/components';
import { Button, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { oAuthProviders, type Provider } from '$lib/stores/oauth-providers';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { updateOAuth } from '../updateOAuth';
import type { Models } from '@appwrite.io/console';
import { Link, Alert } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
@@ -43,43 +44,42 @@
: provider.secret;
</script>
<Modal {error} bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 settings</svelte:fragment>
<FormList>
<p>
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="clientID"
label="Client ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
placeholder="Enter Client Secret"
minlength={0}
showPasswordButton
bind:value={clientSecret} />
<InputText
id="domain"
label="Authentik Base-Domain"
placeholder="Your Authentik domain"
bind:value={authentikDomain} />
<Alert type="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
</FormList>
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<Link.Anchor
class="link"
href={oAuthProvider?.docs}
target="_blank"
rel="noopener noreferrer">visit the docs.</Link.Anchor>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="clientID"
label="Client ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="Client Secret"
placeholder="Enter Client Secret"
minlength={0}
bind:value={clientSecret} />
<InputText
id="domain"
label="Authentik Base-Domain"
placeholder="Your Authentik domain"
bind:value={authentikDomain} />
<Alert.Inline type="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert.Inline>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
@@ -1,12 +1,13 @@
<script lang="ts">
import { page } from '$app/stores';
import { Alert, CopyInput, Modal } from '$lib/components';
import { CopyInput, Modal } from '$lib/components';
import { Button, FormList, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { oAuthProviders, type Provider } from '$lib/stores/oauth-providers';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { updateOAuth } from '../updateOAuth';
import type { Models } from '@appwrite.io/console';
import { Link, Alert } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
@@ -41,43 +42,40 @@
clientSecret && endpoint ? JSON.stringify({ clientSecret, endpoint }) : provider.secret;
</script>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 settings</svelte:fragment>
<FormList>
<p>
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="App ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="App Secret"
placeholder="Enter App Secret"
minlength={0}
showPasswordButton
bind:value={clientSecret} />
<InputText
id="endpoint"
label="Endpoint (optional)"
placeholder="Your endpoint"
bind:value={endpoint} />
<Alert type="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
</FormList>
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<Link.Anchor href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</Link.Anchor>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="App ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="App Secret"
placeholder="Enter App Secret"
minlength={0}
showPasswordButton
bind:value={clientSecret} />
<InputText
id="endpoint"
label="Endpoint (optional)"
placeholder="Your endpoint"
bind:value={endpoint} />
<Alert.Inline status="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert.Inline>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
@@ -1,12 +1,13 @@
<script lang="ts">
import { page } from '$app/stores';
import { Alert, CopyInput, Modal } from '$lib/components';
import { Button, FormList, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { CopyInput, Modal } from '$lib/components';
import { Button, InputPassword, InputSwitch, InputText } from '$lib/elements/forms';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { updateOAuth } from '../updateOAuth';
import type { Models } from '@appwrite.io/console';
import { oAuthProviders, type Provider } from '$lib/stores/oauth-providers';
import { Link, Alert } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
@@ -38,38 +39,37 @@
};
</script>
<Modal {error} size="big" bind:show onSubmit={update} on:close>
<svelte:fragment slot="title">{provider.name} OAuth2 settings</svelte:fragment>
<FormList>
<p>
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<a class="link" href={oAuthProvider?.docs} target="_blank" rel="noopener noreferrer"
>visit the docs.</a>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="App ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="App Secret"
placeholder="Enter App Secret"
minlength={0}
showPasswordButton
bind:value={secret} />
<Alert type="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
</FormList>
<Modal {error} bind:show onSubmit={update} on:close title={`${provider.name} OAuth2 settings`}>
<p slot="description">
To use {provider.name} authentication in your application, first fill in this form. For more
info you can
<Link.Anchor
class="link"
href={oAuthProvider?.docs}
target="_blank"
rel="noopener noreferrer">visit the docs.</Link.Anchor>
</p>
<InputSwitch id="state" bind:value={enabled} label={enabled ? 'Enabled' : 'Disabled'} />
<InputText
id="appID"
label="App ID"
autofocus={true}
placeholder="Enter ID"
bind:value={appId} />
<InputPassword
id="secret"
label="App Secret"
placeholder="Enter App Secret"
minlength={0}
bind:value={secret} />
<Alert.Inline status="info">
To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration.
</Alert.Inline>
<div>
<p>URI</p>
<CopyInput
value={`${sdk.forConsole.client.config.endpoint}/account/sessions/oauth2/callback/${provider.key}/${projectId}`} />
</div>
<svelte:fragment slot="footer">
<Button secondary on:click={() => (provider = null)}>Cancel</Button>
<Button
@@ -6,6 +6,8 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { ID } from '@appwrite.io/console';
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
import { Badge, Icon, Tag } from '@appwrite.io/pink-svelte';
import { createEventDispatcher } from 'svelte';
export let showCreate = false;
@@ -42,25 +44,21 @@
</script>
<Modal title="Create team" {error} size="big" bind:show={showCreate} onSubmit={create}>
<FormList>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autofocus={true}
required
bind:value={name} />
{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}
><span class="icon-pencil" aria-hidden="true" />
<span class="text"> Team ID </span>
</Pill>
</div>
{:else}
<CustomId autofocus bind:show={showCustomId} name="Team" bind:id />
{/if}
</FormList>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autofocus={true}
required
bind:value={name} />
{#if !showCustomId}
<div>
<Tag size="s" on:click={() => (showCustomId = !showCustomId)}
><Icon icon={IconPencil} /> Team ID</Tag>
</div>
{:else}
<CustomId autofocus bind:show={showCustomId} name="Team" bind:id />
{/if}
<svelte:fragment slot="footer">
<Button secondary on:click={() => (showCreate = false)}>Cancel</Button>
<Button submit>Create</Button>
@@ -13,6 +13,8 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { ID } from '@appwrite.io/console';
import { IconPencil } from '@appwrite.io/pink-icons-svelte';
import { Icon, Tag } from '@appwrite.io/pink-svelte';
import { createEventDispatcher } from 'svelte';
export let showCreate = false;
@@ -72,20 +74,18 @@
id="password"
label="Password"
placeholder="Enter password"
showPasswordButton={true}
bind:value={pass} />
{#if !showDropdown}
<div>
<Pill button on:click={() => (showDropdown = !showDropdown)}>
<span class="icon-pencil" aria-hidden="true" /><span class="text">
User ID
</span>
</Pill>
<Tag
size="s"
on:click={() => {
showDropdown = true;
}}><Icon icon={IconPencil} /> User ID</Tag>
</div>
{:else}
<CustomId autofocus bind:show={showDropdown} name="User" bind:id />
{/if}
<CustomId bind:show={showDropdown} name="User" bind:id fullWidth={true} />
</FormList>
<svelte:fragment slot="footer">
<Button text on:mousedown={() => (showCreate = false)}>Cancel</Button>
@@ -17,8 +17,8 @@
import { app } from '$lib/stores/app';
import Empty from '$lib/components/empty.svelte';
import type { Models } from '@appwrite.io/console';
import { Icon, Tooltip } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Icon, Input, Layout, Link, Tooltip } from '@appwrite.io/pink-svelte';
import { IconPlus, IconRefresh, IconX } from '@appwrite.io/pink-icons-svelte';
let numbers: Models.MockNumber[] = $project?.authMockNumbers ?? [];
let initialNumbers = [];
@@ -85,12 +85,11 @@
<svelte:fragment slot="title">Mock phone numbers</svelte:fragment>
Generate <b>fictional</b> numbers to simulate phone verification when testing demo accounts
for submitting your application to the App Store or Google Play.
<a
<Link.Anchor
href="https://appwrite.io/docs/products/auth/security#mock-phone-numbers"
target="_blank"
class="u-underline"
rel="noopener noreferrer">
Learn more</a>
Learn more</Link.Anchor>
<svelte:fragment slot="aside">
{#if isComponentDisabled}
<EmptyCardImageCloud source="email_signature_card" noAspectRatio>
@@ -132,7 +131,6 @@
{emptyStateDescription}
<svelte:fragment let:source slot="cta">
<Button
class="u-margin-block-start-32"
secondary
fullWidth
external={isSelfHosted}
@@ -146,79 +144,65 @@
</svelte:fragment>
</EmptyCardImageCloud>
{:else if numbers?.length > 0}
<ul class="form-list u-gap-8">
{#each numbers as number, index}
{#each numbers as number, index}
<Layout.Stack direction="row" alignItems="flex-end">
<InputPhone
id={`key-${index}`}
bind:value={number.phone}
fullWidth
placeholder="Enter phone number"
label="Phone number"
showLabel={index === 0}
label={index === 0 ? 'Phone number' : undefined}
minlength={9}
maxlength={16}
required>
<button
slot="options"
on:click={() => (number.phone = generateNumber())}
class="options-list-button"
aria-label="regenerate text"
type="button">
<Tooltip>
<span class="icon-refresh" aria-hidden="true"></span>
<span slot="tooltip">Regenerate</span>
</Tooltip>
</button>
<Tooltip slot="end">
<Input.Action
icon={IconRefresh}
on:click={() => (number.phone = generateNumber())} />
<span slot="tooltip">Regenerate</span>
</Tooltip>
</InputPhone>
<InputOTP
id={`value-${index}`}
bind:value={number.otp}
fullWidth
placeholder="Enter value"
label="Verification code"
label={index === 0 ? 'Verification code' : undefined}
maxlength={6}
pattern={'^[0-9]{6}$'}
patternError="The value must contain 6 digits"
showLabel={index === 0}
required>
<button
slot="options"
on:click={() => (number.otp = generateOTP())}
class="options-list-button"
aria-label="regenerate text"
type="button">
<Tooltip
><span class="icon-refresh" aria-hidden="true"></span><span
slot="tooltip">Regenerate</span
></Tooltip>
</button>
<Tooltip slot="end">
<Input.Action
icon={IconRefresh}
on:click={() => (number.otp = generateOTP())} />
<span slot="tooltip">Regenerate</span>
</Tooltip>
</InputOTP>
<Button
text
compact
size="s"
disabled={numbers.length === 0}
class={'u-padding-4 ' + (index === 0 ? 'u-margin-block-start-24' : '')}
on:click={() => {
deletePhoneNumber(index);
}}>
<Tooltip
><span class="icon-refresh" aria-hidden="true"></span><span
slot="tooltip">Regenerate</span
></Tooltip>
<Icon icon={IconX} size="s" />
</Button>
{/each}
</ul>
</Layout.Stack>
{/each}
{#if numbers?.length < 10}
<Button
text
on:click={() =>
addPhoneNumber({
phone: generateNumber(),
otp: generateOTP()
})}
disabled={numbers.length >= 10}>
<Icon icon={IconPlus} slot="start" size="s" />
Add number
</Button>
<div>
<Button
secondary
on:click={() =>
addPhoneNumber({
phone: generateNumber(),
otp: generateOTP()
})}
disabled={numbers.length >= 10}>
<Icon icon={IconPlus} slot="start" size="s" />
Add number
</Button>
</div>
{/if}
{:else}
<Empty
@@ -39,6 +39,7 @@
<Form onSubmit={updatePasswordHistoryLimit}>
<CardGrid>
<svelte:fragment slot="title">Password history</svelte:fragment>
Set the maximum number of passwords saved per user.
<svelte:fragment slot="aside">
<InputSwitch
bind:value={passwordHistoryEnabled}
@@ -48,16 +49,15 @@
Enabling this option prevents users from reusing recent passwords by comparing the
new password with their password history.
</Typography.Text>
<Typography.Text>
Set the maximum number of passwords saved per user. Maximum 20 passwords.
</Typography.Text>
<InputNumber
required
max={20}
min={1}
id="password-history"
label="Limit"
disabled={!passwordHistoryEnabled}
bind:value={passwordHistory} />
bind:value={passwordHistory}
helper="Maximum 20 passwords." />
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -38,8 +38,8 @@
If you reduce the limit, users who are currently logged in will be logged out of the application.
<svelte:fragment slot="aside">
<Layout.Stack direction="row">
<InputNumber id="length" label="Length" bind:value={$value} min={0} />
<InputSelect id="period" label="Time period" bind:value={$unit} {options} />
<InputNumber required id="length" label="Length" bind:value={$value} min={0} />
<InputSelect required id="period" label="Time period" bind:value={$unit} {options} />
</Layout.Stack>
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -34,12 +34,16 @@
<Form onSubmit={updateSessionsLimit}>
<CardGrid>
<svelte:fragment slot="title">Sessions limit</svelte:fragment>
<Typography.Text>
<p>Maximum number of active sessions allowed per user.</p>
</Typography.Text>
<Typography.Text>Maximum number of active sessions allowed per user</Typography.Text>
<svelte:fragment slot="aside">
<InputNumber id="max-session" label="Limit" bind:value={maxSessions} />
<InputNumber
required
min={1}
max={100}
id="max-session"
label="Limit"
bind:value={maxSessions} />
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={maxSessions === $project?.authSessionsLimit} submit>Update</Button>
@@ -7,7 +7,7 @@
import { Button } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography, Layout, Selector, Input } from '@appwrite.io/pink-svelte';
import { Typography, Layout, Selector, Input, Badge } from '@appwrite.io/pink-svelte';
import { project } from '../../store';
import { tick } from 'svelte';
@@ -67,7 +67,7 @@
id="unlimited"
label="Unlimited"
value="unlimited" />
<Pill>recommended</Pill>
<Badge variant="secondary" content="Recommended" />
</Layout.Stack>
<Layout.Stack direction="row" alignItems="center">
<Selector.Radio
@@ -13,7 +13,7 @@
import type { Models } from '@appwrite.io/console';
import { project } from '../../store';
import { base } from '$app/paths';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import { Avatar, Badge, Card, Layout, Typography } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
let showProvider = false;
@@ -46,34 +46,34 @@
{#if $authMethods && $project}
<Container>
<CardGrid>
<svelte:fragment slot="title">Auth methods</svelte:fragment>
Enable the authentication methods you wish to use.
<svelte:fragment slot="aside">
<Layout.Stack gap="l" direction="row" wrap="wrap">
{#each $authMethods.list as box}
<div style="flex-basis: 45%;">
<InputSwitch
label={box.label}
id={box.method}
bind:value={box.value}
on:change={() => authUpdate(box)} />
</div>
{/each}
</Layout.Stack>
</svelte:fragment>
</CardGrid>
<section class="common-section">
<h2 class="heading-level-6 common-section">OAuth2 Providers</h2>
<ul class="grid-box common-section">
{#each $project.oAuthProviders
.filter((p) => p.name !== 'Mock')
.sort((a, b) => (a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1)) as provider}
{@const oAuthProvider = oAuthProviders[provider.key]}
{#if oAuthProvider}
<li class="grid-box-item">
<button
class="card u-flex u-flex-vertical u-cross-center u-width-full-line"
<Layout.Stack gap="xxl">
<CardGrid>
<svelte:fragment slot="title">Auth methods</svelte:fragment>
Enable the authentication methods you wish to use.
<svelte:fragment slot="aside">
<Layout.Stack gap="l" direction="row" wrap="wrap">
{#each $authMethods.list as box}
<div style="flex-basis: 45%;">
<InputSwitch
label={box.label}
id={box.method}
bind:value={box.value}
on:change={() => authUpdate(box)} />
</div>
{/each}
</Layout.Stack>
</svelte:fragment>
</CardGrid>
<Layout.Stack>
<Typography.Title size="s">OAuth2 Providers</Typography.Title>
<ul class="grid-box" style:--grid-gap="1rem" style:--grid-item-size="15rem">
{#each $project.oAuthProviders
.filter((p) => p.name !== 'Mock')
.sort( (a, b) => (a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1) ) as provider}
{@const oAuthProvider = oAuthProviders[provider.key]}
{#if oAuthProvider}
<Card.Button
padding="s"
on:click={() => {
selectedProvider = provider;
showProvider = true;
@@ -81,25 +81,31 @@
provider: provider.key.toLowerCase()
});
}}>
<div class="avatar">
<img
height="20"
width="20"
src={`${base}/icons/${$app.themeInUse}/color/${oAuthProvider.icon}.svg`}
alt={provider.name} />
</div>
<p class="u-margin-block-start-8">{provider.name}</p>
<div class="u-margin-block-start-24">
<Pill success={provider.enabled}>
{provider.enabled ? 'enabled' : 'disabled'}
</Pill>
</div>
</button>
</li>
{/if}
{/each}
</ul>
</section>
<Layout.Stack alignItems="flex-start" gap="xxl">
<Layout.Stack
direction="row"
justifyContent="flex-start"
alignItems="center">
<Avatar size="s">
<img
height="20"
width="20"
src={`${base}/icons/${$app.themeInUse}/color/${oAuthProvider.icon}.svg`}
alt={provider.name} />
</Avatar>
<Typography.Text>{provider.name}</Typography.Text>
</Layout.Stack>
<Badge
type={provider.enabled ? 'success' : undefined}
variant="secondary"
content={provider.enabled ? 'enabled' : 'disabled'} />
</Layout.Stack>
</Card.Button>
{/if}
{/each}
</ul>
</Layout.Stack>
</Layout.Stack>
</Container>
{/if}
@@ -39,14 +39,13 @@
<CardGrid>
<svelte:fragment slot="title">Name</svelte:fragment>
<svelte:fragment slot="aside">
<ul>
<InputText
id="name"
label="Name"
placeholder="Enter team name"
autocomplete={false}
bind:value={teamName} />
</ul>
<InputText
required
id="name"
label="Name"
placeholder="Enter team name"
autocomplete={false}
bind:value={teamName} />
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -33,7 +33,7 @@
<script lang="ts">
import { base } from '$app/paths';
import { Alert, CardGrid, Collapsible, CollapsibleItem, Heading } from '$lib/components';
import { CardGrid } from '$lib/components';
import { Container } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
@@ -59,7 +59,7 @@
EmailTemplateType,
EmailTemplateLocale
} from '@appwrite.io/console';
import { Accordion, Layout } from '@appwrite.io/pink-svelte';
import { Accordion, Layout, Alert, Tag, Badge, Link } from '@appwrite.io/pink-svelte';
export let data;
@@ -96,41 +96,29 @@
</script>
<Container>
<div class="u-flex u-gap-8 u-cross-center">
<Heading tag="h2" size="5">Templates</Heading>
<div class="tag eyebrow-heading-3">
<span class="text u-x-small">Experimental</span>
</div>
</div>
{#if !data.project.smtpEnabled}
<div class="u-margin-block-start-24">
<Alert
isStandalone
dismissible={false}
type="info"
buttons={[
{
slot: 'SMTP settings',
href: `${base}/project-${data.project.$id}/settings/smtp`
}
]}>
<svelte:fragment slot="title">
Custom SMTP server is required for customizing emails
</svelte:fragment>
Configure a custom SMTP server to enable custom email templates and prevent emails from
being labeled as spam.
</Alert>
</div>
<Alert.Inline
dismissible={false}
status="info"
title="Custom SMTP server is required for customizing emails">
Configure a custom SMTP server to enable custom email templates and prevent emails from
being labeled as spam.
<Button
compact
slot="actions"
href={`${base}/project-${data.project.$id}/settings/smtp`}>
SMTP settings
</Button>
</Alert.Inline>
{/if}
<CardGrid>
<svelte:fragment slot="title">Email templates</svelte:fragment>
<svelte:fragment slot="title"
>Email templates <Badge variant="secondary" content="Experimental" /></svelte:fragment>
Use templates to send and process account management emails.
<a href="https://appwrite.io/docs/advanced/platform/message-templates" class="link">
Learn more about email templates.
</a>
<Link.Anchor href="https://appwrite.io/docs/advanced/platform/message-templates">
Learn more
</Link.Anchor>
<svelte:fragment slot="aside">
<Layout.Stack>
<Accordion
@@ -1,20 +1,14 @@
<script lang="ts">
import { Box } from '$lib/components';
import { InputSelect, InputSelectSearch } from '$lib/elements/forms';
import { Input } from '@appwrite.io/pink-svelte';
import { localeCodes } from './store';
export let value = 'en';
let search = 'English';
$: options =
$localeCodes
?.map((code) => ({
label: code.name,
value: code.code
}))
?.filter((option) => option.label?.toLowerCase().startsWith(search?.toLowerCase())) ??
[];
const options = $localeCodes?.map((code) => ({
label: code.name,
value: code.code
}));
</script>
<Box radius="small" class="u-flex u-gap-16 u-cross-center">
@@ -22,13 +16,12 @@
<span class="icon-translate" />
<span class="text">Template language</span>
</div>
<InputSelect
<Input.Combobox
on:select
id="language"
placeholder="Select a language"
name="locale"
bind:value
bind:search
required
fullWidth
stretch={false}
@@ -22,7 +22,7 @@
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Icon } from '@appwrite.io/pink-svelte';
import { Icon, Link, Typography } from '@appwrite.io/pink-svelte';
const functionId = $page.params.function;
const eventSet: Writable<Set<string>> = writable(new Set($func.events));
@@ -160,12 +160,11 @@
</Form>
<EventModal bind:show={showEvents} initialValue={eventValue} on:created={handleEvent}>
<p class="text">
Select events in your Appwrite project that will trigger your function. <a
<Typography.Text
>Select events in your Appwrite project that will trigger your function<Link.Anchor
href="https://appwrite.io/docs/advanced/platform/events"
target="_blank"
rel="noopener noreferrer"
class="link">Learn more about Appwrite Events</a
>.
</p>
class="link">Learn more about Appwrite Events</Link.Anchor
>.</Typography.Text>
</EventModal>
@@ -65,7 +65,7 @@
{#if showCreate}
<EventModal bind:show={showCreate} on:created={handleCreated}>
<p class="text">
Select events in your Appwrite project that will trigger your function. <a
Select events in your Appwrite project that will trigger your function<a
href="https://appwrite.io/docs/advanced/platform/events"
target="_blank"
rel="noopener noreferrer"
@@ -1,17 +1,9 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { registerCommands, updateCommandGroupRanks } from '$lib/commandCenter';
import { Arrow, CardGrid, Heading } from '$lib/components';
import { Arrow, Avatar, AvatarGroup, CardGrid } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import {
TableBody,
TableCell,
TableCellHead,
TableHeader,
TableRow
} from '$lib/elements/table';
import Table from '$lib/elements/table/table.svelte';
import { isSameDay, toLocaleDate } from '$lib/helpers/date';
import { Container } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
@@ -21,11 +13,21 @@
import { openImportWizard } from './(import)';
import Details from './details.svelte';
import ExportModal from './exportModal.svelte';
import Status from '$lib/components/status.svelte';
import { capitalize } from '$lib/helpers/string';
import { readOnly } from '$lib/stores/billing';
import type { Models } from '@appwrite.io/console';
import { canWriteProjects } from '$lib/stores/roles';
import {
IconCloud,
IconDatabase,
IconDownload,
IconFolder,
IconLightningBolt,
IconServer,
IconUpload,
IconUserGroup
} from '@appwrite.io/pink-icons-svelte';
import { Icon, Layout, Link, Status, Table } from '@appwrite.io/pink-svelte';
import { capitalize } from '$lib/helpers/string';
export let data;
let migration: Models.Migration = null;
@@ -55,7 +57,7 @@
$: $registerCommands([
{
label: 'Import data',
icon: 'download',
icon: IconDownload,
keys: ['i', 'd'],
callback: openImportWizard,
group: 'migrations',
@@ -64,14 +66,14 @@
isSelfHosted
? {
label: 'Deploy to cloud',
icon: 'cloud',
icon: IconCloud,
keys: ['d', 'c'],
callback: deployToCloud,
group: 'migrations'
}
: {
label: 'Export data',
icon: 'upload',
icon: IconUpload,
keys: ['e', 'd'],
callback: () => (showExport = true),
group: 'migrations'
@@ -136,13 +138,11 @@
<CardGrid>
<svelte:fragment slot="title">Import project data</svelte:fragment>
Import data from another platform or from a different Appwrite instance.
<a
class="link"
<Link.Anchor
href="https://appwrite.io/docs/advanced/migrations"
target="_blank"
rel="noopener noreferrer">
Learn about which platforms are supported</a
>.
Learn more</Link.Anchor>
<svelte:fragment slot="aside">
{#if data.migrations.length}
<div class="u-flex">
@@ -151,79 +151,56 @@
</div>
</div>
<Table noStyles>
<TableHeader>
<TableCellHead>Date</TableCellHead>
<TableCellHead>Source</TableCellHead>
<TableCellHead>Status</TableCellHead>
<TableCellHead />
</TableHeader>
<TableBody>
{#each data.migrations as entry}
<TableRow>
{@const status = getStatus(entry.status)}
<TableCell title="Data">
{isSameDay(new Date(), new Date(entry.$createdAt))
? 'Today'
: toLocaleDate(entry.$createdAt)}
</TableCell>
<TableCell title="Source">{entry.source}</TableCell>
<TableCell title="Status">
<Status {status}>
{capitalize(status)}
</Status>
</TableCell>
<TableCell title="" showOverflow>
<div class="u-flex u-main-end">
<Button secondary on:click={() => showDetails(entry)}
>Details</Button>
</div>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
<Table.Root>
<svelte:fragment slot="header">
<Table.Header.Cell>Date</Table.Header.Cell>
<Table.Header.Cell>Source</Table.Header.Cell>
<Table.Header.Cell>Status</Table.Header.Cell>
<Table.Header.Cell />
</svelte:fragment>
{#each data.migrations as entry}
<Table.Row>
{@const status = getStatus(entry.status)}
<Table.Cell>
{isSameDay(new Date(), new Date(entry.$createdAt))
? 'Today'
: toLocaleDate(entry.$createdAt)}
</Table.Cell>
<Table.Cell>{entry.source}</Table.Cell>
<Table.Cell>
<Status label={capitalize(status)} {status} />
</Table.Cell>
<Table.Cell>
<div class="u-flex u-main-end">
<Button secondary on:click={() => showDetails(entry)}
>Details</Button>
</div>
</Table.Cell>
</Table.Row>
{/each}
</Table.Root>
{:else}
<div class="import-box">
<ul class="avatars-group is-with-border">
<li class="avatars-group-item">
<div class="avatar">
<i class="icon-user-group" />
</div>
</li>
<li class="avatars-group-item">
<div class="avatar">
<i class="icon-database" />
</div>
</li>
<li class="avatars-group-item">
<div class="avatar">
<i class="icon-lightning-bolt" />
</div>
</li>
<li class="avatars-group-item">
<div class="avatar">
<i class="icon-folder" />
</div>
</li>
</ul>
<div class="u-margin-block-start-8">
<Layout.Stack alignItems="center" gap="xl">
<Layout.Stack alignItems="center">
<AvatarGroup
--icon-fill="var(--color-fgcolor-neutral-tertiary)"
icons={[IconUserGroup, IconDatabase, IconLightningBolt, IconFolder]}
size="s" />
<Arrow direction="down" />
</div>
<div class="avatar u-margin-block-start-8" style="--size: {48 / 16}rem">
<span class="icon-cloud" />
</div>
<Avatar alt="appwrite" size="s">
<Icon
icon={IconCloud}
size="s"
color="--color-fgcolor-neutral-tertiary" />
</Avatar>
</Layout.Stack>
<Button
class="u-margin-block-start-20"
secondary
on:click={openImportWizard}
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}>
Import data
</Button>
</div>
</Layout.Stack>
{/if}
</svelte:fragment>
</CardGrid>
@@ -231,55 +208,65 @@
<CardGrid>
<svelte:fragment slot="title">Deploy to Cloud</svelte:fragment>
Export data from your project to Appwrite Cloud.
<a
class="link"
<Link.Anchor
href="https://appwrite.io/docs/advanced/migrations/self-hosted"
target="_blank"
rel="noopener noreferrer">
Learn more in our documentation.</a>
Learn more</Link.Anchor>
<svelte:fragment slot="aside">
<div class="import-box">
<div class="u-flex u-cross-center u-gap-8">
<div class="avatar" style="--size: {48 / 16}rem">
<span class="icon-server" />
</div>
<Layout.Stack alignItems="center" gap="xl">
<Layout.Stack direction="row" justifyContent="center" alignItems="center">
<Avatar alt="appwrite" size="s">
<Icon
icon={IconServer}
size="s"
color="--color-fgcolor-neutral-tertiary" />
</Avatar>
<Arrow direction="right" />
<div class="avatar" style="--size: {48 / 16}rem">
<span class="icon-cloud" />
</div>
<Avatar alt="appwrite" size="s">
<Icon
icon={IconCloud}
size="s"
color="--color-fgcolor-neutral-tertiary" />
</Avatar>
</Layout.Stack>
<div>
<Button secondary on:click={deployToCloud}>Deploy to Cloud</Button>
</div>
<Button class="u-margin-block-start-48" secondary on:click={deployToCloud}>
Deploy to Cloud
</Button>
</div>
</Layout.Stack>
</svelte:fragment>
</CardGrid>
{:else}
<CardGrid>
<svelte:fragment slot="title">Export to self-hosted instance</svelte:fragment>
Export data from your project to a self-hosted instance.
<a
<Link.Anchor
class="link"
href="https://appwrite.io/docs/advanced/migrations/self-hosted"
target="_blank"
rel="noopener noreferrer">
Learn more in our documentation.</a>
Learn more</Link.Anchor>
<svelte:fragment slot="aside">
<div class="import-box">
<div class="u-flex u-cross-center u-gap-8">
<div class="avatar" style="--size: {48 / 16}rem">
<span class="icon-cloud" />
</div>
<Layout.Stack alignItems="center" gap="xl">
<Layout.Stack direction="row" justifyContent="center" alignItems="center">
<Avatar alt="appwrite" size="s">
<Icon
icon={IconCloud}
size="s"
color="--color-fgcolor-neutral-tertiary" />
</Avatar>
<Arrow direction="right" />
<div class="avatar" style="--size: {48 / 16}rem">
<span class="icon-server" />
</div>
<Avatar alt="appwrite" size="s">
<Icon
icon={IconServer}
size="s"
color="--color-fgcolor-neutral-tertiary" />
</Avatar>
</Layout.Stack>
<div>
<Button secondary on:click={() => (showExport = true)}>Export data</Button>
</div>
<Button
class="u-margin-block-start-48"
secondary
on:click={() => (showExport = true)}>Export data</Button>
</div>
</Layout.Stack>
</svelte:fragment>
</CardGrid>
{/if}
@@ -287,18 +274,6 @@
<ExportModal bind:show={showExport} />
<Details {migration} bind:show={showMigration} />
<style lang="scss">
.import-box {
display: grid;
place-items: center;
border: 1px dashed hsl(var(--color-border));
border-radius: 1rem;
height: 100%;
padding: 1.5rem;
}
</style>
{#if showMigration}
<Details {migration} bind:show={showMigration} />
{/if}
@@ -1,5 +1,5 @@
<script lang="ts">
import { Alert, Code, Modal, Tab } from '$lib/components';
import { Alert, Modal, Tab } from '$lib/components';
import Tabs from '$lib/components/tabs.svelte';
import { total } from '$lib/helpers/array';
import { toLocaleDateTime } from '$lib/helpers/date';
@@ -7,6 +7,7 @@
import { formatNum } from '$lib/helpers/string';
import type { Models } from '@appwrite.io/console';
import { ResourcesFriendly } from '$lib/stores/migration';
import { Card, Layout, Typography, Code } from '@appwrite.io/pink-svelte';
export let migration: Models.Migration = null;
export let show = false;
@@ -45,28 +46,34 @@
let tab = 'details' as 'details' | 'logs';
</script>
<Modal bind:show>
<svelte:fragment slot="title">
{#if migration.status === 'failed'}
Resolve migration issues
{:else}
Migration details
{/if}
</svelte:fragment>
<Tabs>
<Tab selected={tab === 'details'} on:click={() => (tab = 'details')}>Details</Tab>
<Tab selected={tab === 'logs'} on:click={() => (tab = 'logs')}>Logs</Tab>
<Modal
bind:show
title={migration.status === 'failed' ? 'Resolve migration issues' : 'Migration details'}
hideFooter>
<Tabs stretch let:root>
<Tab {root} selected={tab === 'details'} on:click={() => (tab = 'details')}>Details</Tab>
<Tab {root} selected={tab === 'logs'} on:click={() => (tab = 'logs')}>Logs</Tab>
</Tabs>
{#if tab === 'logs'}
<Code code={JSON.stringify(migration, null, 2)} language="json" allowScroll />
<Code code={JSON.stringify(migration, null, 2)} lang="json" hideHeader />
{:else if tab === 'details'}
<div class="box meta">
<span>Date</span>
<span>{toLocaleDateTime(migration.$createdAt)}</span>
<span>Source</span>
<span>{migration.source}</span>
</div>
<Card.Base variant="secondary" padding="s">
<Layout.Stack>
<Layout.Stack direction="row">
<span style:flex-basis="50%">
<Typography.Text variant="m-600">Date</Typography.Text>
</span>
<span>{toLocaleDateTime(migration.$createdAt)}</span>
</Layout.Stack>
<Layout.Stack direction="row">
<span style:flex-basis="50%">
<Typography.Text variant="m-600">Source</Typography.Text>
</span>
<span>{migration.source}</span>
</Layout.Stack>
</Layout.Stack>
</Card.Base>
{#if Object.values(statusCounters).some(hasError)}
<Alert
@@ -1,13 +1,6 @@
<script lang="ts">
import { Alert, CardGrid, Heading } from '$lib/components';
import {
Button,
Form,
FormList,
InputText,
InputChoice,
InputEmail
} from '$lib/elements/forms';
import { CardGrid } from '$lib/components';
import { Button, Form, InputText, InputEmail } from '$lib/elements/forms';
import { Container } from '$lib/layout';
import { project } from '../../store';
import InputPassword from '$lib/elements/forms/inputPassword.svelte';
@@ -24,6 +17,7 @@
import { type SMTPSecure } from '@appwrite.io/console';
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
import { upgradeURL } from '$lib/stores/billing';
import { Link, Selector, Alert } from '@appwrite.io/pink-svelte';
let enabled = false;
let senderName: string;
@@ -126,76 +120,71 @@
<CardGrid>
<svelte:fragment slot="title">SMTP server</svelte:fragment>
You can customize the email service by providing your own SMTP server. View your email templates
<a href={`${base}/project-${$project.$id}/auth/templates`} class="link">here</a>
<Link.Anchor href={`${base}/project-${$project.$id}/auth/templates`}>here</Link.Anchor>
<svelte:fragment slot="aside">
{#if $organization.billingPlan === BillingPlan.FREE}
<Alert type="info">
Custom SMTP is a Pro plan feature. Upgrade to enable custom SMTP sever.
<svelte:fragment slot="action">
<div class="alert-buttons u-flex">
<Button text href={$upgradeURL}>Upgrade plan</Button>
</div>
<Alert.Inline
status="info"
title="Custom SMTP is a Pro plan feature. Upgrade to enable custom SMTP sever.">
<svelte:fragment slot="actions">
<Button compact href={$upgradeURL}>Upgrade plan</Button>
</svelte:fragment>
</Alert>
</Alert.Inline>
{:else}
<FormList>
<InputChoice
type="switchbox"
id="enabled"
bind:value={enabled}
label="Custom SMTP server">
Enabling this option allows customizing email templates and prevents
emails from being labeled as spam.
</InputChoice>
<Selector.Switch
id="enabled"
bind:checked={enabled}
label="Custom SMTP server"
description="Enabling this option allows customizing email templates and prevents emails
from being labeled as spam." />
{#if enabled}
<InputText
id="senderName"
label="Sender name"
bind:value={senderName}
required
placeholder="Enter sender name" />
<InputEmail
id="senderEmail"
label="Sender email"
bind:value={senderEmail}
required
placeholder="user@example.io" />
<InputEmail
id="replyTo"
label="Reply to"
bind:value={replyTo}
placeholder="user@example.io" />
<InputText
id="serverHost"
label="Server host"
bind:value={host}
required
placeholder="smtp.server.com" />
<InputNumber
id="serverPort"
label="Server port"
bind:value={port}
required
placeholder="587" />
<InputText
id="username"
label="Username"
bind:value={username}
placeholder="Enter username" />
<InputPassword
id="passwort"
label="Password"
bind:value={password}
placeholder="Enter password" />
<InputSelect
id="tls"
label="Secure protocol"
placeholder="Select protocol"
bind:value={secure}
{options} />
{/if}
</FormList>
{#if enabled}
<InputText
id="senderName"
label="Sender name"
bind:value={senderName}
required
placeholder="Enter sender name" />
<InputEmail
id="senderEmail"
label="Sender email"
bind:value={senderEmail}
required
placeholder="user@example.io" />
<InputEmail
id="replyTo"
label="Reply to"
bind:value={replyTo}
placeholder="user@example.io" />
<InputText
id="serverHost"
label="Server host"
bind:value={host}
required
placeholder="smtp.server.com" />
<InputNumber
id="serverPort"
label="Server port"
bind:value={port}
required
placeholder="587" />
<InputText
id="username"
label="Username"
bind:value={username}
placeholder="Enter username" />
<InputPassword
id="passwort"
label="Password"
bind:value={password}
placeholder="Enter password" />
<InputSelect
id="tls"
label="Secure protocol"
placeholder="Select protocol"
bind:value={secure}
{options} />
{/if}
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -33,9 +33,10 @@
import MessageStatusPill from './messageStatusPill.svelte';
import { canWriteWebhooks } from '$lib/stores/roles';
import { get } from 'svelte/store';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import { Badge, Icon, Layout, Link, Status, Table } from '@appwrite.io/pink-svelte';
import ViewSelector from '$lib/components/viewSelector.svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { View } from '$lib/helpers/load';
export let data: PageData;
@@ -60,74 +61,75 @@
<Container>
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
<ViewSelector {columns} view={data.view} hideView />
<ViewSelector {columns} view={View.Table} hideView />
{#if $canWriteWebhooks}
<Button on:click={openWizard} event="create_webhook">
<Icon icon={IconPlus} slot="start" size="s" />
Create database
Create webhook
</Button>
{/if}
</Layout.Stack>
{#if data.webhooks.total}
<TableScroll>
<TableHeader>
<Table.Root>
<svelte:fragment slot="header">
{#each $columns as column}
{#if column.show}
<TableCellHead width={column.width}>{column.title}</TableCellHead>
<Table.Header.Cell width={column.width + 'px'}
>{column.title}</Table.Header.Cell>
{/if}
{/each}
</TableHeader>
<TableBody service="webhooks" total={data.webhooks.total}>
{#each data.webhooks.webhooks as webhook}
<TableRowLink
href={`${base}/project-${projectId}/settings/webhooks/${webhook.$id}`}>
{#each $columns as column (column.id)}
{#if column.show}
{#if column.id === '$id'}
{#key $columns}
<TableCell title={column.title} width={column.width}>
<Id value={webhook.$id}>{webhook.$id}</Id>
</TableCell>
{/key}
{:else if column.id === 'events'}
<TableCellText title={column.title} width={column.width}>
{webhook.events.length}
</TableCellText>
{:else if column.type === 'datetime'}
<TableCellText title={column.title} width={column.width}>
{#if !webhook[column.id]}
-
{:else}
{toLocaleDateTime(webhook[column.id])}
{/if}
</TableCellText>
{:else if column.id === 'enabled'}
<TableCellText title={column.title} width={column.width}>
<MessageStatusPill enabled={webhook.enabled} />
&nbsp;
</svelte:fragment>
{#each data.webhooks.webhooks as webhook}
<Table.Link href={`${base}/project-${projectId}/settings/webhooks/${webhook.$id}`}>
{#each $columns as column (column.id)}
{#if column.show}
{#if column.id === '$id'}
{#key $columns}
<Table.Cell>
<Id value={webhook.$id}>{webhook.$id}</Id>
</Table.Cell>
{/key}
{:else if column.id === 'events'}
<Table.Cell>
{webhook.events.length}
</Table.Cell>
{:else if column.type === 'datetime'}
<Table.Cell>
{#if !webhook[column.id]}
-
{:else}
{toLocaleDateTime(webhook[column.id])}
{/if}
</Table.Cell>
{:else if column.id === 'enabled'}
<Table.Cell>
<Layout.Stack direction="row" gap="s" alignItems="normal">
<Status
label={webhook.enabled ? 'Enabled' : 'Disabled'}
status={webhook.enabled ? 'complete' : 'failed'} />
{#if webhook.enabled === false}
<Button
<Link.Button
variant="muted"
on:click={(e) => {
e.preventDefault();
selectedWebhook = webhook;
showFailed = true;
}}>Details</Button>
}}>Details</Link.Button>
{/if}
</TableCellText>
{:else}
<TableCellText title={column.title} width={column.width}>
{webhook[column.id]}
</TableCellText>
{/if}
</Layout.Stack>
</Table.Cell>
{:else}
<Table.Cell>
{webhook[column.id]}
</Table.Cell>
{/if}
{/each}
</TableRowLink>
{/each}
</TableBody>
</TableScroll>
{/if}
{/each}
</Table.Link>
{/each}
</Table.Root>
{:else}
<Empty
single
@@ -1,17 +1,15 @@
import { Dependencies } from '$lib/constants';
import { View, getPage, getView } from '$lib/helpers/load';
import { getPage } from '$lib/helpers/load';
import { sdk } from '$lib/stores/sdk';
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params, url, route, depends }) => {
export const load: PageLoad = async ({ params, url, depends }) => {
depends(Dependencies.WEBHOOKS);
const page = getPage(url);
const view = getView(url, route, View.Grid);
return {
page,
view,
webhooks: await sdk.forConsole.projects.listWebhooks(params.project)
};
};
@@ -1,9 +1,10 @@
<script lang="ts">
import { BoxAvatar, CardGrid, Heading } from '$lib/components';
import { CardGrid } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { webhook } from './store';
import Delete from './delete.svelte';
import { toLocaleDateTime } from '$lib/helpers/date';
import { Card, Typography } from '@appwrite.io/pink-svelte';
let showDelete = false;
</script>
@@ -12,12 +13,10 @@
<svelte:fragment slot="title">Delete webhooks</svelte:fragment>
The webhook will be permanently deleted. This action is irreversible.
<svelte:fragment slot="aside">
<BoxAvatar>
<svelte:fragment slot="title">
<h6 class="u-bold u-trim-1">{$webhook.name}</h6>
</svelte:fragment>
<p>Last updated: {toLocaleDateTime($webhook.$updatedAt)}</p>
</BoxAvatar>
<Card.Base variant="secondary" padding="s">
<Typography.Text variant="m-600">{$webhook.name}</Typography.Text>
<Typography.Text>Last updated: {toLocaleDateTime($webhook.$updatedAt)}</Typography.Text>
</Card.Base>
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -6,6 +6,7 @@
import { Dependencies } from '$lib/constants';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Typography } from '@appwrite.io/pink-svelte';
import { project } from '../../../store';
import { webhook } from './store';
@@ -30,5 +31,7 @@
</script>
<Confirm onSubmit={handleDelete} title="Delete webhook" bind:open={showDelete} bind:error>
Are you sure you want to delete <b>{$webhook.name}</b> from '{$project.name}'?
<Typography.Text>
Are you sure you want to delete <b>{$webhook.name}</b> from '{$project.name}'?
</Typography.Text>
</Confirm>
@@ -14,6 +14,7 @@
import { onMount } from 'svelte';
import MessageStatusPill from '../messageStatusPill.svelte';
import FormList from '$lib/elements/forms/formList.svelte';
import { Status } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
@@ -55,7 +56,7 @@
</script>
<CardGrid>
<svelte:fragment slot="title">{$webhook.url}</svelte:fragment>
<svelte:fragment slot="title">{$webhook.name}</svelte:fragment>
<svelte:fragment slot="aside">
<div class="u-flex u-gap-16">
<ul class="u-stretch">
@@ -69,7 +70,6 @@
<li style="margin-top:16px">Created: {toLocaleDateTime($webhook.$createdAt)}</li>
<li>Last updated: {toLocaleDateTime($webhook.$updatedAt)}</li>
</ul>
<MessageStatusPill enabled={$webhook.enabled} />
</div>
</svelte:fragment>
@@ -3,6 +3,7 @@
import { page } from '$app/stores';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { Modal } from '$lib/components';
import Confirm from '$lib/components/confirm.svelte';
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
@@ -32,13 +33,7 @@
}
</script>
<Modal title="Regenerate Key" bind:show onSubmit={regenerate}>
<p class="u-text">
Are you sure you want to generate a new Signature Key?
<b>You will not be able to recover the current key.</b>
</p>
<svelte:fragment slot="footer">
<Button text on:click={() => (show = false)}>Cancel</Button>
<Button secondary submit>Regenerate</Button>
</svelte:fragment>
</Modal>
<Confirm title="Regenerate Key" bind:open={show} onSubmit={regenerate} action="Regenerate">
Are you sure you want to generate a new Signature key?
<b>You will not be able to recover the current key.</b>
</Confirm>
@@ -12,8 +12,8 @@
import { onMount } from 'svelte';
import { writable, type Writable } from 'svelte/store';
import { webhook } from './store';
import { Icon } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Icon, Layout, Link, Table, Typography } from '@appwrite.io/pink-svelte';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
const projectId = $page.params.project;
const eventSet: Writable<Set<string>> = writable(new Set());
@@ -73,28 +73,31 @@
Set the events that will trigger your webhook. Maximum 100 events allowed.
<svelte:fragment slot="aside">
{#if $eventSet.size}
<TableList>
<Table.Root>
{#each Array.from($eventSet) as event}
<li class="table-row">
<TableCellText title="id">
{event}
</TableCellText>
<TableCell showOverflow title="options" width={40}>
<button
class="button is-text is-only-icon"
aria-label="delete id"
on:click|preventDefault={() => {
$eventSet.delete(event);
eventSet.set($eventSet);
}}>
<span class="icon-x" aria-hidden="true" />
</button>
</TableCell>
</li>
<Table.Row>
<Table.Cell>
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center">
{event}
<Button
extraCompact
ariaLabel="delete event"
on:click={() => {
$eventSet.delete(event);
eventSet.set($eventSet);
}}>
<Icon icon={IconX} size="s" />
</Button>
</Layout.Stack>
</Table.Cell>
</Table.Row>
{/each}
</TableList>
<div class="u-flex u-margin-block-start-16">
<Button text on:click={() => (showCreateEvent = true)}>
</Table.Root>
<div>
<Button secondary on:click={() => (showCreateEvent = true)}>
<Icon icon={IconPlus} slot="start" size="s" />
Add event
</Button>
@@ -111,12 +114,11 @@
</Form>
<EventModal bind:show={showCreateEvent} on:created={handleEvent}>
<p class="text">
Select events in your Appwrite project that will trigger your webhook. <a
<Typography.Text>
Select events in your Appwrite project that will trigger your webhook. <Link.Anchor
href="https://appwrite.io/docs/advanced/platform/events"
target="_blank"
rel="noopener noreferrer"
class="link">Learn more about Appwrite Events</a
>.
</p>
class="link">Learn more</Link.Anchor>
</Typography.Text>
</EventModal>
@@ -16,6 +16,7 @@
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { webhook } from './store';
import { Selector } from '@appwrite.io/pink-svelte';
const projectId = $page.params.project;
let httpUser: string = null;
@@ -63,37 +64,31 @@
Set an optional basic HTTP authentication username and password to protect your endpoint from
unauthorized access.
<svelte:fragment slot="aside">
<FormList>
<div>
<Heading tag="h3" size="7">HTTP Authentication</Heading>
<p class="text">Use to secure your endpoint from untrusted sources.</p>
</div>
<InputText
label="User"
id="user"
placeholder="Enter username"
bind:value={httpUser} />
<InputPassword
label="Password"
id="password"
minlength={0}
placeholder="Enter password"
bind:value={httpPass} />
<div>
<Heading tag="h3" size="7">HTTP Authentication</Heading>
<p class="text">Use to secure your endpoint from untrusted sources.</p>
</div>
<InputText label="User" id="user" placeholder="Enter username" bind:value={httpUser} />
<InputPassword
label="Password"
id="password"
minlength={0}
placeholder="Enter password"
bind:value={httpPass} />
<InputChoice
id="Security"
label="Certificate verification (SSL/TLS)"
bind:value={security}>
<span class="u-color-text-danger">Warning:</span> Untrusted or self-signed
certificates may not be secure.
<a
href="https://appwrite.io/docs/advanced/self-hosting/tls-certificates"
target="_blank"
rel="noopener noreferrer"
class="link">
Learn more</a>
</InputChoice>
</FormList>
<Selector.Checkbox
id="security"
label="Certificate verification (SSL/TLS)"
bind:checked={security}
description="Placeholder" />
<!-- <span class="u-color-text-danger">Warning:</span> Untrusted or self-signed certificates
may not be secure.
<a
href="https://appwrite.io/docs/advanced/self-hosting/tls-certificates"
target="_blank"
rel="noopener noreferrer"
class="link">
Learn more</a> -->
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -1,6 +1,7 @@
<script lang="ts">
import { CardGrid, Heading, Secret } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Link } from '@appwrite.io/pink-svelte';
import Regenerate from './regenerate.svelte';
import { webhook } from './store';
@@ -10,19 +11,18 @@
<CardGrid>
<svelte:fragment slot="title">Signature key</svelte:fragment>
Add the Signature Key to the X-Appwrite-Webhook-Signature header to validate your webhooks.
<a
<Link.Anchor
href="https://appwrite.io/docs/advanced/platform/webhooks#verification"
target="_blank"
rel="noopener noreferrer"
class="link">Learn more about webhook validation.</a>
class="link">Learn more</Link.Anchor>
<svelte:fragment slot="aside">
<div>
<p>Key</p>
<Secret copyEvent="signature" bind:value={$webhook.signatureKey} />
<Secret label="Key" copyEvent="signature" bind:value={$webhook.signatureKey} />
</div>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button on:click={() => (showRegenerate = true)} secondary submit>Regenerate Key</Button>
<Button on:click={() => (showRegenerate = true)} secondary submit>Regenerate key</Button>
</svelte:fragment>
</CardGrid>
@@ -7,11 +7,11 @@ import { writable } from 'svelte/store';
// export const showCreate = writable(false);
export const columns = writable<Column[]>([
{ id: '$id', title: 'Webhook ID', type: 'string', show: true, width: 150 },
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
{ id: 'url', title: 'Post URL', type: 'string', show: true, width: 120 },
{ id: 'events', title: 'Events', type: 'string', show: true, width: 60 },
{ id: 'enabled', title: 'Status', type: 'string', show: true, width: 150 },
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 120 },
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 120 }
{ id: '$id', title: 'Webhook ID', type: 'string', show: false, width: 200 },
{ id: 'name', title: 'Name', type: 'string', show: true, width: 200 },
{ id: 'url', title: 'Post URL', type: 'string', show: true, width: 200 },
{ id: 'events', title: 'Events', type: 'string', show: true, width: 120 },
{ id: 'enabled', title: 'Status', type: 'string', show: true, width: 180 },
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 200 },
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 200 }
]);
@@ -5,8 +5,8 @@
import { createWebhook } from './store';
import { EventModal } from '$lib/components';
import { TableList, TableCellText, TableCell } from '$lib/elements/table';
import { Icon } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Icon, Typography, Link, Table, Layout } from '@appwrite.io/pink-svelte';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
let showCreate = false;
@@ -25,28 +25,31 @@
</svelte:fragment>
{#if $createWebhook?.events?.length}
<TableList>
{#each $createWebhook.events as event}
<li class="table-row">
<TableCellText title="id">
{event}
</TableCellText>
<TableCell showOverflow title="options" width={40}>
<button
class="button is-text is-only-icon"
aria-label="delete id"
on:click|preventDefault={() => {
eventSet.delete(event);
$createWebhook.events = Array.from(eventSet);
}}>
<span class="icon-x" aria-hidden="true" />
</button>
</TableCell>
</li>
{/each}
</TableList>
<div class="u-flex u-margin-block-start-16">
<Button text on:click={() => (showCreate = !showCreate)}>
{#each $createWebhook.events as event}
<Table.Root>
<Table.Row>
<Table.Cell>
<Layout.Stack
direction="row"
justifyContent="space-between"
alignItems="center">
{event}
<Button
extraCompact
ariaLabel="delete event"
on:click={() => {
eventSet.delete(event);
$createWebhook.events = Array.from(eventSet);
}}>
<Icon icon={IconX} size="s" />
</Button>
</Layout.Stack>
</Table.Cell>
</Table.Row>
</Table.Root>
{/each}
<div>
<Button secondary on:click={() => (showCreate = !showCreate)}>
<Icon icon={IconPlus} slot="start" size="s" />
Add event
</Button>
@@ -58,13 +61,12 @@
{#if showCreate}
<EventModal bind:show={showCreate} on:created={handleCreated}>
<p class="text">
Select events in your Appwrite project that will trigger your webhook. <a
<Typography.Text>
Select events in your Appwrite project that will trigger your webhook. <Link.Anchor
href="https://appwrite.io/docs/advanced/platform/events"
target="_blank"
rel="noopener noreferrer"
class="link">Learn more about Appwrite Events</a
>.
</p>
class="link">Learn more</Link.Anchor>
</Typography.Text>
</EventModal>
{/if}