diff --git a/.prettierignore b/.prettierignore index 7d74fe2..4e78dfa 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ # Package Managers +src/lib/components/ui/ package-lock.json pnpm-lock.yaml yarn.lock diff --git a/.prettierrc b/.prettierrc index 8855237..8103a0b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,10 +3,7 @@ "singleQuote": true, "trailingComma": "none", "printWidth": 100, - "plugins": [ - "prettier-plugin-svelte", - "prettier-plugin-tailwindcss" - ], + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], "overrides": [ { "files": "*.svelte", diff --git a/README.md b/README.md index 75842c4..c55f28e 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,23 @@ -# sv +# Minix - Front +Este repositorio consiste del repo que contiene el codigo para poder hacer deploy de una instancia de minix -Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). +# ¿Que es Minix? +Intentamos hacer algo parecido a x.com pero adaptado a nuestra vision. -## Creating a project +# Galeria + + + + + + + + + -If you're seeing this, you've probably already done this step. Congrats! +# ¿Que tecnologias usamos? +- svelte(kit) (framework) +- shadcn-svelte (ui) +- firebase/auth +- vercel (host) -```sh -# create a new project in the current directory -npx sv create - -# create a new project in my-app -npx sv create my-app -``` - -## Developing - -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: - -```sh -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open -``` - -## Building - -To create a production version of your app: - -```sh -npm run build -``` - -You can preview the production build with `npm run preview`. - -> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/bun.lock b/bun.lock index 3eeabd4..c2d60a1 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,9 @@ "": { "name": "mini-x-front", "dependencies": { + "@firebase/auth": "^1.12.0", "@resvg/resvg-js": "^2.6.2", + "firebase": "^12.8.0", "mode-watcher": "^1.1.0", "satori": "^0.18.3", }, @@ -85,12 +87,104 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@firebase/ai": ["@firebase/ai@2.7.0", "", { "dependencies": { "@firebase/app-check-interop-types": "0.3.3", "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x", "@firebase/app-types": "0.x" } }, "sha512-PwpCz+TtAMWICM7uQNO0mkSPpUKwrMV4NSwHkbVKDvPKoaQmSlO96vIz+Suw2Ao1EaUUsxYb5LGImHWt/fSnRQ=="], + + "@firebase/analytics": ["@firebase/analytics@0.10.19", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/installations": "0.6.19", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-3wU676fh60gaiVYQEEXsbGS4HbF2XsiBphyvvqDbtC1U4/dO4coshbYktcCHq+HFaGIK07iHOh4pME0hEq1fcg=="], + + "@firebase/analytics-compat": ["@firebase/analytics-compat@0.2.25", "", { "dependencies": { "@firebase/analytics": "0.10.19", "@firebase/analytics-types": "0.8.3", "@firebase/component": "0.7.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-fdzoaG0BEKbqksRDhmf4JoyZf16Wosrl0Y7tbZtJyVDOOwziE0vrFjmZuTdviL0yhak+Nco6rMsUUbkbD+qb6Q=="], + + "@firebase/analytics-types": ["@firebase/analytics-types@0.8.3", "", {}, "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg=="], + + "@firebase/app": ["@firebase/app@0.14.7", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "idb": "7.1.1", "tslib": "^2.1.0" } }, "sha512-o3ZfnOx0AWBD5n/36p2zPoB0rDDxQP8H/A60zDLvvfRLtW8b3LfCyV97GKpJaAVV1JMMl/BC89EDzMyzxFZxTw=="], + + "@firebase/app-check": ["@firebase/app-check@0.11.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-XAvALQayUMBJo58U/rxW02IhsesaxxfWVmVkauZvGEz3vOAjMEQnzFlyblqkc2iAaO82uJ2ZVyZv9XzPfxjJ6w=="], + + "@firebase/app-check-compat": ["@firebase/app-check-compat@0.4.0", "", { "dependencies": { "@firebase/app-check": "0.11.0", "@firebase/app-check-types": "0.5.3", "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-UfK2Q8RJNjYM/8MFORltZRG9lJj11k0nW84rrffiKvcJxLf1jf6IEjCIkCamykHE73C6BwqhVfhIBs69GXQV0g=="], + + "@firebase/app-check-interop-types": ["@firebase/app-check-interop-types@0.3.3", "", {}, "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A=="], + + "@firebase/app-check-types": ["@firebase/app-check-types@0.5.3", "", {}, "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng=="], + + "@firebase/app-compat": ["@firebase/app-compat@0.5.7", "", { "dependencies": { "@firebase/app": "0.14.7", "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" } }, "sha512-MO+jfap8IBZQ+K8L2QCiHObyMgpYHrxo4Hc7iJgfb9hjGRW/z1y6LWVdT9wBBK+VJ7cRP2DjAiWQP+thu53hHA=="], + + "@firebase/app-types": ["@firebase/app-types@0.9.3", "", {}, "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw=="], + + "@firebase/auth": ["@firebase/auth@1.12.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x", "@react-native-async-storage/async-storage": "^2.2.0" }, "optionalPeers": ["@react-native-async-storage/async-storage"] }, "sha512-zkvLpsrxynWHk07qGrUDfCSqKf4AvfZGEqJ7mVCtYGjNNDbGE71k0Yn84rg8QEZu4hQw1BC0qDEHzpNVBcSVmA=="], + + "@firebase/auth-compat": ["@firebase/auth-compat@0.6.2", "", { "dependencies": { "@firebase/auth": "1.12.0", "@firebase/auth-types": "0.13.0", "@firebase/component": "0.7.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-8UhCzF6pav9bw/eXA8Zy1QAKssPRYEYXaWagie1ewLTwHkXv6bKp/j6/IwzSYQP67sy/BMFXIFaCCsoXzFLr7A=="], + + "@firebase/auth-interop-types": ["@firebase/auth-interop-types@0.2.4", "", {}, "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA=="], + + "@firebase/auth-types": ["@firebase/auth-types@0.13.0", "", { "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg=="], + + "@firebase/component": ["@firebase/component@0.7.0", "", { "dependencies": { "@firebase/util": "1.13.0", "tslib": "^2.1.0" } }, "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg=="], + + "@firebase/data-connect": ["@firebase/data-connect@0.3.12", "", { "dependencies": { "@firebase/auth-interop-types": "0.2.4", "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-baPddcoNLj/+vYo+HSJidJUdr5W4OkhT109c5qhR8T1dJoZcyJpkv/dFpYlw/VJ3dV66vI8GHQFrmAZw/xUS4g=="], + + "@firebase/database": ["@firebase/database@1.1.0", "", { "dependencies": { "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "faye-websocket": "0.11.4", "tslib": "^2.1.0" } }, "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg=="], + + "@firebase/database-compat": ["@firebase/database-compat@2.1.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/database": "1.1.0", "@firebase/database-types": "1.0.16", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" } }, "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg=="], + + "@firebase/database-types": ["@firebase/database-types@1.0.16", "", { "dependencies": { "@firebase/app-types": "0.9.3", "@firebase/util": "1.13.0" } }, "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw=="], + + "@firebase/firestore": ["@firebase/firestore@4.10.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "@firebase/webchannel-wrapper": "1.0.5", "@grpc/grpc-js": "~1.9.0", "@grpc/proto-loader": "^0.7.8", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-fgF6EbpoagGWh5Vwfu/7/jYgBFwUCwTlPNVF/aSjHcoEDRXpRsIqVfAFTp1LD+dWAUcAKEK3h+osk8spMJXtxA=="], + + "@firebase/firestore-compat": ["@firebase/firestore-compat@0.4.4", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/firestore": "4.10.0", "@firebase/firestore-types": "3.0.3", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-JvxxIgi+D5v9BecjLA1YomdyF7LA6CXhJuVK10b4GtRrB3m2O2hT1jJWbKYZYHUAjTaajkvnos+4U5VNxqkI2w=="], + + "@firebase/firestore-types": ["@firebase/firestore-types@3.0.3", "", { "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q=="], + + "@firebase/functions": ["@firebase/functions@0.13.1", "", { "dependencies": { "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", "@firebase/component": "0.7.0", "@firebase/messaging-interop-types": "0.2.3", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-sUeWSb0rw5T+6wuV2o9XNmh9yHxjFI9zVGFnjFi+n7drTEWpl7ZTz1nROgGrSu472r+LAaj+2YaSicD4R8wfbw=="], + + "@firebase/functions-compat": ["@firebase/functions-compat@0.4.1", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/functions": "0.13.1", "@firebase/functions-types": "0.6.3", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-AxxUBXKuPrWaVNQ8o1cG1GaCAtXT8a0eaTDfqgS5VsRYLAR0ALcfqDLwo/QyijZj1w8Qf8n3Qrfy/+Im245hOQ=="], + + "@firebase/functions-types": ["@firebase/functions-types@0.6.3", "", {}, "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg=="], + + "@firebase/installations": ["@firebase/installations@0.6.19", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/util": "1.13.0", "idb": "7.1.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-nGDmiwKLI1lerhwfwSHvMR9RZuIH5/8E3kgUWnVRqqL7kGVSktjLTWEMva7oh5yxQ3zXfIlIwJwMcaM5bK5j8Q=="], + + "@firebase/installations-compat": ["@firebase/installations-compat@0.2.19", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/installations": "0.6.19", "@firebase/installations-types": "0.5.3", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-khfzIY3EI5LePePo7vT19/VEIH1E3iYsHknI/6ek9T8QCozAZshWT9CjlwOzZrKvTHMeNcbpo/VSOSIWDSjWdQ=="], + + "@firebase/installations-types": ["@firebase/installations-types@0.5.3", "", { "peerDependencies": { "@firebase/app-types": "0.x" } }, "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA=="], + + "@firebase/logger": ["@firebase/logger@0.5.0", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g=="], + + "@firebase/messaging": ["@firebase/messaging@0.12.23", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/installations": "0.6.19", "@firebase/messaging-interop-types": "0.2.3", "@firebase/util": "1.13.0", "idb": "7.1.1", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-cfuzv47XxqW4HH/OcR5rM+AlQd1xL/VhuaeW/wzMW1LFrsFcTn0GND/hak1vkQc2th8UisBcrkVcQAnOnKwYxg=="], + + "@firebase/messaging-compat": ["@firebase/messaging-compat@0.2.23", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/messaging": "0.12.23", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-SN857v/kBUvlQ9X/UjAqBoQ2FEaL1ZozpnmL1ByTe57iXkmnVVFm9KqAsTfmf+OEwWI4kJJe9NObtN/w22lUgg=="], + + "@firebase/messaging-interop-types": ["@firebase/messaging-interop-types@0.2.3", "", {}, "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q=="], + + "@firebase/performance": ["@firebase/performance@0.7.9", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/installations": "0.6.19", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0", "web-vitals": "^4.2.4" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-UzybENl1EdM2I1sjYm74xGt/0JzRnU/0VmfMAKo2LSpHJzaj77FCLZXmYQ4oOuE+Pxtt8Wy2BVJEENiZkaZAzQ=="], + + "@firebase/performance-compat": ["@firebase/performance-compat@0.2.22", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/performance": "0.7.9", "@firebase/performance-types": "0.2.3", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-xLKxaSAl/FVi10wDX/CHIYEUP13jXUjinL+UaNXT9ByIvxII5Ne5150mx6IgM8G6Q3V+sPiw9C8/kygkyHUVxg=="], + + "@firebase/performance-types": ["@firebase/performance-types@0.2.3", "", {}, "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ=="], + + "@firebase/remote-config": ["@firebase/remote-config@0.8.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/installations": "0.6.19", "@firebase/logger": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-sJz7C2VACeE257Z/3kY9Ap2WXbFsgsDLfaGfZmmToKAK39ipXxFan+vzB9CSbF6mP7bzjyzEnqPcMXhAnYE6fQ=="], + + "@firebase/remote-config-compat": ["@firebase/remote-config-compat@0.2.21", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", "@firebase/remote-config": "0.8.0", "@firebase/remote-config-types": "0.5.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-9+lm0eUycxbu8GO25JfJe4s6R2xlDqlVt0CR6CvN9E6B4AFArEV4qfLoDVRgIEB7nHKwvH2nYRocPWfmjRQTnw=="], + + "@firebase/remote-config-types": ["@firebase/remote-config-types@0.5.0", "", {}, "sha512-vI3bqLoF14L/GchtgayMiFpZJF+Ao3uR8WCde0XpYNkSokDpAKca2DxvcfeZv7lZUqkUwQPL2wD83d3vQ4vvrg=="], + + "@firebase/storage": ["@firebase/storage@0.14.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app": "0.x" } }, "sha512-xWWbb15o6/pWEw8H01UQ1dC5U3rf8QTAzOChYyCpafV6Xki7KVp3Yaw2nSklUwHEziSWE9KoZJS7iYeyqWnYFA=="], + + "@firebase/storage-compat": ["@firebase/storage-compat@0.4.0", "", { "dependencies": { "@firebase/component": "0.7.0", "@firebase/storage": "0.14.0", "@firebase/storage-types": "0.8.3", "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "peerDependencies": { "@firebase/app-compat": "0.x" } }, "sha512-vDzhgGczr1OfcOy285YAPur5pWDEvD67w4thyeCUh6Ys0izN9fNYtA1MJERmNBfqjqu0lg0FM5GLbw0Il21M+g=="], + + "@firebase/storage-types": ["@firebase/storage-types@0.8.3", "", { "peerDependencies": { "@firebase/app-types": "0.x", "@firebase/util": "1.x" } }, "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg=="], + + "@firebase/util": ["@firebase/util@1.13.0", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ=="], + + "@firebase/webchannel-wrapper": ["@firebase/webchannel-wrapper@1.0.5", "", {}, "sha512-+uGNN7rkfn41HLO0vekTFhTxk61eKa8mTpRGLO0QSqlQdKvIoGAvLp3ppdVIWbTGYJWM6Kp0iN+PjMIOcnVqTw=="], + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + "@grpc/grpc-js": ["@grpc/grpc-js@1.9.15", "", { "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ=="], + + "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + "@internationalized/date": ["@internationalized/date@3.10.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw=="], "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], @@ -115,6 +209,26 @@ "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + "@resvg/resvg-js": ["@resvg/resvg-js@2.6.2", "", { "optionalDependencies": { "@resvg/resvg-js-android-arm-eabi": "2.6.2", "@resvg/resvg-js-android-arm64": "2.6.2", "@resvg/resvg-js-darwin-arm64": "2.6.2", "@resvg/resvg-js-darwin-x64": "2.6.2", "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", "@resvg/resvg-js-linux-arm64-musl": "2.6.2", "@resvg/resvg-js-linux-x64-gnu": "2.6.2", "@resvg/resvg-js-linux-x64-musl": "2.6.2", "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", "@resvg/resvg-js-win32-x64-msvc": "2.6.2" } }, "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q=="], "@resvg/resvg-js-android-arm-eabi": ["@resvg/resvg-js-android-arm-eabi@2.6.2", "", { "os": "android", "cpu": "arm" }, "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA=="], @@ -275,6 +389,8 @@ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -309,7 +425,7 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "emoji-regex-xs": ["emoji-regex-xs@2.0.1", "", {}, "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g=="], @@ -317,6 +433,8 @@ "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], @@ -325,24 +443,34 @@ "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "faye-websocket": ["faye-websocket@0.11.4", "", { "dependencies": { "websocket-driver": ">=0.5.1" } }, "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fflate": ["fflate@0.7.4", "", {}, "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw=="], "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "firebase": ["firebase@12.8.0", "", { "dependencies": { "@firebase/ai": "2.7.0", "@firebase/analytics": "0.10.19", "@firebase/analytics-compat": "0.2.25", "@firebase/app": "0.14.7", "@firebase/app-check": "0.11.0", "@firebase/app-check-compat": "0.4.0", "@firebase/app-compat": "0.5.7", "@firebase/app-types": "0.9.3", "@firebase/auth": "1.12.0", "@firebase/auth-compat": "0.6.2", "@firebase/data-connect": "0.3.12", "@firebase/database": "1.1.0", "@firebase/database-compat": "2.1.0", "@firebase/firestore": "4.10.0", "@firebase/firestore-compat": "0.4.4", "@firebase/functions": "0.13.1", "@firebase/functions-compat": "0.4.1", "@firebase/installations": "0.6.19", "@firebase/installations-compat": "0.2.19", "@firebase/messaging": "0.12.23", "@firebase/messaging-compat": "0.2.23", "@firebase/performance": "0.7.9", "@firebase/performance-compat": "0.2.22", "@firebase/remote-config": "0.8.0", "@firebase/remote-config-compat": "0.2.21", "@firebase/storage": "0.14.0", "@firebase/storage-compat": "0.4.0", "@firebase/util": "1.13.0" } }, "sha512-S1tCIR3ENecee0tY2cfTHfMkXqkitHfbsvqpCtvsT0Zi9vDB7A4CodAjHfHCjVvu/XtGy1LHLjOasVcF10rCVw=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "hex-rgb": ["hex-rgb@4.3.0", "", {}, "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw=="], + "http-parser-js": ["http-parser-js@0.5.10", "", {}, "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], + "inline-style-parser": ["inline-style-parser@0.2.6", "", {}, "sha512-gtGXVaBdl5mAes3rPcMedEBm12ibjt1kDMFfheul1wUAOVEJW60voNdMVzVkfLN06O7ZaD/rxhfKgtlgtTbMjg=="], "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], @@ -385,6 +513,10 @@ "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -437,8 +569,12 @@ "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="], + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "rollup": ["rollup@4.53.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.2", "@rollup/rollup-android-arm64": "4.53.2", "@rollup/rollup-darwin-arm64": "4.53.2", "@rollup/rollup-darwin-x64": "4.53.2", "@rollup/rollup-freebsd-arm64": "4.53.2", "@rollup/rollup-freebsd-x64": "4.53.2", "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", "@rollup/rollup-linux-arm-musleabihf": "4.53.2", "@rollup/rollup-linux-arm64-gnu": "4.53.2", "@rollup/rollup-linux-arm64-musl": "4.53.2", "@rollup/rollup-linux-loong64-gnu": "4.53.2", "@rollup/rollup-linux-ppc64-gnu": "4.53.2", "@rollup/rollup-linux-riscv64-gnu": "4.53.2", "@rollup/rollup-linux-riscv64-musl": "4.53.2", "@rollup/rollup-linux-s390x-gnu": "4.53.2", "@rollup/rollup-linux-x64-gnu": "4.53.2", "@rollup/rollup-linux-x64-musl": "4.53.2", "@rollup/rollup-openharmony-arm64": "4.53.2", "@rollup/rollup-win32-arm64-msvc": "4.53.2", "@rollup/rollup-win32-ia32-msvc": "4.53.2", "@rollup/rollup-win32-x64-gnu": "4.53.2", "@rollup/rollup-win32-x64-msvc": "4.53.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g=="], @@ -447,6 +583,8 @@ "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "satori": ["satori@0.18.3", "", { "dependencies": { "@shuding/opentype.js": "1.4.0-beta.0", "css-background-parser": "^0.1.0", "css-box-shadow": "1.0.0-3", "css-gradient-parser": "^0.0.17", "css-to-react-native": "^3.0.0", "emoji-regex-xs": "^2.0.1", "escape-html": "^1.0.3", "linebreak": "^1.1.0", "parse-css-color": "^0.2.1", "postcss-value-parser": "^4.2.0", "yoga-layout": "^3.2.1" } }, "sha512-T3DzWNmnrfVmk2gCIlAxLRLbGkfp3K7TyRva+Byyojqu83BNvnMeqVeYRdmUw4TKCsyH4RiQ/KuF/I4yEzgR5A=="], "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -463,7 +601,7 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -515,8 +653,14 @@ "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + "web-vitals": ["web-vitals@4.2.4", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "websocket-driver": ["websocket-driver@0.7.4", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="], + + "websocket-extensions": ["websocket-extensions@0.1.4", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="], + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -525,12 +669,20 @@ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="], @@ -543,28 +695,40 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="], "mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="], "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], } } diff --git a/package.json b/package.json index 8e7308c..cca7d3f 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "vite": "^7.1.10" }, "dependencies": { + "@firebase/auth": "^1.12.0", "@resvg/resvg-js": "^2.6.2", + "firebase": "^12.8.0", "mode-watcher": "^1.1.0", "satori": "^0.18.3" } diff --git a/src/lib/components/BotonSeguir.svelte b/src/lib/components/BotonSeguir.svelte index cd28885..2a083b3 100644 --- a/src/lib/components/BotonSeguir.svelte +++ b/src/lib/components/BotonSeguir.svelte @@ -16,16 +16,18 @@ let { post, variant = 'icon-lg' - }: { post: Omit, 'authorId'> & { authorId: string }; variant?: string } = $props(); + }: { + post: Omit, 'authorId'> & { authorId: string; id: string }; + variant?: 'icon-lg' | 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm'; + } = $props(); - let seguido: Boolean | null = $state(null); + let seguido: boolean | null = $state(null); if (typeof window !== 'undefined') { window.addEventListener('followCacheUpdated', (( event: CustomEvent<{ userId: string; isFollowed: boolean } | { clearAll: true }> ) => { if ('clearAll' in event.detail && event.detail.clearAll === true) { - cargarSeguido(); } else if ('userId' in event.detail && event.detail.userId === post.authorId) { seguido = event.detail.isFollowed; } @@ -41,9 +43,11 @@ async function cargarSeguido() { let a = cacheSeguidos.get(post.authorId); if (a === undefined) { - const seguidoStatus = await esSeguido(post); - cacheSeguidos.set(post.authorId, seguidoStatus.isFollowing || false); - seguido = seguidoStatus.isFollowing || false; + const seguidoStatus = await esSeguido(post as Post); + if (seguidoStatus) { + cacheSeguidos.set(post.authorId, seguidoStatus.isFollowing || false); + seguido = seguidoStatus.isFollowing || false; + } return; } seguido = a; diff --git a/src/lib/components/CardPerfil.svelte b/src/lib/components/CardPerfil.svelte index a766b77..d365baf 100644 --- a/src/lib/components/CardPerfil.svelte +++ b/src/lib/components/CardPerfil.svelte @@ -17,6 +17,9 @@ import Badge from './ui/badge/badge.svelte'; import { resolve } from '$app/paths'; import { goto } from '$app/navigation'; + import { Tooltip } from './ui/tooltip'; + import TooltipTrigger from './ui/tooltip/tooltip-trigger.svelte'; + import TooltipContent from './ui/tooltip/tooltip-content.svelte'; let { data = $bindable() } = $props(); @@ -101,10 +104,10 @@ - {usu.displayName} + {data.displayName} @{data.username} - {#if usu.bio} + {#if data.bio} {@html contenido()} @@ -126,10 +129,10 @@ - {usu.displayName} + {data.displayName} @{data.username} - {#if usu.bio} + {#if data.bio} {@html usu.bio.replaceAll('\n', '')} @@ -151,14 +154,30 @@ {#each data.seguidos.response as seguidos (seguidos.id)} - - - - - {seguidos.displayName?.[0] || ''} - - - + + + + + + + {seguidos.displayName?.[0] || ''} + + + + + + + + + + {seguidos.displayName?.[0] || ''} + + + {seguidos.displayName} + @{seguidos.username} + + + {/each} {#if data.seguidos.response?.length < data.countSeguidos} @@ -188,14 +207,30 @@ {#each data.seguidores.response as seguidores (seguidores.id)} - - - - - {seguidores.displayName?.[0] || ''} - - - + + + + + + + {seguidores.displayName?.[0] || ''} + + + + + + + + + + {seguidores.displayName?.[0] || ''} + + + {seguidores.displayName} + @{seguidores.username} + + + {/each} {#if data.seguidores.response?.length < data.countSeguidores} diff --git a/src/lib/components/DialogModificarUsuario.svelte b/src/lib/components/DialogModificarUsuario.svelte index 1d6403f..564d8b3 100644 --- a/src/lib/components/DialogModificarUsuario.svelte +++ b/src/lib/components/DialogModificarUsuario.svelte @@ -1,5 +1,4 @@ + + + + {@render children?.()} + + + + + + Resetear Contraseña + + + {#if mensajeError} + + {mensajeError} + + {/if} + + + Contraseña Actual + + + + Nueva Contraseña + + + La contraseña debe contener al menos una mayúscula, una minúscula, un número y un + carácter especial. Además de 8 caracteres de longitud. + + + + Confirmar Contraseña + + + + + {#if cargando} + + {:else} + Resetear Contraseña + {/if} + + + + diff --git a/src/lib/components/FireBaseButton.svelte b/src/lib/components/FireBaseButton.svelte new file mode 100644 index 0000000..bc9ccf0 --- /dev/null +++ b/src/lib/components/FireBaseButton.svelte @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + Continue with Google + Continue with Google + + + + diff --git a/src/lib/components/TablaUsuarios.svelte b/src/lib/components/TablaUsuarios.svelte index 34cdc3e..9d81d50 100644 --- a/src/lib/components/TablaUsuarios.svelte +++ b/src/lib/components/TablaUsuarios.svelte @@ -9,6 +9,7 @@ import Button from './ui/button/button.svelte'; import KeyIcon from '@lucide/svelte/icons/key'; import UserPen from '@lucide/svelte/icons/user-pen'; + import Shield from '@lucide/svelte/icons/shield'; import Search from '@lucide/svelte/icons/search'; import Plus from '@lucide/svelte/icons/plus'; import { Tooltip } from './ui/tooltip'; @@ -27,6 +28,8 @@ import InputGroupAddon from './ui/input-group/input-group-addon.svelte'; import InputGroupInput from './ui/input-group/input-group-input.svelte'; import AgregarUsuario from './admin/AgregarUsuario.svelte'; + import DarAdmin from './admin/DarAdmin.svelte'; + import { busquedaAdminUsuarios } from '@/hooks/busquedaAdminUsuarios'; interface Props { usuarios: UserResponseDto[]; @@ -36,14 +39,14 @@ let open = $state(false); let openModificarUsuario = $state(false); - + let openDarAdmin = $state(false); let openBorrar = $state(false); + let opencrearUsuario = $state(false); + let usuarioBorrar: UserResponseDto | null = $state(null); - - //si ponia contraseña en español quedaba muy largo el nombre let usuarioCambioPass: UserResponseDto | null = $state(null); - let usuarioModificar: UserResponseDto | null = $state(null); + let usuarioDarAdmin: UserResponseDto | null = $state(null); let search = $state(''); @@ -51,6 +54,8 @@ let sortBy = $state(null); let sortDirection = $state<'asc' | 'desc'>('asc'); + let usuariosFiltrados = $state(usuarios); + function ordenarPor(campo: SortKey) { if (sortBy === campo) { sortDirection = sortDirection === 'asc' ? 'desc' : 'asc'; @@ -58,38 +63,27 @@ sortBy = campo; sortDirection = 'asc'; } + usuariosFiltrados = usuariosFiltrados.toSorted((a, b) => { + if (!sortBy) return 0; + + const key: SortKey = sortBy; + + if (key === 'createdAt') { + const ta = new Date(a.createdAt).getTime(); + const tb = new Date(b.createdAt).getTime(); + return sortDirection === 'asc' ? ta - tb : tb - ta; + } + + if (key === 'postsCount') { + return sortDirection === 'asc' ? a.postsCount - b.postsCount : b.postsCount - a.postsCount; + } + + const sa = a[key].toString().toLowerCase(); + const sb = b[key].toString().toLowerCase(); + return sortDirection === 'asc' ? sa.localeCompare(sb) : sb.localeCompare(sa); + }); } - let usuariosFiltrados = $derived( - usuarios - .filter( - (u) => - u.username.toLowerCase().startsWith(search.toLowerCase()) || - u.displayName.toLowerCase().startsWith(search.toLowerCase()) - ) - .toSorted((a, b) => { - if (!sortBy) return 0; - - const key: SortKey = sortBy; - - if (key === 'createdAt') { - const ta = new Date(a.createdAt).getTime(); - const tb = new Date(b.createdAt).getTime(); - return sortDirection === 'asc' ? ta - tb : tb - ta; - } - - if (key === 'postsCount') { - return sortDirection === 'asc' - ? a.postsCount - b.postsCount - : b.postsCount - a.postsCount; - } - - const sa = a[key].toString().toLowerCase(); - const sb = b[key].toString().toLowerCase(); - return sortDirection === 'asc' ? sa.localeCompare(sb) : sb.localeCompare(sa); - }) - ); - function getSortIcon(campo: SortKey) { if (sortBy !== campo) return ''; return sortDirection === 'asc' ? '↑' : '↓'; @@ -109,17 +103,49 @@ openBorrar = true; usuarioBorrar = usuario; } - -let opencrearUsuario = $state(false); + + function handleDarAdmin(usuario: UserResponseDto) { + openDarAdmin = true; + usuarioDarAdmin = usuario; + } + // $inspect(usuarios); + let timeoutId: ReturnType | number | undefined; + + function buscarUsuarios() { + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(async () => { + if (search === '') { + usuariosFiltrados = usuarios; + return; + } + usuariosFiltrados = await busquedaAdminUsuarios(search); + }, 200); + + return () => { + if (timeoutId) clearTimeout(timeoutId); + }; + } - + buscarUsuarios()} + /> - opencrearUsuario = !opencrearUsuario} variant="secondary" class="bg-blue-500/20"> + (opencrearUsuario = !opencrearUsuario)} + variant="secondary" + class="bg-blue-500/20"> @@ -191,6 +217,24 @@ let opencrearUsuario = $state(false); {/if} + + + + handleDarAdmin(usuario)} + variant={usuario.isAdmin ? 'destructive' : 'default'} + > + + + + + {#if usuario.isAdmin} + Sacar admin + {:else} + Dar Admin + {/if} + + {/each} @@ -200,4 +244,5 @@ let opencrearUsuario = $state(false); - \ No newline at end of file + + diff --git a/src/lib/components/UserCard.svelte b/src/lib/components/UserCard.svelte index 0e43d2a..1df1e45 100644 --- a/src/lib/components/UserCard.svelte +++ b/src/lib/components/UserCard.svelte @@ -35,8 +35,5 @@ - {#if usu.bio} - {usu.bio} - {/if} diff --git a/src/lib/components/admin/AgregarUsuario.svelte b/src/lib/components/admin/AgregarUsuario.svelte index d016b3a..65b9a46 100644 --- a/src/lib/components/admin/AgregarUsuario.svelte +++ b/src/lib/components/admin/AgregarUsuario.svelte @@ -41,6 +41,12 @@ if (error == '') { invalidate('admin:load'); open = false; + dto = { + username: '', + email: '', + password: '', + displayName: '' + }; } cargando = false; diff --git a/src/lib/components/admin/DarAdmin.svelte b/src/lib/components/admin/DarAdmin.svelte new file mode 100644 index 0000000..42e0226 --- /dev/null +++ b/src/lib/components/admin/DarAdmin.svelte @@ -0,0 +1,93 @@ + + +{#if mostrarResultado} + { + mostrarResultado = false; + open = false; + }} + > + + + {mensajeResultado} + + + +{/if} + (open = false)}> + + + Confirmar Admin + + { + e.preventDefault(); + try { + const req = await fetch(`${$apiBase}/api/admin/give`, { + method: 'PATCH', + body: JSON.stringify({ isAdmin: usuario?.isAdmin || false, id: usuario?.id || '' }), + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${$sesionStore?.accessToken}` + } + }); + if (req.ok) { + esExitoso = true; + mensajeResultado = 'Operación realizada con éxito'; + mostrarResultado = true; + invalidate('admin:load'); + } else { + const res = await req.json(); + esExitoso = false; + mensajeResultado = res.message || 'Error desconocido'; + mostrarResultado = true; + } + } catch { + esExitoso = false; + mensajeResultado = 'Error de conexión'; + mostrarResultado = true; + } + }} + > + {#if usuario} + + {usuario.isAdmin + ? '¿Estás seguro que quieres sacarle acceso de administrador al usuario ' + : '¿Estás seguro que quieres dar acceso de administrador al usuario '} + {usuario.displayName} + (@{usuario.username})? + + {/if} + + Confirmar + (open = false)}>Cancelar + + + + diff --git a/src/lib/components/admin/ModificarUsuario.svelte b/src/lib/components/admin/ModificarUsuario.svelte index 567cb31..404ee70 100644 --- a/src/lib/components/admin/ModificarUsuario.svelte +++ b/src/lib/components/admin/ModificarUsuario.svelte @@ -78,7 +78,6 @@ {/if} - {#if cargando} diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte new file mode 100644 index 0000000..a178cf5 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte @@ -0,0 +1,23 @@ + + + + + More + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte new file mode 100644 index 0000000..1a84c4c --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte new file mode 100644 index 0000000..e6bc17d --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte @@ -0,0 +1,31 @@ + + +{#if child} + {@render child({ props: attrs })} +{:else} + + {@render children?.()} + +{/if} diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte new file mode 100644 index 0000000..1272a37 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte new file mode 100644 index 0000000..5fb6979 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte b/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte new file mode 100644 index 0000000..84106a1 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte @@ -0,0 +1,27 @@ + + +svg]:size-3.5", className)} + {...restProps} +> + {#if children} + {@render children?.()} + {:else} + + {/if} + diff --git a/src/lib/components/ui/breadcrumb/breadcrumb.svelte b/src/lib/components/ui/breadcrumb/breadcrumb.svelte new file mode 100644 index 0000000..8f8a3e6 --- /dev/null +++ b/src/lib/components/ui/breadcrumb/breadcrumb.svelte @@ -0,0 +1,21 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/breadcrumb/index.ts b/src/lib/components/ui/breadcrumb/index.ts new file mode 100644 index 0000000..dc914ec --- /dev/null +++ b/src/lib/components/ui/breadcrumb/index.ts @@ -0,0 +1,25 @@ +import Root from "./breadcrumb.svelte"; +import Ellipsis from "./breadcrumb-ellipsis.svelte"; +import Item from "./breadcrumb-item.svelte"; +import Separator from "./breadcrumb-separator.svelte"; +import Link from "./breadcrumb-link.svelte"; +import List from "./breadcrumb-list.svelte"; +import Page from "./breadcrumb-page.svelte"; + +export { + Root, + Ellipsis, + Item, + Separator, + Link, + List, + Page, + // + Root as Breadcrumb, + Ellipsis as BreadcrumbEllipsis, + Item as BreadcrumbItem, + Separator as BreadcrumbSeparator, + Link as BreadcrumbLink, + List as BreadcrumbList, + Page as BreadcrumbPage, +}; diff --git a/src/lib/components/ui/input-otp/index.ts b/src/lib/components/ui/input-otp/index.ts new file mode 100644 index 0000000..e9ae273 --- /dev/null +++ b/src/lib/components/ui/input-otp/index.ts @@ -0,0 +1,15 @@ +import Root from "./input-otp.svelte"; +import Group from "./input-otp-group.svelte"; +import Slot from "./input-otp-slot.svelte"; +import Separator from "./input-otp-separator.svelte"; + +export { + Root, + Group, + Slot, + Separator, + Root as InputOTP, + Group as InputOTPGroup, + Slot as InputOTPSlot, + Separator as InputOTPSeparator, +}; diff --git a/src/lib/components/ui/input-otp/input-otp-group.svelte b/src/lib/components/ui/input-otp/input-otp-group.svelte new file mode 100644 index 0000000..9b311bc --- /dev/null +++ b/src/lib/components/ui/input-otp/input-otp-group.svelte @@ -0,0 +1,20 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/input-otp/input-otp-separator.svelte b/src/lib/components/ui/input-otp/input-otp-separator.svelte new file mode 100644 index 0000000..2c4fb4a --- /dev/null +++ b/src/lib/components/ui/input-otp/input-otp-separator.svelte @@ -0,0 +1,19 @@ + + + + {#if children} + {@render children?.()} + {:else} + + {/if} + diff --git a/src/lib/components/ui/input-otp/input-otp-slot.svelte b/src/lib/components/ui/input-otp/input-otp-slot.svelte new file mode 100644 index 0000000..9d45382 --- /dev/null +++ b/src/lib/components/ui/input-otp/input-otp-slot.svelte @@ -0,0 +1,31 @@ + + + + {cell.char} + {#if cell.hasFakeCaret} + + + + {/if} + diff --git a/src/lib/components/ui/input-otp/input-otp.svelte b/src/lib/components/ui/input-otp/input-otp.svelte new file mode 100644 index 0000000..a9de2c4 --- /dev/null +++ b/src/lib/components/ui/input-otp/input-otp.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/ui/login-form/login-form.svelte b/src/lib/components/ui/login-form/login-form.svelte index 52f83e7..6ddf3dc 100644 --- a/src/lib/components/ui/login-form/login-form.svelte +++ b/src/lib/components/ui/login-form/login-form.svelte @@ -36,7 +36,7 @@ Contraseña - + Te Olvidaste la contraseña? diff --git a/src/lib/head/Busqueda.svelte b/src/lib/head/Busqueda.svelte index 12a8b22..1acaf87 100644 --- a/src/lib/head/Busqueda.svelte +++ b/src/lib/head/Busqueda.svelte @@ -20,6 +20,7 @@ import { resolve } from '$app/paths'; import { busquedaHashtags } from '@/hooks/busquedaHashtags'; import Separator from '@/components/ui/separator/separator.svelte'; + import { goto } from '$app/navigation'; let search: string = $state(''); let open = $state(false); @@ -36,7 +37,7 @@ } // $inspect(usuarios, loading); - let timeoutId: number | undefined; + let timeoutId: ReturnType | number | undefined; function buscar() { if (timeoutId) { clearTimeout(timeoutId); @@ -76,6 +77,12 @@ placeholder="Buscar Usuario o Hashtag" bind:value={search} oninput={buscar} + onkeydown={(e) => { + if (e.key === 'Enter') { + open = false; + goto(resolve(`/search/${encodeURIComponent(search)}`)); + } + }} /> {#if search} diff --git a/src/lib/head/Header.svelte b/src/lib/head/Header.svelte index b403ae7..b2cc441 100644 --- a/src/lib/head/Header.svelte +++ b/src/lib/head/Header.svelte @@ -20,7 +20,7 @@ onMount(() => { sesionStore.subscribe((value) => { - showCerrarSesion = !!value?.accessToken; + showCerrarSesion = !!value?.username; }); }); @@ -50,7 +50,7 @@ - + diff --git a/src/lib/hooks/busquedaAdminUsuarios.ts b/src/lib/hooks/busquedaAdminUsuarios.ts new file mode 100644 index 0000000..ce1b2d1 --- /dev/null +++ b/src/lib/hooks/busquedaAdminUsuarios.ts @@ -0,0 +1,21 @@ +import { apiBase } from '@/stores/url'; +import { sesionStore } from '@/stores/usuario'; +import { get } from 'svelte/store'; + +export async function busquedaAdminUsuarios(q: string) { + try { + const response = await fetch(get(apiBase) + '/api/admin/users?q=' + q, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } + }); + if (response.ok) { + return await response.json(); + } + return []; + } catch { + return []; + } +} diff --git a/src/lib/hooks/cambiarContraseñaUsuario.ts b/src/lib/hooks/cambiarContraseñaUsuario.ts new file mode 100644 index 0000000..2d4b2ab --- /dev/null +++ b/src/lib/hooks/cambiarContraseñaUsuario.ts @@ -0,0 +1,28 @@ +import { apiBase } from '@/stores/url'; +import { get } from 'svelte/store'; +import type { UserResponseDto } from '../../types'; +import { sesionStore } from '@/stores/usuario'; + +export async function cambiarContraseñaUsuario( + oldPassword: string, + newPassword: string, + id: number +) { + try { + const req = await fetch(`${get(apiBase)}/api/users/${id}/password`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + }, + body: JSON.stringify({ currentPassword: oldPassword, newPassword }) + }); + if (req.ok) { + return ''; + } + const data = await req.json(); + return data.message; + } catch { + return 'No se pudo alcanzar el servidor'; + } +} diff --git a/src/lib/hooks/checkEmail.ts b/src/lib/hooks/checkEmail.ts index 2bbc8e8..5d3176c 100644 --- a/src/lib/hooks/checkEmail.ts +++ b/src/lib/hooks/checkEmail.ts @@ -1,16 +1,16 @@ -import { apiBase } from "@/stores/url"; -import { get } from "svelte/store"; +import { apiBase } from '@/stores/url'; +import { get } from 'svelte/store'; export async function checkEmail(email: string) { - try { - const req = await fetch(`${get(apiBase)}/api/users/check-email/${email}`, { - method: "GET" - }); - if (req.ok){ - return (await req.json()).available; - } - return false; - } catch { - return false; - } + try { + const req = await fetch(`${get(apiBase)}/api/users/check-email/${email}`, { + method: 'GET' + }); + if (req.ok) { + return (await req.json()).available; + } + return false; + } catch { + return false; + } } diff --git a/src/lib/hooks/checkUsername.ts b/src/lib/hooks/checkUsername.ts index 2d82818..c950b5e 100644 --- a/src/lib/hooks/checkUsername.ts +++ b/src/lib/hooks/checkUsername.ts @@ -1,16 +1,16 @@ -import { apiBase } from "@/stores/url"; -import { get } from "svelte/store"; +import { apiBase } from '@/stores/url'; +import { get } from 'svelte/store'; export async function checkUsername(username: string) { - try { - const req = await fetch(`${get(apiBase)}/api/users/check-username/${username}`, { - method: "GET" - }); - if (req.ok){ - return (await req.json()).available; - } - return false; - } catch { - return false; - } + try { + const req = await fetch(`${get(apiBase)}/api/users/check-username/${username}`, { + method: 'GET' + }); + if (req.ok) { + return (await req.json()).available; + } + return false; + } catch { + return false; + } } diff --git a/src/lib/hooks/getPosts.ts b/src/lib/hooks/getPosts.ts index e95c52e..d40111d 100644 --- a/src/lib/hooks/getPosts.ts +++ b/src/lib/hooks/getPosts.ts @@ -1,17 +1,20 @@ -import { apiBase } from "@/stores/url"; -import { sesionStore } from "@/stores/usuario"; -import { get } from "svelte/store"; +import { apiBase } from '@/stores/url'; +import { sesionStore } from '@/stores/usuario'; +import { get } from 'svelte/store'; +import type { Post } from '../../types'; +import { PAGE_SIZE } from '../stores/posts'; -export async function getPosts() { +export async function getPosts(page: number = 1): Promise { + const token = get(sesionStore)?.accessToken; + const headers: HeadersInit = {}; + if (token) headers.Authorization = `Bearer ${token}`; - const req = await fetch(`${get(apiBase)}/timeline?pageSize=20`,{ - headers: { - Authorization: `Bearer ${get(sesionStore)?.accessToken}` - - } + const res = await fetch(`${get(apiBase)}/timeline?page=${page}&pageSize=${PAGE_SIZE}`, { + headers }); - if (req.ok) { - return await req.json(); - } + + if (!res.ok) throw new Error('Error cargando posts'); + + return res.json(); } diff --git a/src/lib/hooks/likePost.ts b/src/lib/hooks/likePost.ts index a16787c..691acae 100644 --- a/src/lib/hooks/likePost.ts +++ b/src/lib/hooks/likePost.ts @@ -4,7 +4,7 @@ import { sesionStore } from '@/stores/usuario'; import type { Post } from '../../types'; export async function likePost(post: Post) { - let method = post.isLiked ? "DELETE" : "POST"; + let method = post.isLiked ? 'DELETE' : 'POST'; try { const req = await fetch(get(apiBase) + `/api/posts/${post.id}/like`, { method: method, diff --git a/src/lib/hooks/loadMorePosts.ts b/src/lib/hooks/loadMorePosts.ts new file mode 100644 index 0000000..8a89428 --- /dev/null +++ b/src/lib/hooks/loadMorePosts.ts @@ -0,0 +1,32 @@ +import { get } from 'svelte/store'; +import { page, loadingPosts, PAGE_SIZE } from '@/stores/posts'; +import { appendPosts } from '@/stores/posts'; +import { getPosts } from './getPosts'; + +let finished = false; + +export async function loadMorePosts() { + if (get(loadingPosts) || finished) return; + + loadingPosts.set(true); + + try { + const currentPage = get(page); + const newPosts = await getPosts(currentPage); + + if (newPosts.length === 0) { + finished = true; + return; + } + + appendPosts(newPosts); + + if (newPosts.length < PAGE_SIZE) { + finished = true; + } else { + page.update((p) => p + 1); + } + } finally { + loadingPosts.set(false); + } +} diff --git a/src/lib/hooks/loginFirebase.ts b/src/lib/hooks/loginFirebase.ts new file mode 100644 index 0000000..52d46c1 --- /dev/null +++ b/src/lib/hooks/loginFirebase.ts @@ -0,0 +1,29 @@ +import { apiBase } from '@/stores/url'; +import type { LoginSsoDto, Sesion } from '../../types'; +import { sesionStore } from '@/stores/usuario'; +import { goto } from '$app/navigation'; +import { get } from 'svelte/store'; + +export async function loginFirebase(dto: LoginSsoDto, callbackfn: () => void) { + if (dto.accessToken == '' || dto.uid == '') return; + try { + const req = await fetch(get(apiBase) + '/api/auth/login/sso', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + credentials: 'include', + body: JSON.stringify(dto) + }); + if (req.ok) { + const token: Sesion = await req.json(); + sesionStore.set({ ...token, accessToken: dto.accessToken }); + goto('/'); + } else { + callbackfn(); + } + } catch { + // callbackfn(); + console.error('fallo al intentar alcanzar el servidor'); + } +} diff --git a/src/lib/hooks/obtenerRespuestasPorId.ts b/src/lib/hooks/obtenerRespuestasPorId.ts index 27124c7..bf3264f 100644 --- a/src/lib/hooks/obtenerRespuestasPorId.ts +++ b/src/lib/hooks/obtenerRespuestasPorId.ts @@ -6,12 +6,13 @@ import type { Post } from '../../types'; export async function obtenerRespuestasPorId( id: string, fetch2?: Function, - depends?: Function + depends?: Function, + page: number = 1 ): Promise { if (depends) depends('post:respuestas'); const fetchFn = fetch2 ? fetch2 : fetch; try { - const req = await fetchFn(`${get(apiBase)}/api/posts/${id}/replies`, { + const req = await fetchFn(`${get(apiBase)}/api/posts/${id}/replies?page=${page}`, { method: 'GET', headers: { Authorization: `Bearer ${get(sesionStore)?.accessToken}` diff --git a/src/lib/hooks/obtenerSeguidoresPorUsuario.ts b/src/lib/hooks/obtenerSeguidoresPorUsuario.ts index 3c84b05..a8fe627 100644 --- a/src/lib/hooks/obtenerSeguidoresPorUsuario.ts +++ b/src/lib/hooks/obtenerSeguidoresPorUsuario.ts @@ -1,22 +1,28 @@ import { sesionStore } from '@/stores/usuario'; import type { UsersResponseDto } from '../../types'; -import { get } from 'svelte/store'; import { apiBase } from '@/stores/url'; +import { get } from 'svelte/store'; export async function obtenerSeguidoresPorUsuario( id: string, + page: number = 1, limit: number = 20, - fetch2: Function + fetch2?: Function ): Promise { try { const fetchFunc = fetch2 || fetch; - const response = await fetchFunc(`${get(apiBase)}/api/users/${id}/followers?limit=${limit}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${get(sesionStore)?.accessToken}` + const skip = (page - 1) * limit; + + const response = await fetchFunc( + `${get(apiBase)}/api/users/${id}/followers?skip=${skip}&limit=${limit}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } } - }); + ); if (!response.ok) { return null; diff --git a/src/lib/hooks/obtenerSeguidosPorUsuario.ts b/src/lib/hooks/obtenerSeguidosPorUsuario.ts index 1db413b..0bc91cb 100644 --- a/src/lib/hooks/obtenerSeguidosPorUsuario.ts +++ b/src/lib/hooks/obtenerSeguidosPorUsuario.ts @@ -5,26 +5,31 @@ import { get } from 'svelte/store'; export async function obtenerSeguidosPorUsuario( id: string, + page: number = 1, limit: number = 20, fetch2?: Function ): Promise { try { const fetchFunc = fetch2 || fetch; + const skip = (page - 1) * limit; - const response = await fetchFunc(`${get(apiBase)}/api/users/${id}/following?limit=${limit}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${get(sesionStore)?.accessToken}` + const response = await fetchFunc( + `${get(apiBase)}/api/users/${id}/following?skip=${skip}&limit=${limit}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } } - }); + ); if (!response.ok) { return null; } - const users: UsersResponseDto = await response.json(); - return users; + const data: UsersResponseDto = await response.json(); + return data; } catch (error) { return null; } diff --git a/src/lib/hooks/publicarPost.ts b/src/lib/hooks/publicarPost.ts index 8ddb6f0..e32b3c5 100644 --- a/src/lib/hooks/publicarPost.ts +++ b/src/lib/hooks/publicarPost.ts @@ -1,27 +1,27 @@ -import { addPost } from "@/stores/posts"; -import { apiBase } from "@/stores/url"; -import { sesionStore } from "@/stores/usuario"; -import { get } from "svelte/store"; +import { addPost } from '@/stores/posts'; +import { apiBase } from '@/stores/url'; +import { sesionStore } from '@/stores/usuario'; +import { get } from 'svelte/store'; -export async function publicarPost(formData: FormData){ - try{ - const req = fetch(get(apiBase) + '/api/posts', { - method: 'POST', - //credentials: 'include', - headers: { - Authorization: `Bearer ${get(sesionStore)?.accessToken}` - }, - body: formData - }); +export async function publicarPost(formData: FormData) { + try { + const req = fetch(get(apiBase) + '/api/posts', { + method: 'POST', + //credentials: 'include', + headers: { + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + }, + body: formData + }); - const res = await req; - if (res.ok) { - const post = await res.json(); - addPost(post); - return ''; - } - return 'No se pudo crear el post.'; - } catch { - return 'Fallo al alcanzar el servidor'; + const res = await req; + if (res.ok) { + const post = await res.json(); + addPost(post); + return ''; } + return 'No se pudo crear el post.'; + } catch { + return 'Fallo al alcanzar el servidor'; + } } diff --git a/src/lib/hooks/registerFirebase.ts b/src/lib/hooks/registerFirebase.ts new file mode 100644 index 0000000..bdc939a --- /dev/null +++ b/src/lib/hooks/registerFirebase.ts @@ -0,0 +1,30 @@ +import { apiBase } from '@/stores/url'; +import { goto } from '$app/navigation'; +import type { RegisterDto, RegisterSsoDto } from '../../types'; +import { get } from 'svelte/store'; + +export async function registerFirebase( + dto: RegisterSsoDto, + callbackfn: () => void, + admin: boolean = false +) { + if (dto.uid == '' || dto.token == '' || !dto.email?.includes('@') || dto.username == '') return; + try { + const req = await fetch(get(apiBase) + '/api/auth/register/sso', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(dto) + }); + if (req.ok) { + const data = await req.json(); + if (!admin) goto('/login?msg=' + data.message); + } else { + callbackfn(); + } + } catch { + callbackfn(); + console.error('fallo al intentar alcanzar el servidor'); + } +} diff --git a/src/lib/hooks/updateImagenDePerfil.ts b/src/lib/hooks/updateImagenDePerfil.ts index 6b28196..e0e0d54 100644 --- a/src/lib/hooks/updateImagenDePerfil.ts +++ b/src/lib/hooks/updateImagenDePerfil.ts @@ -1,7 +1,4 @@ -export async function updateImagenDePerfil(){ - try{ - - }catch{ - - } +export async function updateImagenDePerfil() { + try { + } catch {} } diff --git a/src/lib/hooks/updatePost.ts b/src/lib/hooks/updatePost.ts index 7197820..721d0fd 100644 --- a/src/lib/hooks/updatePost.ts +++ b/src/lib/hooks/updatePost.ts @@ -5,9 +5,9 @@ import { sesionStore } from '@/stores/usuario'; export async function updatePost(post: Post, callbackfn: Function, message: string) { try { - const formData = new FormData(); - formData.append("content", post.content); - formData.append("image", post.image||""); + const formData = new FormData(); + formData.append('content', post.content); + formData.append('image', post.image || ''); const req = await fetch(get(apiBase) + `/api/posts/${post.id}`, { method: 'PUT', diff --git a/src/lib/stores/firebase.ts b/src/lib/stores/firebase.ts new file mode 100644 index 0000000..d032b93 --- /dev/null +++ b/src/lib/stores/firebase.ts @@ -0,0 +1,37 @@ +import { initializeApp, type FirebaseApp } from 'firebase/app'; +import { getAuth, type Auth } from 'firebase/auth'; + +import { + PUBLIC_f_apiKey, + PUBLIC_f_appId, + PUBLIC_f_authDomain, + PUBLIC_f_messagingSenderId, + PUBLIC_f_projectId, + PUBLIC_f_storageBucket +} from '$env/static/public'; + +let app: FirebaseApp | null = null; +let auth: Auth | null = null; + +const firebaseConfig = { + apiKey: PUBLIC_f_apiKey, + authDomain: PUBLIC_f_authDomain, + projectId: PUBLIC_f_projectId, + storageBucket: PUBLIC_f_storageBucket, + messagingSenderId: PUBLIC_f_messagingSenderId, + appId: PUBLIC_f_appId +}; + +export function getFirebaseApp(): FirebaseApp { + if (!app) { + app = initializeApp(firebaseConfig); + } + return app; +} + +export function getFirebaseAuth(): Auth { + if (!auth) { + auth = getAuth(getFirebaseApp()); + } + return auth; +} diff --git a/src/lib/stores/posts.ts b/src/lib/stores/posts.ts index 85fcbca..cb19b9b 100644 --- a/src/lib/stores/posts.ts +++ b/src/lib/stores/posts.ts @@ -1,29 +1,35 @@ import { writable } from 'svelte/store'; import type { Post } from '../../types'; -export const posts = writable(undefined); +export const posts = writable([]); +export const loadingPosts = writable(false); +export const page = writable(1); + +export const PAGE_SIZE = 20; export const setPosts = (newPosts: Post[]) => { posts.set(newPosts); }; +export const appendPosts = (newPosts: Post[]) => { + posts.update((current) => [...current, ...newPosts]); +}; + export const addPost = (post: Post) => { - posts.update((currentPosts) => [post, ...currentPosts]); + posts.update((current) => [post, ...current]); }; export const updatePostStore = (postId: string, updatedData: Partial) => { - posts.update((currentPosts) => - currentPosts.map((post) => (post.id === postId ? { ...post, ...updatedData } : post)) + posts.update((current) => + current.map((post) => (post.id === postId ? { ...post, ...updatedData } : post)) ); }; export const removePost = (postId: string) => { - posts.update((currentPosts) => { - const a = currentPosts.filter((post) => post.id !== postId); - return a; - }); + posts.update((current) => current.filter((post) => post.id !== postId)); }; export const resetPosts = () => { - posts.set(undefined); + posts.set([]); + page.set(1); }; diff --git a/src/lib/stores/usuario.ts b/src/lib/stores/usuario.ts index aa1b2e5..0436f16 100644 --- a/src/lib/stores/usuario.ts +++ b/src/lib/stores/usuario.ts @@ -2,6 +2,7 @@ import { get, writable } from 'svelte/store'; import { browser } from '$app/environment'; import type { Sesion } from '../../types'; import { apiBase } from '@/stores/url'; +import { getFirebaseApp, getFirebaseAuth } from './firebase'; const { subscribe } = apiBase; let baseUrl: string = ''; @@ -21,6 +22,10 @@ export const sesionStore = { reset: () => currentSesion.set(null) }; +// sesionStore.subscribe((value) => { +// console.log(value); +// }); + if (browser) { currentSesion.subscribe((value) => { localStorage.setItem('sesion', JSON.stringify(value)); @@ -45,9 +50,32 @@ if (browser) { } }; - const shouldRefreshToken = (sesion: Sesion | null): boolean => { + const shouldRefreshToken = async (sesion: Sesion | null): Promise => { if (!sesion || !sesion.accessToken) return false; + if (sesion.isFirebase) { + try { + getFirebaseApp(); + + const auth = getFirebaseAuth(); + const user = auth.currentUser; + + if (user) { + const token = await user.getIdToken(true); + currentSesion.update((s) => { + if (s) { + return { ...s, accessToken: token }; + } + return s; + }); + return false; + } + } catch (error) { + console.error('Error obteniendo token de Firebase:', error); + return false; + } + } + const decoded = decodeJWT(sesion.accessToken); if (!decoded || !decoded.exp) return false; @@ -61,28 +89,31 @@ if (browser) { const refreshAccessToken = async () => { try { const sesion = get(currentSesion); - if (!shouldRefreshToken(sesion)) return; + if (!(await shouldRefreshToken(sesion))) return; - console.log('refrescando token'); - const response = await fetch(get(apiBase) + '/api/auth/refresh', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'include' - }); - - if (response.ok) { - const data = await response.json(); - currentSesion.update((sesion) => { - if (sesion) { - return { ...sesion, accessToken: data.accessToken }; - } - return sesion; + // Solo ejecutar este código si NO es Firebase + if (!sesion?.isFirebase) { + console.log('refrescando token'); + const response = await fetch(get(apiBase) + '/api/auth/refresh', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + credentials: 'include' }); - } else { - console.error('Error refrescando token:', response.statusText); - currentSesion.set(null); + + if (response.ok) { + const data = await response.json(); + currentSesion.update((sesion) => { + if (sesion) { + return { ...sesion, accessToken: data.accessToken }; + } + return sesion; + }); + } else { + console.error('Error refrescando token:', response.statusText); + currentSesion.set(null); + } } } catch (error) { console.error('Error refrescando token:', error); @@ -90,6 +121,9 @@ if (browser) { } }; - setInterval(refreshAccessToken, 30 * 1000); // Check every 30 seconds + setInterval(() => { + refreshAccessToken(); + }, 30 * 1000); // Check every 30 seconds + refreshAccessToken(); } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 0bdba03..092740a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -13,11 +13,11 @@ export type WithoutChildrenOrChild = WithoutChildren>; export type WithElementRef = T & { ref?: U | null }; export function filtrarImagen(file: File) { - if (file) { + if (file) { const allowed = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/webp']; if (allowed.includes(file.type)) { return file; } } - return null; + return null; } diff --git a/src/routes/(privado)/admin/+page.svelte b/src/routes/(privado)/admin/+page.svelte index d1bea2a..ea7a515 100644 --- a/src/routes/(privado)/admin/+page.svelte +++ b/src/routes/(privado)/admin/+page.svelte @@ -2,7 +2,6 @@ import CardContent from '@/components/ui/card/card-content.svelte'; import Card from '@/components/ui/card/card.svelte'; import CardDescription from '@/components/ui/card/card-description.svelte'; - import { page } from '$app/state'; import TablaUsuarios from '@/components/TablaUsuarios.svelte'; import CardTitle from '@/components/ui/card/card-title.svelte'; import CardHeader from '@/components/ui/card/card-header.svelte'; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index a0ca932..5d0e401 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,6 +4,7 @@ import { ModeWatcher } from 'mode-watcher'; import Header from '@/head/Header.svelte'; import { TooltipProvider } from '@/components/ui/tooltip'; + import { resolve } from '$app/paths'; let { children } = $props(); @@ -17,3 +18,11 @@ {@render children()} + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index eec2ce0..6492f55 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,40 +1,67 @@ +{#if from === 'cambio_contraseña'} + + Se cambió la contraseña del usuario exitosamente + +{/if} + @@ -46,22 +73,22 @@ - {#if $sesionStore !== null} + {#if $sesionStore} {/if} - {#if $posts === undefined} + {#if $posts.length === 0 && $loadingPosts} Cargando - {:else if $posts.length <= 0} + {:else if $posts.length === 0} - No hay Posts que mostrar + No hay Posts que mostrar {:else} @@ -72,6 +99,13 @@ {/each} {/if} + + + {#if $loadingPosts && $posts.length > 0} + + + + {/if} {#if postAModificar} diff --git a/src/routes/+page.ts b/src/routes/+page.ts index fcb6fab..faf7d29 100644 --- a/src/routes/+page.ts +++ b/src/routes/+page.ts @@ -1 +1,4 @@ +import { resetPosts } from '@/stores/posts'; + //export const ssr = true; +// export async function load({}) {} diff --git a/src/routes/[perfil]/+page.svelte b/src/routes/[perfil]/+page.svelte index 50d6836..50dd647 100644 --- a/src/routes/[perfil]/+page.svelte +++ b/src/routes/[perfil]/+page.svelte @@ -1,6 +1,5 @@ @@ -70,9 +127,9 @@ - {#key data} - - {/key} + + + @@ -89,14 +146,12 @@ {:else if $posts?.length == 0} - + {/if} - {#if cargando} - - {:else if mensajeError !== ''} + {#if mensajeError !== ''} {:else} @@ -105,6 +160,16 @@ {/each} + + + + {#if cargando && !finished} + + {/if} + + {#if finished && $posts.length === 0} + No hay posts para mostrar + {/if} {/if} @@ -131,7 +196,20 @@ {#if $sesionStore?.isAdmin || $sesionStore?.username == params.perfil} - + + + + + + + {#if !$sesionStore.isFirebase} + + + + + + {/if} + {/if} diff --git a/src/routes/[perfil]/+page.ts b/src/routes/[perfil]/+page.ts index df6bef5..1678053 100644 --- a/src/routes/[perfil]/+page.ts +++ b/src/routes/[perfil]/+page.ts @@ -1,19 +1,20 @@ import { obtenerUsuarioPorUsername } from '@/hooks/obtenerUsuario.js'; -import type { User, UserResponseDto } from '../../types.js'; +import type { UserResponseDto } from '../../types.js'; import { error } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; import { obtenerSeguidosPorUsuario } from '@/hooks/obtenerSeguidosPorUsuario.js'; import { obtenerSeguidoresPorUsuario } from '@/hooks/obtenerSeguidoresPorUsuario.js'; import { obtenerCantidadDeSeguidores } from '@/hooks/obtenerCantidadDeSeguidores.js'; import { obtenerCantidadDeSeguidos } from '@/hooks/obtenerCantidadDeSeguidos.js'; -export async function load({ params, depends, fetch }) { +export const load: PageLoad = async ({ params, depends, fetch }) => { depends('perfil:general'); const usuario: UserResponseDto | null = await obtenerUsuarioPorUsername(params.perfil, fetch); if (!usuario) error(404, 'No se encontro el usuario, ' + params.perfil); const [seguidos, seguidores, countSeguidores, countSeguidos] = await Promise.all([ - obtenerSeguidosPorUsuario(usuario.id, 5, fetch), - obtenerSeguidoresPorUsuario(usuario.id, 5, fetch), + obtenerSeguidosPorUsuario(usuario.id, 1, 5, fetch), + obtenerSeguidoresPorUsuario(usuario.id, 1, 5, fetch), obtenerCantidadDeSeguidores(usuario.id, fetch), obtenerCantidadDeSeguidos(usuario.id, fetch) ]); @@ -25,4 +26,4 @@ export async function load({ params, depends, fetch }) { countSeguidores: countSeguidores.count, countSeguidos: countSeguidos.count }; -} +}; diff --git a/src/routes/[perfil]/seguidores/+page.svelte b/src/routes/[perfil]/seguidores/+page.svelte index 5f6bee8..07153bf 100644 --- a/src/routes/[perfil]/seguidores/+page.svelte +++ b/src/routes/[perfil]/seguidores/+page.svelte @@ -1,15 +1,40 @@ @@ -25,9 +50,14 @@ - {#if data.seguidores.length === 0} + + {#if isLoading} - No hay seguidores para mostrar. + Cargando... + + {:else if data.seguidores.length === 0} + + No hay seguidos para mostrar. {:else} @@ -38,5 +68,29 @@ {/each} {/if} + + {#if totalPages > 1} + + loadPage(currentPage - 1)} + disabled={currentPage === 1 || isLoading} + > + + + + + Página {currentPage} de {totalPages} + + + loadPage(currentPage + 1)} + disabled={currentPage === totalPages || isLoading} + > + + + + {/if} diff --git a/src/routes/[perfil]/seguidores/+page.ts b/src/routes/[perfil]/seguidores/+page.ts index e9f0f8d..7f23a42 100644 --- a/src/routes/[perfil]/seguidores/+page.ts +++ b/src/routes/[perfil]/seguidores/+page.ts @@ -1,14 +1,16 @@ import { obtenerSeguidoresPorUsuario } from '@/hooks/obtenerSeguidoresPorUsuario'; import { obtenerUsuarioPorUsername } from '@/hooks/obtenerUsuario'; import { error } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; import type { UserResponseDto, UsersResponseDto } from '../../../types'; -export async function load({ params, fetch }) { +export const load: PageLoad = async ({ params, fetch }) => { const usuario: UserResponseDto | null = await obtenerUsuarioPorUsername(params.perfil, fetch); if (!usuario) error(404, 'No se encontro el usuario, ' + params.perfil); const seguidoresResponse: UsersResponseDto | null = await obtenerSeguidoresPorUsuario( usuario.id, + 1, 100, fetch ); @@ -17,4 +19,4 @@ export async function load({ params, fetch }) { usuario, seguidores: seguidoresResponse?.response || [] }; -} +}; diff --git a/src/routes/[perfil]/seguidos/+page.svelte b/src/routes/[perfil]/seguidos/+page.svelte index f8501c1..63e4717 100644 --- a/src/routes/[perfil]/seguidos/+page.svelte +++ b/src/routes/[perfil]/seguidos/+page.svelte @@ -1,15 +1,40 @@ @@ -25,7 +50,12 @@ - {#if data.seguidos.length === 0} + + {#if isLoading} + + Cargando... + + {:else if data.seguidos.length === 0} No hay seguidos para mostrar. @@ -38,5 +68,29 @@ {/each} {/if} + + {#if totalPages > 1} + + loadPage(currentPage - 1)} + disabled={currentPage === 1 || isLoading} + > + + + + + Página {currentPage} de {totalPages} + + + loadPage(currentPage + 1)} + disabled={currentPage === totalPages || isLoading} + > + + + + {/if} diff --git a/src/routes/[perfil]/seguidos/+page.ts b/src/routes/[perfil]/seguidos/+page.ts index 4e0e506..8d575f2 100644 --- a/src/routes/[perfil]/seguidos/+page.ts +++ b/src/routes/[perfil]/seguidos/+page.ts @@ -1,20 +1,23 @@ import { obtenerUsuarioPorUsername } from '@/hooks/obtenerUsuario'; import { error } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; import type { UserResponseDto, UsersResponseDto } from '../../../types'; import { obtenerSeguidosPorUsuario } from '@/hooks/obtenerSeguidosPorUsuario'; -export async function load({ params, fetch }) { +export const load: PageLoad = async ({ params, fetch }) => { const usuario: UserResponseDto | null = await obtenerUsuarioPorUsername(params.perfil, fetch); if (!usuario) error(404, 'No se encontro el usuario, ' + params.perfil); const seguidosResponse: UsersResponseDto | null = await obtenerSeguidosPorUsuario( usuario.id, + 1, 100, fetch ); return { usuario, - seguidos: seguidosResponse?.response || [] + seguidos: seguidosResponse?.response || [], + totalCount: seguidosResponse?.totalCount ?? 0 }; -} +}; diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte new file mode 100644 index 0000000..4fdd452 --- /dev/null +++ b/src/routes/about/+page.svelte @@ -0,0 +1,66 @@ + + + + + + Sobre Nosotros + + + + + {#each devs as dev} + + + + + + {dev.name.toLocaleUpperCase()[0]} + + + + + + {dev.name} + {dev.role} + + + + + + + {/each} + + + diff --git a/src/routes/htag/[htag]/+page.svelte b/src/routes/htag/[htag]/+page.svelte index e58b96a..90b028b 100644 --- a/src/routes/htag/[htag]/+page.svelte +++ b/src/routes/htag/[htag]/+page.svelte @@ -10,7 +10,6 @@ import { updatePost } from '@/hooks/updatePost'; import Separator from '@/components/ui/separator/separator.svelte'; import { page } from '$app/state'; - import { onMount } from 'svelte'; interface props { data: { diff --git a/src/routes/htag/[htag]/+page.ts b/src/routes/htag/[htag]/+page.ts index 78cce78..32dc6e0 100644 --- a/src/routes/htag/[htag]/+page.ts +++ b/src/routes/htag/[htag]/+page.ts @@ -1,4 +1,5 @@ import { obtenerCantidadDeUsosdeHtag } from '@/hooks/obtenerCantidadDeUsosdeHtag.js'; +export const ssr = false; export async function load({ params, fetch }) { let { htag } = params; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index 276fa50..d630172 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -4,6 +4,9 @@ import AlertCircleIcon from '@lucide/svelte/icons/alert-circle'; import { fade, fly } from 'svelte/transition'; import Info from '@lucide/svelte/icons/info'; + import Card from '@/components/ui/card/card.svelte'; + import CardContent from '@/components/ui/card/card-content.svelte'; + import FireBaseButton from '@/components/FireBaseButton.svelte'; let { data } = $props(); @@ -41,6 +44,12 @@ {/if} + + + + + + {#if showAlert} diff --git a/src/routes/password-reset/+page.svelte b/src/routes/password-reset/+page.svelte new file mode 100644 index 0000000..c56b523 --- /dev/null +++ b/src/routes/password-reset/+page.svelte @@ -0,0 +1,35 @@ + + + + + + + {#if estado === 'email'} + + {/if} + {#if estado === 'otp'} + + {/if} + {#if estado === 'nuevapass'} + + {/if} + + + diff --git a/src/routes/password-reset/IngresarEmail.svelte b/src/routes/password-reset/IngresarEmail.svelte new file mode 100644 index 0000000..bd10395 --- /dev/null +++ b/src/routes/password-reset/IngresarEmail.svelte @@ -0,0 +1,101 @@ + + +{#if mensajeError} + (mensajeError = '')}> + + + + +{/if} + + + + + + Ingresa tu correo electrónico + {#if checkeado == null} + + {:else if checkeado == true} + + {:else if esEmailExistente} + + {:else} + + {/if} + + { + e.preventDefault(); + try { + const formData = new FormData(); + formData.append('email', email); + const req = await fetch(`${$apiBase}/api/password-reset/otp`, { + method: 'POST', + body: formData + }); + if (req.ok) { + estado = 'otp'; + return; + } + mensajeError = await req.text(); + } catch { + mensajeError = 'No se pudo alcanzar el servidor'; + } + }} + > + + + Enviar código + + + + + + diff --git a/src/routes/password-reset/NuevaPass.svelte b/src/routes/password-reset/NuevaPass.svelte new file mode 100644 index 0000000..54cabf8 --- /dev/null +++ b/src/routes/password-reset/NuevaPass.svelte @@ -0,0 +1,96 @@ + + +{#if mensajeError} + (mensajeError = '')}> + + + + +{/if} + + + + + Crear una Nueva Contraseña + { + e.preventDefault(); + const formData = new FormData(); + formData.append('otp', otp); + formData.append('email', email); + formData.append('newpass', pass); + try { + const req = await fetch(`${$apiBase}/api/password-reset/change`, { + method: 'PATCH', + body: formData + }); + if (req.ok) { + const token = await req.json(); + sesionStore.set(token); + goto('/?from=cambio_contraseña'); + return; + } + const data = await req.text(); + mensajeError = data; + } catch { + mensajeError = 'No se pudo alcanzar el servidor'; + } + }} + > + + + Contraseña + + + La contraseña debe contener al menos una mayúscula, una minúscula, un número y un + carácter especial. Además de 8 chars de longitud. + + + + Repetir Contraseña + + + Establecer Contraseña + + + + + diff --git a/src/routes/password-reset/Otp.svelte b/src/routes/password-reset/Otp.svelte new file mode 100644 index 0000000..39c9cec --- /dev/null +++ b/src/routes/password-reset/Otp.svelte @@ -0,0 +1,92 @@ + + +{#if mensajeError} + (mensajeError = '')}> + + + + +{/if} + + + + + + Verificación de correo + + Hemos enviado un código de verificación a tu correo electrónico. + + + + + + {#snippet children({ cells })} + + {#each cells as cell} + + {/each} + + {/snippet} + + + + + { + try { + const formData = new FormData(); + formData.append('otp', otp); + formData.append('email', email); + let req = await fetch(`${$apiBase}/api/password-reset/otp/check`, { + method: 'POST', + body: formData + }); + if (req.ok) { + estado = 'nuevapass'; + return; + } else { + mensajeError = await req.text(); + } + } catch { + mensajeError = 'No se pudo alcanzar el servidor'; + } + }} + > + Verificar + + + console.log('Reenviar código')}> + Reenviar código de verificación + + + + + + + diff --git a/src/routes/password-reset/Pasos.svelte b/src/routes/password-reset/Pasos.svelte new file mode 100644 index 0000000..329ef5d --- /dev/null +++ b/src/routes/password-reset/Pasos.svelte @@ -0,0 +1,30 @@ + + + + + + + + Ingrese Email + + + + + Ingresar OTP + + + + + Nueva Contraseña + + + + + diff --git a/src/routes/post/[idpost]/+page.svelte b/src/routes/post/[idpost]/+page.svelte index a6dbdff..4e01809 100644 --- a/src/routes/post/[idpost]/+page.svelte +++ b/src/routes/post/[idpost]/+page.svelte @@ -5,7 +5,7 @@ import type { Post } from '../../../types'; import PostCard from '@/components/PostCard.svelte'; import ModalEditar from '../../[perfil]/modalEditar.svelte'; - import { fade, slide } from 'svelte/transition'; + import { fade } from 'svelte/transition'; import { updatePost } from '@/hooks/updatePost'; import { goto, invalidate } from '$app/navigation'; import Separator from '@/components/ui/separator/separator.svelte'; @@ -18,6 +18,14 @@ import ThumbsUp from '@lucide/svelte/icons/thumbs-up'; import { TamañoPantalla } from './TamañoPantalla.svelte'; import BotonSeguir from '@/components/BotonSeguir.svelte'; + import Pen from '@lucide/svelte/icons/pen'; + import Trash_2 from '@lucide/svelte/icons/trash-2'; + import { Tooltip } from '@/components/ui/tooltip'; + import TooltipTrigger from '@/components/ui/tooltip/tooltip-trigger.svelte'; + import TooltipContent from '@/components/ui/tooltip/tooltip-content.svelte'; + import { deletePost } from '@/hooks/deletePost'; + import { flip } from 'svelte/animate'; + import { obtenerRespuestasPorId } from '@/hooks/obtenerRespuestasPorId'; interface Prop { data: { @@ -29,7 +37,17 @@ let tamaño = new TamañoPantalla(); let { data }: Prop = $props(); - // $inspect(data); + + let respuestasPaginadas: Post[] = $state([]); + let pagerespuestas: number = $state(1); + + let seguirMostrandoMostrarMás = $derived.by(() => { + if (data.post.repliesCount <= 20) return false; + if (respuestasPaginadas.length == 0) return true; + return data.respuestas.length + respuestasPaginadas.length < data.post.repliesCount; + }); + // $inspect([respuestasPaginadas, seguirMostrandoMostrarMás]); + let postAModificar: Post | null = $state(null); async function handleEditar(e: SubmitEvent) { @@ -97,14 +115,36 @@ - {#each data.respuestas as respuesta (respuesta.id)} - - - {@render Respuesta(respuesta)} - - - + {#each [...data.respuestas, ...respuestasPaginadas] as respuesta (respuesta.id)} + + + + {@render Respuesta(respuesta)} + + + + {/each} + {#if seguirMostrandoMostrarMás} + { + let ret = await obtenerRespuestasPorId( + data.post.id, + undefined, + undefined, + ++pagerespuestas + ); + if (ret == null) return; + if (typeof ret == 'string') return; + if (ret.length == 0) { + seguirMostrandoMostrarMás = false; + return; + } + respuestasPaginadas.push(...ret); + }}>Cargar Más Respuestas + {/if} @@ -134,11 +174,46 @@ @{post.authorName} {new Date(post.createdAt).toLocaleDateString()} - {#key $sesionStore?.accessToken} - - - - {/key} + + {#if $sesionStore?.username === post.authorName} + + + { + postAModificar = post; + }} + > + + + + Editar + + + + { + await deletePost( + post, + () => { + invalidate('post:respuestas'); + }, + false, + '' + ); + }} + > + + + + Borrar + + {/if} + + {post.content} diff --git a/src/routes/post/[idpost]/+page.ts b/src/routes/post/[idpost]/+page.ts index 44ec681..d315c8f 100644 --- a/src/routes/post/[idpost]/+page.ts +++ b/src/routes/post/[idpost]/+page.ts @@ -2,6 +2,8 @@ import { obtenerPostPorId } from '@/hooks/obtenerPostPorId.js'; import { obtenerRespuestasPorId } from '@/hooks/obtenerRespuestasPorId'; import { error } from '@sveltejs/kit'; +export const ssr = false; + export async function load({ params, fetch, depends }) { let ret = await obtenerPostPorId(params.idpost, fetch, depends); if (ret == null) return error(404, 'no existe un post con ese id.'); diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte index 11d6656..618bcf3 100644 --- a/src/routes/register/+page.svelte +++ b/src/routes/register/+page.svelte @@ -3,6 +3,9 @@ import AlertCircleIcon from '@lucide/svelte/icons/alert-circle'; import * as Alert from '@/components/ui/alert'; import { fade } from 'svelte/transition'; + import FireBaseButton from '@/components/FireBaseButton.svelte'; + import Card from '@/components/ui/card/card.svelte'; + import CardContent from '@/components/ui/card/card-content.svelte'; let showAlert: boolean = $state(false); @@ -21,6 +24,12 @@ + + + + + + {#if showAlert} diff --git a/src/routes/search/[user]/+page.svelte b/src/routes/search/[user]/+page.svelte index 23634fd..416f31f 100644 --- a/src/routes/search/[user]/+page.svelte +++ b/src/routes/search/[user]/+page.svelte @@ -1,13 +1,47 @@ - {#each data.usuarios as usu} - - {/each} + + {#if data.usuarios.length != 0} + Usuarios + + {#each data.usuarios as usu} + + + + {/each} + {/if} + {#if data.htags.length != 0} + + Hastags + + + + {#each data.htags as htag} + + + #{htag} + + + + {/each} + + {/if} + diff --git a/src/routes/search/[user]/+page.ts b/src/routes/search/[user]/+page.ts index 9f4fd74..b1861dc 100644 --- a/src/routes/search/[user]/+page.ts +++ b/src/routes/search/[user]/+page.ts @@ -1,15 +1,21 @@ import { busquedaUsuarios } from '@/hooks/busquedaUsuarios'; import { error } from '@sveltejs/kit'; import type { UserResponseDto } from '../../../types'; +import { busquedaHashtags } from '@/hooks/busquedaHashtags.js'; export async function load({ params }) { - let usuarios: UserResponseDto[] = await busquedaUsuarios(params.user); + let req1 = busquedaUsuarios(params.user); + //usa el mismo param + let req2 = busquedaHashtags(params.user); + + let [usuarios, htags] = await Promise.all([req1, req2]); + if (usuarios == null) { return error(500, 'No se pudo alcanzar el servidor.'); } - if (usuarios.length == 0) { - return error(404, 'No se encontraron usuarios que coinsidan con la busqueda.'); + if (usuarios.length == 0 && htags.length == 0) { + return error(404, 'No se encontraron usuarios ni hashtags que coinsidan con la busqueda.'); } - return { usuarios }; + return { usuarios, htags }; } diff --git a/src/types.d.ts b/src/types.d.ts index 1ebbe92..3bebbd4 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -39,6 +39,8 @@ export interface Sesion { displayName: string; username: string; isAdmin: boolean; + email: string; + isFirebase: boolean; } export interface LoginDto { @@ -46,6 +48,11 @@ export interface LoginDto { password: string?; } +export interface LoginSsoDto { + accessToken: string; + uid: string; +} + export interface RegisterDto { username: string; email: string; @@ -53,6 +60,14 @@ export interface RegisterDto { displayName: string; } +export interface RegisterSsoDto { + username: string; + email: string; + displayName: string; + token: string; + uid: string; +} + export interface CreatePostDto { content: string; imageUrl: string?;
@{data.username}
{@html contenido()} @@ -126,10 +129,10 @@
{@html usu.bio.replaceAll('\n', '')}
+ La contraseña debe contener al menos una mayúscula, una minúscula, un número y un + carácter especial. Además de 8 caracteres de longitud. +
+ {usuario.isAdmin + ? '¿Estás seguro que quieres sacarle acceso de administrador al usuario ' + : '¿Estás seguro que quieres dar acceso de administrador al usuario '} + {usuario.displayName} + (@{usuario.username})? +
Cargando
No hay Posts que mostrar
No hay posts para mostrar
No hay seguidores para mostrar.
Cargando...
No hay seguidos para mostrar.
+ La contraseña debe contener al menos una mayúscula, una minúscula, un número y un + carácter especial. Además de 8 chars de longitud. +
+ Hemos enviado un código de verificación a tu correo electrónico. +
+ Ingrese Email +
Ingresar OTP
+ Nueva Contraseña +
{post.content} diff --git a/src/routes/post/[idpost]/+page.ts b/src/routes/post/[idpost]/+page.ts index 44ec681..d315c8f 100644 --- a/src/routes/post/[idpost]/+page.ts +++ b/src/routes/post/[idpost]/+page.ts @@ -2,6 +2,8 @@ import { obtenerPostPorId } from '@/hooks/obtenerPostPorId.js'; import { obtenerRespuestasPorId } from '@/hooks/obtenerRespuestasPorId'; import { error } from '@sveltejs/kit'; +export const ssr = false; + export async function load({ params, fetch, depends }) { let ret = await obtenerPostPorId(params.idpost, fetch, depends); if (ret == null) return error(404, 'no existe un post con ese id.'); diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte index 11d6656..618bcf3 100644 --- a/src/routes/register/+page.svelte +++ b/src/routes/register/+page.svelte @@ -3,6 +3,9 @@ import AlertCircleIcon from '@lucide/svelte/icons/alert-circle'; import * as Alert from '@/components/ui/alert'; import { fade } from 'svelte/transition'; + import FireBaseButton from '@/components/FireBaseButton.svelte'; + import Card from '@/components/ui/card/card.svelte'; + import CardContent from '@/components/ui/card/card-content.svelte'; let showAlert: boolean = $state(false); @@ -21,6 +24,12 @@