From 2f13c5c7bdea34f783e08738ffa6af37f7ef4c97 Mon Sep 17 00:00:00 2001 From: Valti Date: Tue, 29 Apr 2025 14:40:22 -0500 Subject: [PATCH 01/14] Update footer layout and styles - Updated the background gradient for a smoother visual - Reorganized text alignment for better readability - Added hover effects for interactive feedback --- app/_components/Footer.tsx | 54 +++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/app/_components/Footer.tsx b/app/_components/Footer.tsx index 86d08a7..f38e89a 100644 --- a/app/_components/Footer.tsx +++ b/app/_components/Footer.tsx @@ -1,31 +1,53 @@ +import Link from 'next/link'; + export default function Footer() { return ( -
-
-
-

ParChat

-

Comunica, Conecta y Transmite

+
+
+
+

ParChat

+

+ Comunica,{' '} + Conecta y{' '} + Transmite +

-
+

Stacks

-
    +

Info

-
    +
    • Información
    • Salas
    • -
    • Registro
    • +
    • + + Únete + +

Contacto

-
    +
    • Tuluá, Valle
    • Colombia
    • 3000000000
    • @@ -33,9 +55,15 @@ export default function Footer() {
-
+

© 2025 ParChat. Todos los derechos reservados.

-

Hecho con amor por el equipo de Rippio ♥

+

+ Hecho con amor por el equipo{' '} + + Rippio{' '} + + ♥ +

); From 2a8e71b5b26c21266cc9287ff27d3d7ddf3ba99b Mon Sep 17 00:00:00 2001 From: Valti Date: Tue, 29 Apr 2025 14:44:11 -0500 Subject: [PATCH 02/14] Adjust on landing page visuals and responsiveness - Added subtle animations to enhance user engagement - Improved responsive layout for better display on all screen sizes - Adjusted color scheme for better visual consistency and contrast --- app/(auth)/page.tsx | 81 ++++++++++++++++++++++---------- app/_ui/globals.css | 19 +++++++- app/layout.tsx | 4 +- public/favicon.jpg | Bin 9832 -> 0 bytes public/favicon.png | Bin 0 -> 54035 bytes public/home-decoration-bg.svg | 28 +++++++++++ public/home-decoration.svg | 86 ++++++++++++---------------------- 7 files changed, 135 insertions(+), 83 deletions(-) delete mode 100644 public/favicon.jpg create mode 100644 public/favicon.png create mode 100644 public/home-decoration-bg.svg diff --git a/app/(auth)/page.tsx b/app/(auth)/page.tsx index 0c74bf1..24568df 100644 --- a/app/(auth)/page.tsx +++ b/app/(auth)/page.tsx @@ -1,33 +1,52 @@ import Image from 'next/image'; import homeDecoration from '@/public/home-decoration.svg'; +import Link from 'next/link'; + export default function Home() { return ( -
+
-

+

Para todas tus necesidades

Una web creada por personas para conectar con personas

-

+

Transmite tu mensaje en segundos con tus compañeros de trabajo, estudio, viaje, fiesta y completos desconocidos.

- + +
+ Ingresar a una Sala +
+ +
+
+ Background Decoration + Chat Simulation
- Chat Simulation
@@ -40,10 +59,19 @@ export default function Home() { retroalimentación cuando lo necesites e interactúa con nuevas personas.

-
- Event +
+
+ Event +
-
+

Ideal para Eventos

Parchat es la solución perfecta para mantener a tus asistentes conectados y bien @@ -51,14 +79,16 @@ export default function Home() { un código de acceso y permite que todos participen fácilmente desde cualquier lugar.

-
+ +

Comunicación al Instante

Al igual que los servicios de mensajería más populares, aquí los mensajes llegan al momento de ser enviados, no te perderás ningún detalle.

-
+ +

Siempre Tienes el Control

Gestiona fácilmente los asistentes en tu evento, y elimina rápidamente a quienes @@ -88,10 +118,11 @@ export default function Home() { alt="Event" width={0} height={0} - className="w-[50%] min-w-[200px] max-w-[300px]" + draggable={false} + className="w-[50%] min-w-[350px] max-w-[400px] transition-all duration-500 ease-in-out hover:scale-104 hover:animate-pulse" />

-

Eventos y Convenciones

+

Eventos y Convenciones

Mantén a todos los asistentes conectados y al tanto de anuncios importantes. Perfecto para convenciones, conciertos y festivales. @@ -104,10 +135,11 @@ export default function Home() { alt="Event" width={0} height={0} - className="w-[50%] min-w-[200px] max-w-[300px]" + draggable={false} + className="w-[50%] min-w-[350px] max-w-[400px] transition-all duration-500 ease-in-out hover:scale-104 hover:animate-pulse" />

-

Universidades

+

Universidades

Resuelve dudas de clase, organiza trabajos en grupo o coordina transporte con tus compañeros. ¡Comunicación sin complicaciones! @@ -120,10 +152,11 @@ export default function Home() { alt="Event" width={0} height={0} - className="w-[50%] min-w-[200px] max-w-[300px]" + draggable={false} + className="w-[50%] min-w-[350px] max-w-[400px] transition-all duration-500 ease-in-out hover:scale-104 hover:animate-pulse" />

-

Ambientes Empresariales

+

Ambientes Empresariales

Crea chats de oficina para coordinar tareas sin necesidad de compartir números personales. Comunicación rápida y efectiva en el trabajo. diff --git a/app/_ui/globals.css b/app/_ui/globals.css index 9293207..88acbfe 100644 --- a/app/_ui/globals.css +++ b/app/_ui/globals.css @@ -3,7 +3,7 @@ :root { --color-blue: #3d5786; --color-babyblue: #a4b6dd; - --color-lightpurple: #a58ada; + --color-lightpurple: #b79af3; --color-purple: #6e52a3; --color-purple-2: #262240; --color-semidarkpurple: #392f4d; @@ -69,3 +69,20 @@ body { .font-quicksand { font-family: 'Quicksand', sans-serif; } + +@keyframes rotateImage { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.animate-rotate-slow { + animation: rotateImage 15s linear infinite; +} + +.animate-rotate-medium { + animation: rotateImage 5s linear infinite; +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 4a16b6d..222261e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -20,13 +20,13 @@ export const metadata: Metadata = { description: 'Comunícate y conecta con diferentes comunidades a través de salas de chat en tiempo real', icons: { - icon: '/favicon.jpg', + icon: '/favicon.png', }, openGraph: { title: 'Parchat - Salas de chat para comunidades', description: 'Únete a diferentes comunidades y chatea en tiempo real. Crea tus propias salas y conecta con personas que comparten tus intereses.', - images: ['/favicon.jpg'], // Ruta de la imagen para compartir + images: ['/favicon.png'], // Ruta de la imagen para compartir type: 'website', locale: 'es_ES', }, diff --git a/public/favicon.jpg b/public/favicon.jpg deleted file mode 100644 index 8b36b17ba6a6e8f074f6d5752d8919cfce306646..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9832 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 diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..68c354c0d9a14b72ec9b411e09aed378f950f0e1 GIT binary patch literal 54035 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/home-decoration.svg b/public/home-decoration.svg index c7afb8c..88cd6f8 100644 --- a/public/home-decoration.svg +++ b/public/home-decoration.svg @@ -1,57 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a27715dcc12f149e0c278e4b4175e06d2cb720ab Mon Sep 17 00:00:00 2001 From: Valti Date: Tue, 29 Apr 2025 14:45:25 -0500 Subject: [PATCH 03/14] update header colors and add hover effects - Changed header color palette to match new design - Added hover transitions for interactive elements --- app/_components/Header.tsx | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/app/_components/Header.tsx b/app/_components/Header.tsx index e680354..812494f 100644 --- a/app/_components/Header.tsx +++ b/app/_components/Header.tsx @@ -11,7 +11,7 @@ export default function Header() {

- ParChat Logo + ParChat Logo
@@ -41,25 +41,43 @@ export default function Header() {
{/* Desktop */} -
+
- ParChat Logo + ParChat Logo
-
    +
    • - Información + + Información +
    • - Salas + + Salas +
    • - Contacto + + Contacto +
- + Parchemos Entrar como Invitado From 8331c9aa00c888275025f6d1166d162bf2ad4126 Mon Sep 17 00:00:00 2001 From: DanilsGit Date: Tue, 29 Apr 2025 15:08:07 -0500 Subject: [PATCH 04/14] Fixing pipelines placeholders --- .github/workflows/develop-CD.yml | 1 + .github/workflows/master-CD.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/develop-CD.yml b/.github/workflows/develop-CD.yml index 70ce8cc..a31981a 100644 --- a/.github/workflows/develop-CD.yml +++ b/.github/workflows/develop-CD.yml @@ -46,5 +46,6 @@ jobs: cd /home/proyectosdanils/ docker compose down frontend-dev docker pull ${{ secrets.DOCKER_USERNAME }}/frontend-dev:stage-${{ github.sha }} + export DOCKER_IMAGE_FRONTEND_PROD=placeholder export DOCKER_IMAGE_FRONTEND_DEV=${{ secrets.DOCKER_USERNAME }}/frontend-dev:stage-${{ github.sha }} docker compose up -d frontend-dev diff --git a/.github/workflows/master-CD.yml b/.github/workflows/master-CD.yml index bd001d3..ada3fa0 100644 --- a/.github/workflows/master-CD.yml +++ b/.github/workflows/master-CD.yml @@ -47,5 +47,6 @@ jobs: cd /home/proyectosdanils/ docker compose down frontend-prod docker pull ${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }} + export DOCKER_IMAGE_FRONTEND_DEV=placeholder export DOCKER_IMAGE_FRONTEND_PROD=${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }} docker compose up -d frontend-prod \ No newline at end of file From a4952ea7256e6004804cd77ef764a55aa94fc851 Mon Sep 17 00:00:00 2001 From: DanilsGit Date: Thu, 8 May 2025 16:55:53 -0500 Subject: [PATCH 05/14] add middleware, add axios, add register and add login --- .dockerignore | 2 +- .github/workflows/develop-CD.yml | 14 +- .github/workflows/master-CD.yml | 14 +- dockerfile.dev => Dockerfile.local | 0 app/(auth)/login/_components/DesktopForm.tsx | 36 +- app/(auth)/login/_components/MobileForm.tsx | 21 +- app/(auth)/login/_lib/_actions/session.ts | 19 + app/(auth)/login/_lib/_schemas/auth.ts | 15 + app/(auth)/login/page.tsx | 23 +- app/(protected)/dashboard/room/[id]/page.tsx | 2 +- .../dashboard/room/_components/Message.tsx | 2 +- .../room/_components/MessagesList.tsx | 2 +- app/_apis/auth.ts | 7 + app/_apis/myAxios.config.ts | 27 + app/_apis/mySSAxios.config.ts | 28 + app/_components/AsideDashboard.tsx | 14 +- app/_hooks/useAuth.tsx | 79 ++ app/_hooks/useChatSocket.tsx | 2 +- app/_lib/_firebase/firebase.config.ts | 16 + app/_lib/_interfaces/IAuth.ts | 9 + .../_interfaces/IMessage.ts} | 0 app/_lib/_interfaces/IUser.ts | 11 + app/layout.tsx | 17 +- docker-compose.yaml | 5 +- middleware.ts | 31 + package-lock.json | 1072 ++++++++++++++++- package.json | 7 +- tsconfig.json | 2 +- 28 files changed, 1424 insertions(+), 53 deletions(-) rename dockerfile.dev => Dockerfile.local (100%) create mode 100644 app/(auth)/login/_lib/_actions/session.ts create mode 100644 app/(auth)/login/_lib/_schemas/auth.ts create mode 100644 app/_apis/auth.ts create mode 100644 app/_apis/myAxios.config.ts create mode 100644 app/_apis/mySSAxios.config.ts create mode 100644 app/_hooks/useAuth.tsx create mode 100644 app/_lib/_firebase/firebase.config.ts create mode 100644 app/_lib/_interfaces/IAuth.ts rename app/{_interfaces/IMessage.tsx => _lib/_interfaces/IMessage.ts} (100%) create mode 100644 app/_lib/_interfaces/IUser.ts create mode 100644 middleware.ts diff --git a/.dockerignore b/.dockerignore index 5681fc3..c2a3835 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,5 @@ # Ignorar dependencias locales -# node_modules se utilizará para mandarse desde github actions +node_modules # Ignorar config de Git .git diff --git a/.github/workflows/develop-CD.yml b/.github/workflows/develop-CD.yml index a31981a..541c693 100644 --- a/.github/workflows/develop-CD.yml +++ b/.github/workflows/develop-CD.yml @@ -35,6 +35,17 @@ jobs: mkdir -p ~/.ssh ssh-keyscan ${{ secrets.GCP_VM_IP }} >> ~/.ssh/known_hosts + - name: Actualizar o agregar imagen en .env + uses: appleboy/ssh-action@v0.1.4 + with: + host: ${{ secrets.GCP_VM_IP }} + username: ${{ secrets.GCP_VM_USER }} + key: ${{ secrets.GCP_SSH_KEY }} + script: | + cd /home/proyectosdanils/ + sed -i '/^DOCKER_IMAGE_FRONTEND_DEV=/d' .env + echo "DOCKER_IMAGE_FRONTEND_DEV=${{ secrets.DOCKER_USERNAME }}/frontend-dev:${{ github.sha }}" >> .env + # 👉 Desplegar en la VM - name: Deploy on GCP VM uses: appleboy/ssh-action@v0.1.4 @@ -45,7 +56,4 @@ jobs: script: | cd /home/proyectosdanils/ docker compose down frontend-dev - docker pull ${{ secrets.DOCKER_USERNAME }}/frontend-dev:stage-${{ github.sha }} - export DOCKER_IMAGE_FRONTEND_PROD=placeholder - export DOCKER_IMAGE_FRONTEND_DEV=${{ secrets.DOCKER_USERNAME }}/frontend-dev:stage-${{ github.sha }} docker compose up -d frontend-dev diff --git a/.github/workflows/master-CD.yml b/.github/workflows/master-CD.yml index ada3fa0..2ae286f 100644 --- a/.github/workflows/master-CD.yml +++ b/.github/workflows/master-CD.yml @@ -36,6 +36,17 @@ jobs: mkdir -p ~/.ssh ssh-keyscan ${{ secrets.GCP_VM_IP }} >> ~/.ssh/known_hosts + - name: Actualizar o agregar imagen en .env + uses: appleboy/ssh-action@v0.1.4 + with: + host: ${{ secrets.GCP_VM_IP }} + username: ${{ secrets.GCP_VM_USER }} + key: ${{ secrets.GCP_SSH_KEY }} + script: | + cd /home/proyectosdanils/ + sed -i '/^DOCKER_IMAGE_FRONTEND_PROD=/d' .env + echo "DOCKER_IMAGE_FRONTEND_PROD=${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }}" >> .env + # 👉 Desplegar en la VM - name: Deploy on GCP VM uses: appleboy/ssh-action@v0.1.4 @@ -46,7 +57,4 @@ jobs: script: | cd /home/proyectosdanils/ docker compose down frontend-prod - docker pull ${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }} - export DOCKER_IMAGE_FRONTEND_DEV=placeholder - export DOCKER_IMAGE_FRONTEND_PROD=${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }} docker compose up -d frontend-prod \ No newline at end of file diff --git a/dockerfile.dev b/Dockerfile.local similarity index 100% rename from dockerfile.dev rename to Dockerfile.local diff --git a/app/(auth)/login/_components/DesktopForm.tsx b/app/(auth)/login/_components/DesktopForm.tsx index ba34169..255c346 100644 --- a/app/(auth)/login/_components/DesktopForm.tsx +++ b/app/(auth)/login/_components/DesktopForm.tsx @@ -1,4 +1,12 @@ -export default function DesktopForm({ setMode }: { setMode: (mode: string) => void }) { +'use client'; +import { useFormStatus } from 'react-dom'; +interface Props { + setMode: (mode: string) => void; + registerAction: (e: React.FormEvent) => void; + loginAction: (e: React.FormEvent) => void; +} + +export default function DesktopForm({ setMode, registerAction, loginAction }: Props) { return (
@@ -8,7 +16,7 @@ export default function DesktopForm({ setMode }: { setMode: (mode: string) => vo

Inicia sesión para acceder a tus salas

-
+
@@ -31,9 +40,10 @@ export default function DesktopForm({ setMode }: { setMode: (mode: string) => vo placeholder="Contraseña" className="w-full border-1 border-purple-300 p-2 rounded" autoComplete="current-password" + name="password" />
- +

¿Aún no tienes una cuenta?

@@ -48,16 +58,17 @@ export default function DesktopForm({ setMode }: { setMode: (mode: string) => vo

Regístrate y Conecta

Podrás guardar tus conversaciones y salas

-
+
@@ -70,6 +81,7 @@ export default function DesktopForm({ setMode }: { setMode: (mode: string) => vo placeholder="Correo electrónico" autoComplete="email" className="w-full border-1 border-purple-300 p-2 rounded" + name="email" />
@@ -82,9 +94,10 @@ export default function DesktopForm({ setMode }: { setMode: (mode: string) => vo placeholder="Contraseña" className="w-full border-1 border-purple-300 p-2 rounded" autoComplete="current-password" + name="password" />
- +

¿Ya tienes una cuenta?

@@ -96,3 +109,12 @@ export default function DesktopForm({ setMode }: { setMode: (mode: string) => vo
); } + +function SubmitButton({ text }: { text: string }) { + const { pending } = useFormStatus(); + return ( + + ); +} diff --git a/app/(auth)/login/_components/MobileForm.tsx b/app/(auth)/login/_components/MobileForm.tsx index 47dab7b..3447ea7 100644 --- a/app/(auth)/login/_components/MobileForm.tsx +++ b/app/(auth)/login/_components/MobileForm.tsx @@ -1,8 +1,14 @@ +'use client'; +import { useState } from 'react'; + interface Props { - mobileModeLogin: boolean; - setMobileModeLogin: (value: boolean) => void; + registerAction: (e: React.FormEvent) => void; + loginAction: (e: React.FormEvent) => void; } -export default function MobileForm({ mobileModeLogin, setMobileModeLogin }: Props) { + +export default function MobileForm({ registerAction, loginAction }: Props) { + const [mobileModeLogin, setMobileModeLogin] = useState(true); + return (

Inicia sesión para acceder a tus salas

-
+
@@ -37,6 +44,7 @@ export default function MobileForm({ mobileModeLogin, setMobileModeLogin }: Prop placeholder="Contraseña" autoComplete="current-password" className="w-full border-1 border-purple-300 p-2 rounded" + name="password" />
@@ -56,7 +64,7 @@ export default function MobileForm({ mobileModeLogin, setMobileModeLogin }: Prop

Regístrate y Conecta

Podrás guardar tus conversaciones y salas

- +
@@ -78,6 +87,7 @@ export default function MobileForm({ mobileModeLogin, setMobileModeLogin }: Prop placeholder="Correo electrónico" autoComplete="email" className="w-full border-1 border-purple-300 p-2 rounded" + name="email" />
@@ -90,6 +100,7 @@ export default function MobileForm({ mobileModeLogin, setMobileModeLogin }: Prop placeholder="Contraseña" autoComplete="current-password" className="w-full border-1 border-purple-300 p-2 rounded" + name="password" />
diff --git a/app/(auth)/login/_lib/_actions/session.ts b/app/(auth)/login/_lib/_actions/session.ts new file mode 100644 index 0000000..236aa8e --- /dev/null +++ b/app/(auth)/login/_lib/_actions/session.ts @@ -0,0 +1,19 @@ +// app/actions/session.ts +'use server'; + +import { cookies } from 'next/headers'; + +export async function createSession(token: string) { + (await cookies()).set('session', token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 60 * 60 * 24 * 7, // 1 semana + }); + console.log('Session created with token:', token); +} + +export async function deleteSession() { + (await cookies()).delete('session'); + console.log('Session deleted'); +} diff --git a/app/(auth)/login/_lib/_schemas/auth.ts b/app/(auth)/login/_lib/_schemas/auth.ts new file mode 100644 index 0000000..d6e75d3 --- /dev/null +++ b/app/(auth)/login/_lib/_schemas/auth.ts @@ -0,0 +1,15 @@ +import { z } from 'zod'; + +export const registerSchema = z.object({ + email: z.string().email({ message: 'Invalid email address' }).trim(), + password: z.string().min(6, { message: 'Password must be at least 6 characters long' }).trim(), + displayName: z + .string() + .min(3, { message: 'Display name must be at least 3 characters long' }) + .trim(), +}); + +export const loginSchema = z.object({ + email: z.string().email({ message: 'Invalid email address' }).trim(), + password: z.string().min(6, { message: 'Password must be at least 6 characters long' }).trim(), +}); diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 072b80e..769bea0 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -3,10 +3,27 @@ import Image from 'next/image'; import { useState } from 'react'; import DesktopForm from './_components/DesktopForm'; import MobileForm from './_components/MobileForm'; +import { useRouter } from 'next/navigation'; +import { useAuth } from '@/app/_hooks/useAuth'; export default function Login() { const [mode, setMode] = useState('login'); - const [mobileModeLogin, setMobileModeLogin] = useState(true); + const { login, register } = useAuth(); + const router = useRouter(); + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + const formData = new FormData(e.currentTarget); + await login(formData); + router.push('/dashboard'); + }; + + const handleRegister = async (e: React.FormEvent) => { + e.preventDefault(); + const formData = new FormData(e.currentTarget); + await register(formData); + router.push('/dashboard'); + }; return (
@@ -19,10 +36,10 @@ export default function Login() { />
{/* Desktop */} - + {/* Mobile */} - +
myAxios.post('/auth/signup', data); + +export const verifySession = async () => myAxios.get('/api/v1/auth/me'); diff --git a/app/_apis/myAxios.config.ts b/app/_apis/myAxios.config.ts new file mode 100644 index 0000000..c5a18a9 --- /dev/null +++ b/app/_apis/myAxios.config.ts @@ -0,0 +1,27 @@ +import axios from 'axios'; +import { getAuth } from 'firebase/auth'; + +const myAxios = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + headers: { + 'Content-Type': 'application/json', + }, +}); + +myAxios.interceptors.request.use( + async config => { + const user = getAuth().currentUser; + if (user) { + const token = await user.getIdToken(); + config.headers.Authorization = `Bearer ${token}`; + } + console.log(process.env.NEXT_PUBLIC_API_URL, 'URL API'); + return config; + }, + error => { + console.error('Request error:', error); + return Promise.reject(error); + } +); + +export default myAxios; diff --git a/app/_apis/mySSAxios.config.ts b/app/_apis/mySSAxios.config.ts new file mode 100644 index 0000000..88c968b --- /dev/null +++ b/app/_apis/mySSAxios.config.ts @@ -0,0 +1,28 @@ +import axios from 'axios'; + +const mySSAxios = axios.create({ + baseURL: process.env.API_URL_SS, + headers: { + 'Content-Type': 'application/json', + }, +}); + +mySSAxios.interceptors.request.use( + // config => { + // const token = localStorage.getItem('token'); + // if (token) { + // config.headers['Authorization'] = `Bearer ${token}`; + // } + // return config; + // }, + config => { + console.log(process.env.API_URL_SS); + return config; + }, + error => { + console.error('Request error:', error); + return Promise.reject(error); + } +); + +export default mySSAxios; diff --git a/app/_components/AsideDashboard.tsx b/app/_components/AsideDashboard.tsx index a82e154..e24eb54 100644 --- a/app/_components/AsideDashboard.tsx +++ b/app/_components/AsideDashboard.tsx @@ -3,9 +3,18 @@ import Image from 'next/image'; import Link from 'next/link'; import { AnonymousMaskIcon, GearIcon, InfoIcon, LinkIcon, RoomIcon } from '../_ui/icons'; import { useIsMobileLarge } from '../_hooks/useIsMobileLarge'; +import { useAuth } from '../_hooks/useAuth'; +import { useRouter } from 'next/navigation'; export default function AsideDashboard() { const { isMobileLg } = useIsMobileLarge(); + const router = useRouter(); + const { logout } = useAuth(); + + const handleLogout = async () => { + await logout(); + router.push('/login'); + }; return ( <> @@ -65,7 +74,10 @@ export default function AsideDashboard() {

-
diff --git a/app/_hooks/useAuth.tsx b/app/_hooks/useAuth.tsx new file mode 100644 index 0000000..0c70696 --- /dev/null +++ b/app/_hooks/useAuth.tsx @@ -0,0 +1,79 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { toast } from 'react-toastify'; +import { create } from 'zustand'; +import { + browserLocalPersistence, + setPersistence, + signInWithEmailAndPassword, + User, +} from 'firebase/auth'; +import { auth } from '../_lib/_firebase/firebase.config'; +import { loginSchema, registerSchema } from '../(auth)/login/_lib/_schemas/auth'; +import { registerUser } from '../_apis/auth'; +import { createSession, deleteSession } from '../(auth)/login/_lib/_actions/session'; + +type Auth = { + user: User | null; + setUser: (user: User | null) => void; + login: (formData: FormData) => Promise; + register: (formData: FormData) => Promise; + logout: () => Promise; +}; + +export const useAuth = create()(set => ({ + user: null, + setUser: user => { + set({ user }); + }, + login: async (formData: FormData) => { + const result = loginSchema.safeParse(Object.fromEntries(formData.entries())); + + if (!result.success) { + toast.error(Object.values(result.error.flatten().fieldErrors).flat().join(', ')); + return; + } + + const { email, password } = result.data; + try { + const { user } = await signInWithEmailAndPassword(auth, email, password); + const token = await user.getIdToken(); + + await createSession(token); + + await setPersistence(auth, browserLocalPersistence); + set({ user }); + toast.success('Usuario logueado correctamente'); + } catch (error: any) { + const mensaje = + error.code === 'auth/user-not-found' ? 'Usuario no encontrado' : error.message; + toast.error(mensaje); + } + }, + register: async (formData: FormData) => { + const result = registerSchema.safeParse(Object.fromEntries(formData.entries())); + + if (!result.success) { + toast.error(Object.values(result.error.flatten().fieldErrors).flat().join(', ')); + return; + } + + const { email, password, displayName } = result.data; + try { + await registerUser({ email, password, displayName }); + toast.success('Usuario registrado correctamente'); + + // Logear al usuario después de registrarse + await useAuth.getState().login(formData); + } catch (error: any) { + console.error(error); + const mensaje = error.response?.data || 'Error al registrar usuario'; + toast.error(mensaje); + } + }, + logout: async () => { + await auth.signOut(); + await deleteSession(); + set({ user: null }); + toast.success('Usuario deslogueado correctamente'); + }, +})); diff --git a/app/_hooks/useChatSocket.tsx b/app/_hooks/useChatSocket.tsx index 409c221..99d8471 100644 --- a/app/_hooks/useChatSocket.tsx +++ b/app/_hooks/useChatSocket.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { IMessage } from '../_interfaces/IMessage'; +import { IMessage } from '../_lib/_interfaces/IMessage'; interface Props { room_id: string; diff --git a/app/_lib/_firebase/firebase.config.ts b/app/_lib/_firebase/firebase.config.ts new file mode 100644 index 0000000..649b485 --- /dev/null +++ b/app/_lib/_firebase/firebase.config.ts @@ -0,0 +1,16 @@ +'use client'; +import { initializeApp } from 'firebase/app'; +import { getAuth } from 'firebase/auth'; + +const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, + measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, +}; + +const app = initializeApp(firebaseConfig); +export const auth = getAuth(app); diff --git a/app/_lib/_interfaces/IAuth.ts b/app/_lib/_interfaces/IAuth.ts new file mode 100644 index 0000000..601422f --- /dev/null +++ b/app/_lib/_interfaces/IAuth.ts @@ -0,0 +1,9 @@ +export interface RegisterUserData { + email: string; + password: string; + displayName: string; +} +export interface LoginUserData { + email: string; + password: string; +} diff --git a/app/_interfaces/IMessage.tsx b/app/_lib/_interfaces/IMessage.ts similarity index 100% rename from app/_interfaces/IMessage.tsx rename to app/_lib/_interfaces/IMessage.ts diff --git a/app/_lib/_interfaces/IUser.ts b/app/_lib/_interfaces/IUser.ts new file mode 100644 index 0000000..fff5310 --- /dev/null +++ b/app/_lib/_interfaces/IUser.ts @@ -0,0 +1,11 @@ +export interface IUser { + createdAt: string; + displayName: string; + email: string; + isDeleted: boolean; + lastSeen: string; + photoUrl: string; + status: string; + uid: string; + updatedAt: string; +} diff --git a/app/layout.tsx b/app/layout.tsx index 222261e..2a20582 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -14,6 +14,7 @@ import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css'; import type { Metadata } from 'next'; +import { ToastContainer } from 'react-toastify'; export const metadata: Metadata = { title: 'Parchat', @@ -39,7 +40,21 @@ export default function RootLayout({ }>) { return ( - {children} + + + {children} + ); } diff --git a/docker-compose.yaml b/docker-compose.yaml index c6b97ab..df927df 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,11 +2,12 @@ services: frontend-dev: build: context: . - dockerfile: Dockerfile.dev + dockerfile: Dockerfile.local ports: - '3000:3000' volumes: - .:/home/frontend-dev - - /home/frontend-dev/node_modules environment: - NODE_ENV=development + env_file: + - .env diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..641e648 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,31 @@ +import { cookies } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; + +const protectedRoutes = ['/dashboard']; +const publicRoutes = ['/login', '/login-gest']; + +export default async function middleware(req: NextRequest) { + console.log('Middleware ejecutándose'); + + const path = req.nextUrl.pathname; + const isProtectedRoute = protectedRoutes.includes(path); + const isPublicRoute = publicRoutes.includes(path); + + const cookie = (await cookies()).get('session')?.value; + const session = cookie ? cookie : null; + + if (isProtectedRoute && !session) { + return NextResponse.redirect(new URL('/login', req.url)); + } + + if (isPublicRoute && session) { + return NextResponse.redirect(new URL('/dashboard', req.url)); + } + + return NextResponse.next(); +} + +// Especifica en qué rutas debe ejecutarse +export const config = { + matcher: ['/:path*'], +}; diff --git a/package-lock.json b/package-lock.json index da15903..7432cfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,14 @@ "@fontsource/raleway": "^5.2.5", "@fontsource/roboto": "^5.2.5", "@mui/material": "^7.0.2", + "axios": "^1.9.0", + "firebase": "^11.6.1", "next": "^15.3.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-toastify": "^11.0.5", + "zod": "^3.24.4", + "zustand": "^5.0.4" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -469,6 +474,570 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@firebase/analytics": { + "version": "0.10.12", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.12.tgz", + "integrity": "sha512-iDCGnw6qdFqwI5ywkgece99WADJNoymu+nLIQI4fZM/vCZ3bEo4wlpEetW71s1HqGpI0hQStiPhqVjFxDb2yyw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.18.tgz", + "integrity": "sha512-Hw9mzsSMZaQu6wrTbi3kYYwGw9nBqOHr47pVLxfr5v8CalsdrG5gfs9XUlPOZjHRVISp3oQrh1j7d3E+ulHPjQ==", + "dependencies": { + "@firebase/analytics": "0.10.12", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==" + }, + "node_modules/@firebase/app": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.11.5.tgz", + "integrity": "sha512-uNp8/Rv12GrrM/dfyqzZCftA2i/5X9axmiEtUDmyQw+0S17EV5s9gudOgdIIGr849LmbAk3At2CBZMqiQJVwNw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.13.tgz", + "integrity": "sha512-ONsgml8/dplUOAP42JQO6hhiWDEwR9+RUTLenxAN9S8N6gel/sDQ9Ci721Py1oASMGdDU8v9R7xAZxzvOX5lPg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.20.tgz", + "integrity": "sha512-/twgmlnNAaZ/wbz3kcQrL/26b+X+zUX+lBmu5LwwEcWcpnb+mrVEAKhD7/ttm52dxYiSWtLDeuXy3FXBhqBC5A==", + "dependencies": { + "@firebase/app-check": "0.8.13", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.54", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.54.tgz", + "integrity": "sha512-Vwf29tV/5bHEnp+VPgNWOFMbFG+RSur2ntmzZ19Plp5dJOtoo2nQS817COALLaHlebG/Xf/P5PVHyeQNcSVCqQ==", + "dependencies": { + "@firebase/app": "0.11.5", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.1.tgz", + "integrity": "sha512-YsCppueiV4AsMTf4oQ49KiADvtqKnG5j9Q4mBv7xGa0hnSTAX3jpdwlTluU3n0JxUT2tbPkeOESJmF4a9GWlMQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.21.tgz", + "integrity": "sha512-FrUEcqLEWVA3mGyq96wWVxXzEIWTrdBctgQuC4MVuCyH5rJZu1kPsLKdeCYuYbqTz7i94DNuGxMNIW3Y5eFqaQ==", + "dependencies": { + "@firebase/auth": "1.10.1", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.13.tgz", + "integrity": "sha512-I/Eg1NpAtZ8AAfq8mpdfXnuUpcLxIDdCDtTzWSh+FXnp/9eCKJ3SNbOCKrUCyhLzNa2SiPJYruei0sxVjaOTeg==", + "dependencies": { + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.4.tgz", + "integrity": "sha512-Clt0bHoth4N60RmzTdCaw20S5Eeg5PhjbsxP7tIB9FQlP9qm9pS25WW9v4C3gj9DugrBrJ8d/gh/e+H5+F276Q==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.14.tgz", + "integrity": "sha512-9nxYtkHAG02/Nh2Ssms1T4BbWPPjiwohCvkHDUl4hNxnki1kPgsLo5xe9kXNzbacOStmVys+RUXvwzynQSKmUQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.5.tgz", + "integrity": "sha512-CNf1UbvWh6qIaSf4sn6sx2DTDz/em/D7QxULH1LTxxDQHr9+CeYGvlAqrKnk4ZH0P0eIHyQFQU7RwkUJI0B9gQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/database": "1.0.14", + "@firebase/database-types": "1.0.10", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.10.tgz", + "integrity": "sha512-mH6RC1E9/Pv8jf1/p+M8YFTX+iu+iHDN89hecvyO7wHrI4R1V0TXjxOHvX3nLJN1sfh0CWG6CHZ0VlrSmK/cwg==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.11.0" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.11.tgz", + "integrity": "sha512-Ve9Q1YZKgG7Of8jhwPCy43CLe0Oi62clCDYLNYs0Rz08U75caIFZyASRmz+2FZWdMt8fLGmRLDNd0KfX16zMvA==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.46", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.46.tgz", + "integrity": "sha512-wwcs1aexd46z/SYHRV9ICOU3nzugSsMGdLAerInswy1SYjiilEq5jubb5KxZZk60jvirGKRbZUbTEhx7FsUkOw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/firestore": "4.7.11", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.3.tgz", + "integrity": "sha512-Wv7JZMUkKLb1goOWRtsu3t7m97uK6XQvjQLPvn8rncY91+VgdU72crqnaYCDI/ophNuBEmuK8mn0/pAnjUeA6A==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.13", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.20.tgz", + "integrity": "sha512-iIudmYDAML6n3c7uXO2YTlzra2/J6lnMzmJTXNthvrKVMgNMaseNoQP1wKfchK84hMuSF8EkM4AvufwbJ+Juew==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/functions": "0.12.3", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.13.tgz", + "integrity": "sha512-6ZpkUiaygPFwgVneYxuuOuHnSPnTA4KefLEaw/sKk/rNYgC7X6twaGfYb0sYLpbi9xV4i5jXsqZ3WO+yaguNgg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.13.tgz", + "integrity": "sha512-f/o6MqCI7LD/ulY9gvgkv6w5k6diaReD8BFHd/y/fEdpsXmFWYS/g28GXCB72bRVBOgPpkOUNl+VsMvDwlRKmw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.17", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.17.tgz", + "integrity": "sha512-W3CnGhTm6Nx8XGb6E5/+jZTuxX/EK8Vur4QXvO1DwZta/t0xqWMRgO9vNsZFMYBqFV4o3j4F9qK/iddGYwWS6g==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.11.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.17.tgz", + "integrity": "sha512-5Q+9IG7FuedusdWHVQRjpA3OVD9KUWp/IPegcv0s5qSqRLBjib7FlAeWxN+VL0Ew43tuPJBY2HKhEecuizmO1Q==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/messaging": "0.12.17", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==" + }, + "node_modules/@firebase/performance": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.2.tgz", + "integrity": "sha512-DXLLp0R0jdxH/yTmv+WTkOzsLl8YYecXh4lGZE0dzqC0IV8k+AxpLSSWvOTCkAETze8yEU/iF+PtgYVlGjfMMQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.15.tgz", + "integrity": "sha512-wUxsw7hGBEMN6XfvYQqwPIQp5LcJXawWM5tmYp6L7ClCoTQuEiCKHWWVurJgN8Q1YHzoHVgjNfPQAOVu29iMVg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.2", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.0.tgz", + "integrity": "sha512-Yrk4l5+6FJLPHC6irNHMzgTtJ3NfHXlAXVChCBdNFtgmzyGmufNs/sr8oA0auEfIJ5VpXCaThRh3P4OdQxiAlQ==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/installations": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.13.tgz", + "integrity": "sha512-UmHoO7TxAEJPIZf8e1Hy6CeFGMeyjqSCpgoBkQZYXFI2JHhzxIyDpr8jVKJJN1dmAePKZ5EX7dC13CmcdTOl7Q==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.0", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.7.tgz", + "integrity": "sha512-FkRyc24rK+Y6EaQ1tYFm3TevBnnfSNA0VyTfew2hrYyL/aYfatBg7HOgktUdB4kWMHNA9VoTotzZTGoLuK92wg==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.17.tgz", + "integrity": "sha512-CBlODWEZ5b6MJWVh21VZioxwxNwVfPA9CAdsk+ZgVocJQQbE2oDW1XJoRcgthRY1HOitgbn4cVrM+NlQtuUYhw==", + "dependencies": { + "@firebase/component": "0.6.13", + "@firebase/storage": "0.13.7", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.11.0.tgz", + "integrity": "sha512-PzSrhIr++KI6y4P6C/IdgBNMkEx0Ex6554/cYd0Hm+ovyFSJtJXqb/3OSIdnBoa2cpwZT1/GW56EmRc5qEc5fQ==", + "hasInstallScript": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/vertexai": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@firebase/vertexai/-/vertexai-1.2.1.tgz", + "integrity": "sha512-cukZ5ne2RsOWB4PB1EO6nTXgOLxPMKDJfEn+XnSV5ZKWM0ID5o0DvbyS59XihFaBzmy2SwJldP5ap7/xUnW4jA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.13", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.11.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" + }, "node_modules/@fontsource/quicksand": { "version": "5.2.6", "resolved": "https://registry.npmjs.org/@fontsource/quicksand/-/quicksand-5.2.6.tgz", @@ -495,6 +1064,35 @@ "url": "https://github.com/sponsors/ayuhito" } }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "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" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1377,6 +1975,60 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1657,7 +2309,6 @@ "version": "20.17.27", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.27.tgz", "integrity": "sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==", - "dev": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2104,11 +2755,18 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2299,6 +2957,11 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -2360,6 +3023,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2476,7 +3149,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2549,6 +3221,19 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2574,7 +3259,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2585,8 +3269,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { "version": "1.9.1", @@ -2598,6 +3281,17 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2756,6 +3450,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -2790,7 +3492,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2907,7 +3608,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2916,7 +3616,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2952,7 +3651,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -2964,7 +3662,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -3008,7 +3705,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "engines": { "node": ">=6" } @@ -3531,6 +4227,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3576,6 +4283,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "11.6.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.6.1.tgz", + "integrity": "sha512-aF00ZR+ziiq5/vxamCKpY1I0LA/ungG2qrsQIDibT+xqdvz8MaMnN0aHU4LIxxTx+Dbga/KlUXeklidRJahgHg==", + "dependencies": { + "@firebase/analytics": "0.10.12", + "@firebase/analytics-compat": "0.2.18", + "@firebase/app": "0.11.5", + "@firebase/app-check": "0.8.13", + "@firebase/app-check-compat": "0.3.20", + "@firebase/app-compat": "0.2.54", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.1", + "@firebase/auth-compat": "0.5.21", + "@firebase/data-connect": "0.3.4", + "@firebase/database": "1.0.14", + "@firebase/database-compat": "2.0.5", + "@firebase/firestore": "4.7.11", + "@firebase/firestore-compat": "0.3.46", + "@firebase/functions": "0.12.3", + "@firebase/functions-compat": "0.3.20", + "@firebase/installations": "0.6.13", + "@firebase/installations-compat": "0.2.13", + "@firebase/messaging": "0.12.17", + "@firebase/messaging-compat": "0.2.17", + "@firebase/performance": "0.7.2", + "@firebase/performance-compat": "0.2.15", + "@firebase/remote-config": "0.6.0", + "@firebase/remote-config-compat": "0.2.13", + "@firebase/storage": "0.13.7", + "@firebase/storage-compat": "0.3.17", + "@firebase/util": "1.11.0", + "@firebase/vertexai": "1.2.1" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -3595,6 +4337,25 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3610,6 +4371,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -3660,11 +4435,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -3688,7 +4470,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3770,7 +4551,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3842,7 +4622,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3854,7 +4633,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -3884,6 +4662,16 @@ "react-is": "^16.7.0" } }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4096,6 +4884,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", @@ -4703,12 +5499,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4724,7 +5530,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4751,6 +5556,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5238,6 +6062,34 @@ "react-is": "^16.13.1" } }, + "node_modules/protobufjs": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.0.tgz", + "integrity": "sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==", + "hasInstallScript": true, + "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" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5291,6 +6143,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-toastify": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz", + "integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -5353,6 +6217,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -5463,6 +6335,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -5731,6 +6622,24 @@ "node": ">=10.0.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -5838,6 +6747,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -6143,8 +7063,7 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/update-browserslist-db": { "version": "1.1.3", @@ -6185,6 +7104,32 @@ "punycode": "^2.1.0" } }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6294,6 +7239,30 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -6302,6 +7271,31 @@ "node": ">= 6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -6313,6 +7307,42 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", + "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz", + "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 658d3ae..fdc883b 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,14 @@ "@fontsource/raleway": "^5.2.5", "@fontsource/roboto": "^5.2.5", "@mui/material": "^7.0.2", + "axios": "^1.9.0", + "firebase": "^11.6.1", "next": "^15.3.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-toastify": "^11.0.5", + "zod": "^3.24.4", + "zustand": "^5.0.4" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/tsconfig.json b/tsconfig.json index d8b9323..5662b35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,6 @@ "@/*": ["./*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "middleware.ts"], "exclude": ["node_modules"] } From 4c16df86be50ce0a9cdd180654200113a8c42c4b Mon Sep 17 00:00:00 2001 From: DanilsGit Date: Thu, 8 May 2025 21:40:26 -0500 Subject: [PATCH 06/14] Fix CD and add backend conections --- .github/workflows/develop-CD.yml | 16 ++++++++++++---- .github/workflows/master-CD.yml | 14 +++++++++++--- Dockerfile.local | 4 ++-- Dockerfile.prod | 23 ++++++++++++++++++++--- app/_lib/_firebase/firebase.config.ts | 5 ++--- docker-compose.yaml | 4 ++-- 6 files changed, 49 insertions(+), 17 deletions(-) diff --git a/.github/workflows/develop-CD.yml b/.github/workflows/develop-CD.yml index 541c693..f0cd50d 100644 --- a/.github/workflows/develop-CD.yml +++ b/.github/workflows/develop-CD.yml @@ -17,9 +17,18 @@ jobs: with: ref: develop - # 👉 Build la imagen Docker + # 👉 Build Docker image with Firebase args - name: Build Docker image - run: docker build -f Dockerfile.prod -t ${{ secrets.DOCKER_USERNAME }}/frontend-dev:stage-${{ github.sha }} . + run: | + docker build \ + --build-arg NEXT_PUBLIC_FIREBASE_API_KEY=${{ secrets.FIREBASE_API_KEY }} \ + --build-arg NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=${{ secrets.FIREBASE_AUTH_DOMAIN }} \ + --build-arg NEXT_PUBLIC_FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }} \ + --build-arg NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=${{ secrets.FIREBASE_STORAGE_BUCKET }} \ + --build-arg NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=${{ secrets.FIREBASE_MESSAGING_SENDER_ID }} \ + --build-arg NEXT_PUBLIC_FIREBASE_APP_ID=${{ secrets.FIREBASE_APP_ID }} \ + --build-arg NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=${{ secrets.FIREBASE_MEASUREMENT_ID }} \ + -f Dockerfile.prod -t ${{ secrets.DOCKER_USERNAME }}/frontend-dev:${{ github.sha }} . # 👉 Log in to DockerHub - name: DockerHub Login @@ -27,7 +36,7 @@ jobs: # 👉 Push la imagen - name: Push Docker image to DockerHub - run: docker push ${{ secrets.DOCKER_USERNAME }}/frontend-dev:stage-${{ github.sha }} + run: docker push ${{ secrets.DOCKER_USERNAME }}/frontend-dev:${{ github.sha }} # 👉 Add VM to known_hosts - name: Add VM to known_hosts @@ -55,5 +64,4 @@ jobs: key: ${{ secrets.GCP_SSH_KEY }} script: | cd /home/proyectosdanils/ - docker compose down frontend-dev docker compose up -d frontend-dev diff --git a/.github/workflows/master-CD.yml b/.github/workflows/master-CD.yml index 2ae286f..7487e85 100644 --- a/.github/workflows/master-CD.yml +++ b/.github/workflows/master-CD.yml @@ -18,9 +18,18 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - # 👉 Build la imagen Docker + # 👉 Build Docker image with Firebase args - name: Build Docker image - run: docker build -f Dockerfile.prod -t ${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }} . + run: | + docker build \ + --build-arg NEXT_PUBLIC_FIREBASE_API_KEY=${{ secrets.FIREBASE_API_KEY }} \ + --build-arg NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=${{ secrets.FIREBASE_AUTH_DOMAIN }} \ + --build-arg NEXT_PUBLIC_FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }} \ + --build-arg NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=${{ secrets.FIREBASE_STORAGE_BUCKET }} \ + --build-arg NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=${{ secrets.FIREBASE_MESSAGING_SENDER_ID }} \ + --build-arg NEXT_PUBLIC_FIREBASE_APP_ID=${{ secrets.FIREBASE_APP_ID }} \ + --build-arg NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=${{ secrets.FIREBASE_MEASUREMENT_ID }} \ + -f Dockerfile.prod -t ${{ secrets.DOCKER_USERNAME }}/frontend-prod:${{ github.sha }} . # 👉 Log in to DockerHub - name: DockerHub Login @@ -56,5 +65,4 @@ jobs: key: ${{ secrets.GCP_SSH_KEY }} script: | cd /home/proyectosdanils/ - docker compose down frontend-prod docker compose up -d frontend-prod \ No newline at end of file diff --git a/Dockerfile.local b/Dockerfile.local index 2f3b06e..758cfd1 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -1,8 +1,8 @@ FROM node:22-slim -RUN mkdir -p /home/frontend-dev +RUN mkdir -p /app -WORKDIR /home/frontend-dev +WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci diff --git a/Dockerfile.prod b/Dockerfile.prod index b44a2df..14b440d 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,14 +1,31 @@ FROM node:22-alpine -RUN mkdir -p /home/frontend-prod +RUN mkdir -p /app -WORKDIR /home/frontend-prod +WORKDIR /app -COPY package.json package-lock.json ./ +COPY package.json ./ RUN npm install COPY . . +ARG NEXT_PUBLIC_FIREBASE_API_KEY +ARG NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN +ARG NEXT_PUBLIC_FIREBASE_PROJECT_ID +ARG NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET +ARG NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID +ARG NEXT_PUBLIC_FIREBASE_APP_ID +ARG NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID + +ENV NEXT_PUBLIC_FIREBASE_API_KEY=$NEXT_PUBLIC_FIREBASE_API_KEY +ENV NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=$NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN +ENV NEXT_PUBLIC_FIREBASE_PROJECT_ID=$NEXT_PUBLIC_FIREBASE_PROJECT_ID +ENV NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=$NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET +ENV NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=$NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID +ENV NEXT_PUBLIC_FIREBASE_APP_ID=$NEXT_PUBLIC_FIREBASE_APP_ID +ENV NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=$NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID + + RUN npm run build EXPOSE 3000 diff --git a/app/_lib/_firebase/firebase.config.ts b/app/_lib/_firebase/firebase.config.ts index 649b485..d9670c7 100644 --- a/app/_lib/_firebase/firebase.config.ts +++ b/app/_lib/_firebase/firebase.config.ts @@ -1,5 +1,4 @@ -'use client'; -import { initializeApp } from 'firebase/app'; +import { getApp, getApps, initializeApp } from 'firebase/app'; import { getAuth } from 'firebase/auth'; const firebaseConfig = { @@ -12,5 +11,5 @@ const firebaseConfig = { measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, }; -const app = initializeApp(firebaseConfig); +const app = getApps().length > 0 ? getApp() : initializeApp(firebaseConfig); export const auth = getAuth(app); diff --git a/docker-compose.yaml b/docker-compose.yaml index df927df..6ffe5a5 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,8 +6,8 @@ services: ports: - '3000:3000' volumes: - - .:/home/frontend-dev + - .:/app environment: - NODE_ENV=development env_file: - - .env + - .env \ No newline at end of file From fdefe27e4a23968ac09d5ca6765cdc855728e653 Mon Sep 17 00:00:00 2001 From: DanilsGit Date: Fri, 9 May 2025 14:15:13 -0500 Subject: [PATCH 07/14] fix auth --- Dockerfile.local | 6 +- app/(auth)/login/_components/DesktopForm.tsx | 10 +- app/(auth)/login/_components/MobileForm.tsx | 8 +- app/(auth)/login/_lib/_actions/session.ts | 2 - app/(auth)/login/page.tsx | 12 +- app/(protected)/layout.tsx | 2 +- app/_apis/auth.ts | 2 +- app/_apis/myAxios.config.ts | 4 +- app/_hooks/useAuth.tsx | 17 +- app/_lib/_firebase/firebase.config.ts | 2 +- middleware.ts | 2 - package-lock.json | 2164 ++++++++++++------ 12 files changed, 1531 insertions(+), 700 deletions(-) diff --git a/Dockerfile.local b/Dockerfile.local index 758cfd1..7f1ab7b 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -4,10 +4,12 @@ RUN mkdir -p /app WORKDIR /app -COPY package.json package-lock.json ./ -RUN npm ci +COPY package.json ./ + +RUN npm install COPY . . + COPY next.config.ts ./next.config.ts EXPOSE 3000 diff --git a/app/(auth)/login/_components/DesktopForm.tsx b/app/(auth)/login/_components/DesktopForm.tsx index 255c346..b4c8d71 100644 --- a/app/(auth)/login/_components/DesktopForm.tsx +++ b/app/(auth)/login/_components/DesktopForm.tsx @@ -2,8 +2,8 @@ import { useFormStatus } from 'react-dom'; interface Props { setMode: (mode: string) => void; - registerAction: (e: React.FormEvent) => void; - loginAction: (e: React.FormEvent) => void; + registerAction: (payload: FormData) => void; + loginAction: (payload: FormData) => void; } export default function DesktopForm({ setMode, registerAction, loginAction }: Props) { @@ -16,7 +16,7 @@ export default function DesktopForm({ setMode, registerAction, loginAction }: Pr

Inicia sesión para acceder a tus salas

- +
); } diff --git a/app/(protected)/dashboard/room/[id]/page.tsx b/app/(protected)/dashboard/room/[id]/page.tsx index f34469b..b7e5c5e 100644 --- a/app/(protected)/dashboard/room/[id]/page.tsx +++ b/app/(protected)/dashboard/room/[id]/page.tsx @@ -1,111 +1,38 @@ +'use client'; +import { useQuery } from '@tanstack/react-query'; import MessagesList from '../_components/MessagesList'; -import { IMessage } from '@/app/_lib/_interfaces/IMessage'; +import LoadingEmoji from '@/app/_components/LoadingEmoji'; +import { use } from 'react'; +import { getRoomMessages } from '../../_apis/messages'; +import { getRoomById } from '../../_apis/rooms'; interface RouterProps { params: Promise<{ id: string; }>; } -export default async function RoomPage({ params }: RouterProps) { - const { id } = await params; - const messages = await fetchMessages(); +export default function RoomPage({ params }: RouterProps) { + const id = use(params).id; + + const { data: messages, isLoading: messagesLoading } = useQuery({ + queryKey: [`room-messages-${id}`], + queryFn: () => getRoomMessages(id), + }); + + const { data: room, isLoading: roomLoading } = useQuery({ + queryKey: [`room-${id}`], + queryFn: () => getRoomById(id), + }); + + if (messagesLoading || roomLoading) return ; + return (
-

Nombre de la sala {id}

+

{room?.name}

Pública

- + {room && }
); } - -async function fetchMessages(): Promise { - // Simula una llamada a una API externa - return new Promise(resolve => { - setTimeout(() => { - resolve([ - { - id: '1', - send_by: 'Danils', - send_at: new Date().toISOString(), - content: 'Hola, bienvenido a la sala!', - }, - { - id: '2', - send_by: 'Valtimore', - send_at: new Date().toISOString(), - content: 'Hola! ¿Cómo están?', - }, - { - id: '3', - send_by: 'Zers', - send_at: new Date().toISOString(), - content: '¡Todo bien! ¿Y tú?', - }, - { - id: '4', - send_by: 'Liferip', - send_at: new Date().toISOString(), - content: '¡Genial! ¿Qué tal el clima?', - }, - { - id: '5', - send_by: 'LIFERIP', - send_at: new Date().toISOString(), - content: 'Sigue lloviendo, pero no importa.', - }, - { - id: '6', - send_by: 'user1', - send_at: new Date().toISOString(), - content: '¡Qué suerte! Aquí hace mucho calor.', - }, - { - id: '7', - send_by: 'user2', - send_at: new Date().toISOString(), - content: '¿Alguien sabe qué hora es?', - }, - { - id: '8', - send_by: 'user1', - send_at: new Date().toISOString(), - content: 'Son las 3 PM.', - }, - { - id: '9', - send_by: 'user2', - send_at: new Date().toISOString(), - content: 'Gracias!', - }, - { - id: '10', - send_by: 'user1', - send_at: new Date().toISOString(), - content: 'De nada!', - }, - { - id: '11', - send_by: 'user2', - send_at: new Date().toISOString(), - content: '¿Alguien quiere jugar un juego?', - }, - { - id: '12', - send_by: 'user1', - send_at: new Date().toISOString(), - content: '¡Sí! ¿Qué juego?', - }, - { - id: '13', - send_by: 'user2', - send_at: new Date().toISOString(), - content: - 'Podemos jugar a adivinar el tamaño de mi aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aa a a a aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + - 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', - }, - ]); - }, 2000); - }); -} diff --git a/app/(protected)/dashboard/room/_components/CreateChat.tsx b/app/(protected)/dashboard/room/_components/CreateChat.tsx new file mode 100644 index 0000000..bde8d24 --- /dev/null +++ b/app/(protected)/dashboard/room/_components/CreateChat.tsx @@ -0,0 +1,109 @@ +'use client'; + +import { CancelIcon } from '@/app/_ui/icons'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useState } from 'react'; +import { createRoom } from '../../_apis/rooms'; +import { ICreateRoom } from '@/app/_lib/_interfaces/IRoom'; +import { auth } from '@/app/_lib/_firebase/firebase.config'; + +export default function CreateChat() { + const [createMode, setCreateMode] = useState(false); + + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: (data: ICreateRoom) => createRoom(data), + onSuccess: res => { + if (!res) return; + queryClient.invalidateQueries({ queryKey: [`all-rooms-dashboard`] }); + setCreateMode(false); + }, + }); + + const handleCreateRoom = (e: React.FormEvent) => { + e.preventDefault(); + const form = e.currentTarget; + const name = (form.elements.namedItem('name') as HTMLInputElement).value; + const description = (form.elements.namedItem('description') as HTMLTextAreaElement).value; + const isPrivate = (form.elements.namedItem('isPrivate') as HTMLSelectElement).value === 'true'; + const data: ICreateRoom = { + name, + description, + isPrivate, + userIds: [auth.currentUser?.uid as string], + }; + mutation.mutate(data); + }; + + return ( +
+ {createMode ? ( +
+
+ + +
+
+ +