@@ -20,11 +20,13 @@ export default function Home() {
diff --git a/app/(nologged)/login/page.tsx b/app/(nologged)/login/page.tsx
deleted file mode 100644
index af873f5..0000000
--- a/app/(nologged)/login/page.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-'use client';
-import Image from 'next/image';
-import { useState } from 'react';
-
-export default function Login() {
- const [mode, setMode] = useState('login');
-
- return (
-
-
-
-
-
-
- Mantente Comunicado en Cualquier Lugar
- Inicia sesión para acceder a tus salas
-
-
-
-
¿Aún no tienes una cuenta?
-
-
-
-
-
-
- Regístrate y Conecta
- Podrás guardar tus conversaciones y salas
-
-
-
-
¿Ya tienes una cuenta?
-
-
-
-
-
-
-
- );
-}
diff --git a/app/(protected)/dashboard/_components/RoomList.tsx b/app/(protected)/dashboard/_components/RoomList.tsx
new file mode 100644
index 0000000..43e114d
--- /dev/null
+++ b/app/(protected)/dashboard/_components/RoomList.tsx
@@ -0,0 +1,133 @@
+import { CancelIcon, DoorBellIcon } from '@/app/_ui/icons';
+import { Tooltip } from '@mui/material';
+import Link from 'next/link';
+
+const salas = [
+ {
+ id: 1,
+ name: 'Sala 1',
+ created_by: 'Usuario 1',
+ created_at: '2023-10-01',
+ },
+ {
+ id: 2,
+ name: 'Sala 2',
+ created_by: 'Usuario 2',
+ created_at: '2023-10-02',
+ },
+ {
+ id: 3,
+ name: 'Sala 3',
+ created_by: 'Usuario 3',
+ created_at: '2023-10-03',
+ },
+ {
+ id: 4,
+ name: 'Sala 4',
+ created_by: 'Usuario 4',
+ created_at: '2023-10-04',
+ },
+ {
+ id: 5,
+ name: 'Sala 5',
+ created_by: 'Usuario 5',
+ created_at: '2023-10-05',
+ },
+ {
+ id: 6,
+ name: 'Sala 6',
+ created_by: 'Usuario 6',
+ created_at: '2023-10-06',
+ },
+ {
+ id: 7,
+ name: 'Sala 7',
+ created_by: 'Usuario 7',
+ created_at: '2023-10-07',
+ },
+ {
+ id: 8,
+ name: 'Sala 8',
+ created_by: 'Usuario 8',
+ created_at: '2023-10-08',
+ },
+ {
+ id: 9,
+ name: 'Sala 9',
+ created_by: 'Usuario 9',
+ created_at: '2023-10-09',
+ },
+ {
+ id: 10,
+ name: 'Sala 10',
+ created_by: 'Usuario 10',
+ created_at: '2023-10-10',
+ },
+ {
+ id: 11,
+ name: 'Sala 11',
+ created_by: 'Usuario 11',
+ created_at: '2023-10-11',
+ },
+ {
+ id: 12,
+ name: 'Sala 12',
+ created_by: 'Usuario 12',
+ created_at: '2023-10-12',
+ },
+ {
+ id: 13,
+ name: 'Sala 13',
+ created_by: 'Usuario 13',
+ created_at: '2023-10-13',
+ },
+ {
+ id: 14,
+ name: 'Sala 14',
+ created_by: 'Usuario 14',
+ created_at: '2023-10-14',
+ },
+ {
+ id: 15,
+ name: 'Sala 15',
+ created_by: 'Usuario 15',
+ created_at: '2023-10-14',
+ },
+];
+
+export default function RoomList() {
+ return (
+
+ {salas.map(sala => (
+
+
+ {sala.name}
+
+
+ Creado por: {sala.created_by}
+
+
+ Fecha: {sala.created_at}
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/app/(protected)/dashboard/page.tsx b/app/(protected)/dashboard/page.tsx
new file mode 100644
index 0000000..63049b6
--- /dev/null
+++ b/app/(protected)/dashboard/page.tsx
@@ -0,0 +1,14 @@
+import RoomList from './_components/RoomList';
+
+export default function Dashboard() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/app/(protected)/dashboard/room/[id]/loading.tsx b/app/(protected)/dashboard/room/[id]/loading.tsx
new file mode 100644
index 0000000..b8fd4ed
--- /dev/null
+++ b/app/(protected)/dashboard/room/[id]/loading.tsx
@@ -0,0 +1,7 @@
+export default function RoomLoading() {
+ return (
+
+ );
+}
diff --git a/app/(protected)/dashboard/room/[id]/page.tsx b/app/(protected)/dashboard/room/[id]/page.tsx
new file mode 100644
index 0000000..40623e6
--- /dev/null
+++ b/app/(protected)/dashboard/room/[id]/page.tsx
@@ -0,0 +1,111 @@
+import MessagesList from '../_components/MessagesList';
+import { IMessage } from '@/app/_interfaces/IMessage';
+
+interface RouterProps {
+ params: Promise<{
+ id: string;
+ }>;
+}
+export default async function RoomPage({ params }: RouterProps) {
+ const { id } = await params;
+ const messages = await fetchMessages();
+ return (
+
+
+
Nombre de la sala {id}
+
Pública
+
+
+
+ );
+}
+
+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/Message.tsx b/app/(protected)/dashboard/room/_components/Message.tsx
new file mode 100644
index 0000000..664211d
--- /dev/null
+++ b/app/(protected)/dashboard/room/_components/Message.tsx
@@ -0,0 +1,45 @@
+import { IMessage } from '@/app/_interfaces/IMessage';
+
+interface Props {
+ message: IMessage;
+}
+
+const colorList = [
+ 'rgba(255, 99, 132, 0.1)', // Rojo opaco
+ 'rgba(54, 162, 235, 0.1)', // Azul opaco
+ 'rgba(255, 159, 64, 0.1)', // Naranja opaco
+ 'rgba(75, 192, 192, 0.1)', // Verde opaco
+ 'rgba(153, 102, 255, 0.1)', // Púrpura opaco
+ 'rgba(255, 205, 86, 0.1)', // Amarillo opaco
+ 'rgba(255, 99, 71, 0.1)', // Tomate opaco
+ 'rgba(173, 216, 230, 0.1)', // Azul claro opaco
+ 'rgba(240, 128, 128, 0.1)', // Rosa claro opaco
+ 'rgba(144, 238, 144, 0.1)', // Verde claro opaco
+];
+
+function stringToRGBa(str: string) {
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ hash += str.charCodeAt(i);
+ }
+ const index = hash % colorList.length;
+ return colorList[index];
+}
+
+export default function Message({ message }: Props) {
+ const color = stringToRGBa(message.send_by);
+ return (
+
+
{message.send_by}
+
{message.content}
+
+ {message.send_at !== 'pending' &&
+ message.send_at.toString().split('T')[1].split('.')[0].split(':').slice(0, 2).join(':')}
+ {message.send_at === 'pending' && '⏱'}
+
+
+ );
+}
diff --git a/app/(protected)/dashboard/room/_components/MessageInput.tsx b/app/(protected)/dashboard/room/_components/MessageInput.tsx
new file mode 100644
index 0000000..287f9ef
--- /dev/null
+++ b/app/(protected)/dashboard/room/_components/MessageInput.tsx
@@ -0,0 +1,70 @@
+'use client';
+
+import { SendIcon } from '@/app/_ui/icons';
+import { useEffect, useRef, useState } from 'react';
+
+interface Props {
+ onSend: (msg: string) => void;
+ bottomRef: React.RefObject;
+}
+
+export default function MessageInput({ onSend, bottomRef }: Props) {
+ const [message, setMessage] = useState('');
+ const textareaRef = useRef(null);
+
+ useEffect(() => {
+ const textarea = textareaRef.current;
+ if (textarea) {
+ // Resetear altura para cálculo
+ textarea.style.height = 'auto';
+ // Obtener altura máxima del CSS (max-h-48 = 192px)
+ const maxHeight = parseInt(getComputedStyle(textarea).maxHeight, 10);
+ // Aplicar la menor altura entre contenido y máximo
+ textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight) - 10}px`;
+ }
+ }, [message]);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!message.trim()) return;
+ onSend(message.trim());
+ setTimeout(() => {
+ bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, 150);
+ setMessage('');
+ };
+
+ const onKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ if (message.trim()) {
+ onSend(message.trim());
+ setMessage('');
+ }
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/app/(protected)/dashboard/room/_components/MessagesList.tsx b/app/(protected)/dashboard/room/_components/MessagesList.tsx
new file mode 100644
index 0000000..72b9cd9
--- /dev/null
+++ b/app/(protected)/dashboard/room/_components/MessagesList.tsx
@@ -0,0 +1,32 @@
+'use client';
+
+import { useChatSocket } from '@/app/_hooks/useChatSocket';
+import { IMessage } from '@/app/_interfaces/IMessage';
+import { useEffect, useRef, useState } from 'react';
+import Message from './Message';
+import useIsBottom from '@/app/_hooks/useScroll';
+import MessageInput from './MessageInput';
+
+interface Props {
+ room_id: string;
+ initial_messages: IMessage[];
+}
+
+export default function MessagesList({ room_id, initial_messages }: Props) {
+ const { messages, sendMessage } = useChatSocket({ room_id, initial_messages });
+ const { containerRef, bottomRef } = useIsBottom({ items: messages });
+
+ return (
+ <>
+
+ {messages.map(msg => (
+
+ ))}
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/(protected)/dashboard/room/layout.tsx b/app/(protected)/dashboard/room/layout.tsx
new file mode 100644
index 0000000..8922623
--- /dev/null
+++ b/app/(protected)/dashboard/room/layout.tsx
@@ -0,0 +1,14 @@
+import AsideRoom from '@/app/_components/AsideRoom';
+
+export default function RoomLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+ );
+}
diff --git a/app/(protected)/layout.tsx b/app/(protected)/layout.tsx
new file mode 100644
index 0000000..9858cb2
--- /dev/null
+++ b/app/(protected)/layout.tsx
@@ -0,0 +1,16 @@
+import AsideDashboard from '../_components/AsideDashboard';
+
+export default function ProtectedLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+ );
+}
diff --git a/app/_components/AsideDashboard.tsx b/app/_components/AsideDashboard.tsx
new file mode 100644
index 0000000..a82e154
--- /dev/null
+++ b/app/_components/AsideDashboard.tsx
@@ -0,0 +1,110 @@
+'use client';
+import Image from 'next/image';
+import Link from 'next/link';
+import { AnonymousMaskIcon, GearIcon, InfoIcon, LinkIcon, RoomIcon } from '../_ui/icons';
+import { useIsMobileLarge } from '../_hooks/useIsMobileLarge';
+
+export default function AsideDashboard() {
+ const { isMobileLg } = useIsMobileLarge();
+
+ return (
+ <>
+ {/* Desktop aside */}
+ {!isMobileLg && (
+
+ )}
+ {/* Mobile aside */}
+ {isMobileLg && (
+
+ )}
+ >
+ );
+}
diff --git a/app/_components/AsideRoom.tsx b/app/_components/AsideRoom.tsx
new file mode 100644
index 0000000..ef7c04d
--- /dev/null
+++ b/app/_components/AsideRoom.tsx
@@ -0,0 +1,10 @@
+export default function AsideRoom() {
+ return (
+
+ );
+}
diff --git a/app/_components/Footer.tsx b/app/_components/Footer.tsx
index 28eb55d..86d08a7 100644
--- a/app/_components/Footer.tsx
+++ b/app/_components/Footer.tsx
@@ -1,31 +1,31 @@
-const Footer = () => {
+export default function Footer() {
return (
-
+
ParChat
Comunica, Conecta y Transmite
-
-
+
+
Stacks
-
+
- Sobre Nosotros
- Otros Proyectos
- GitHub
-
+
Info
-
+
- Información
- Salas
- Registro
-
+
Contacto
-
+
- Tuluá, Valle
- Colombia
- 3000000000
@@ -33,12 +33,10 @@ const Footer = () => {
-
+
© 2025 ParChat. Todos los derechos reservados.
Hecho con amor por el equipo de Rippio ♥
);
-};
-
-export default Footer;
+}
diff --git a/app/_components/Header.tsx b/app/_components/Header.tsx
index ced1298..e680354 100644
--- a/app/_components/Header.tsx
+++ b/app/_components/Header.tsx
@@ -1,19 +1,61 @@
+'use client';
import Image from 'next/image';
import Link from 'next/link';
+import { useState } from 'react';
export default function Header() {
+ const [opened, setOpened] = useState(false);
return (
+ {/* Mobile */}
+
+
+
+
+
+
+
+ Parchemos
+
+ Invitado
+
+
+
+
+
+ -
+ Información
+
+ -
+ Salas
+
+ -
+ Contacto
+
+
+
+
+
{/* Desktop */}
-
+
- - Información
- - Salas
- - Contacto
+ -
+ Información
+
+ -
+ Salas
+
+ -
+ Contacto
+
diff --git a/app/_components/LoadingEmoji.tsx b/app/_components/LoadingEmoji.tsx
new file mode 100644
index 0000000..66402df
--- /dev/null
+++ b/app/_components/LoadingEmoji.tsx
@@ -0,0 +1,7 @@
+export default function LoadingEmoji() {
+ return (
+
+ );
+}
diff --git a/app/_hooks/useChatSocket.tsx b/app/_hooks/useChatSocket.tsx
new file mode 100644
index 0000000..409c221
--- /dev/null
+++ b/app/_hooks/useChatSocket.tsx
@@ -0,0 +1,81 @@
+import { useEffect, useRef, useState } from 'react';
+import { IMessage } from '../_interfaces/IMessage';
+
+interface Props {
+ room_id: string;
+ initial_messages: IMessage[];
+}
+
+export function useChatSocket({ initial_messages }: Props) {
+ const [messages, setMessages] = useState
(initial_messages);
+ const socketRef = useRef(null);
+
+ // useEffect(() => {
+ // const socket = new WebSocket(`ws://localhost:8080/ws/${room_id}`);
+ // socketRef.current = socket;
+
+ // socket.onmessage = event => {
+ // setMessages(prev => [...prev, event.data]);
+ // };
+
+ // socket.onerror = err => {
+ // console.error('WebSocket error:', err);
+ // };
+
+ // socket.onclose = () => {
+ // console.log('WebSocket cerrado');
+ // };
+
+ // return () => {
+ // socket.close();
+ // };
+ // }, [room_id]);
+
+ useEffect(() => {
+ // Simula WebSocket: llega un nuevo mensaje cada 5 segundos
+ const interval = setInterval(() => {
+ setMessages(prev => [
+ ...prev,
+ {
+ id: String(prev.length + 1),
+ send_by: 'system',
+ send_at: new Date().toISOString(),
+ content: `Mensaje simulado ${prev.length + 1}`,
+ },
+ ]);
+ }, 2000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ const sendMessage = (msg: string) => {
+ // if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
+ // socketRef.current.send(msg);
+ // }
+ setMessages(prev => [
+ ...prev,
+ {
+ id: String(new Date().getTime()),
+ send_by: 'Danils',
+ send_at: 'pending',
+ content: msg,
+ },
+ ]);
+
+ // Simula la respuesta del servidor
+ setTimeout(() => {
+ setMessages(prev => {
+ return prev.map(msg =>
+ msg.send_at === 'pending'
+ ? {
+ ...msg,
+ send_at: new Date().toISOString(),
+ }
+ : msg
+ );
+ });
+ }, 2000);
+ };
+
+ return { messages, sendMessage };
+}
diff --git a/app/_hooks/useIsMobileLarge.tsx b/app/_hooks/useIsMobileLarge.tsx
new file mode 100644
index 0000000..b1d8434
--- /dev/null
+++ b/app/_hooks/useIsMobileLarge.tsx
@@ -0,0 +1,22 @@
+'use client';
+import { useEffect, useState } from 'react';
+
+export const useIsMobileLarge = () => {
+ const [isMobileLg, setIsMobileLg] = useState(false);
+ useEffect(() => {
+ const handleResize = () => {
+ if (window.innerWidth < 1024) {
+ setIsMobileLg(true);
+ } else {
+ setIsMobileLg(false);
+ }
+ };
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ return { isMobileLg };
+};
diff --git a/app/_hooks/useScroll.tsx b/app/_hooks/useScroll.tsx
new file mode 100644
index 0000000..1b7075f
--- /dev/null
+++ b/app/_hooks/useScroll.tsx
@@ -0,0 +1,32 @@
+import { useEffect, useRef, useState } from 'react';
+interface Props {
+ items: T[];
+}
+export default function useIsBottom({ items }: Props) {
+ const containerRef = useRef(null);
+ const bottomRef = useRef(null);
+ const [isInBottom, setIsInBottom] = useState(true);
+
+ // Detectar si el usuario scrollea manualmente
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+
+ const handleScroll = () => {
+ const nearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 50;
+ setIsInBottom(nearBottom);
+ };
+
+ container.addEventListener('scroll', handleScroll);
+ return () => container.removeEventListener('scroll', handleScroll);
+ }, []);
+
+ // Auto scroll solo si el usuario está al fondo
+ useEffect(() => {
+ if (isInBottom) {
+ bottomRef.current?.scrollIntoView({ behavior: 'auto' });
+ }
+ }, [items, isInBottom]);
+
+ return { containerRef, bottomRef };
+}
diff --git a/app/_interfaces/IMessage.tsx b/app/_interfaces/IMessage.tsx
new file mode 100644
index 0000000..53c1be9
--- /dev/null
+++ b/app/_interfaces/IMessage.tsx
@@ -0,0 +1,6 @@
+export interface IMessage {
+ id: string;
+ send_by: string;
+ send_at: string;
+ content: string;
+}
diff --git a/app/_ui/fonts.ts b/app/_ui/fonts.ts
deleted file mode 100644
index 7a93beb..0000000
--- a/app/_ui/fonts.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Quicksand, Raleway } from 'next/font/google';
-
-export const quicksand = Quicksand({
- subsets: ['latin'],
- weight: ['300', '400', '500', '600', '700'],
-});
-
-export const raleway = Raleway({
- subsets: ['latin'],
- weight: ['300', '400', '500', '600', '700'],
-});
diff --git a/app/_ui/globals.css b/app/_ui/globals.css
index 54a9f44..9293207 100644
--- a/app/_ui/globals.css
+++ b/app/_ui/globals.css
@@ -5,8 +5,12 @@
--color-babyblue: #a4b6dd;
--color-lightpurple: #a58ada;
--color-purple: #6e52a3;
+ --color-purple-2: #262240;
+ --color-semidarkpurple: #392f4d;
+ --color-darkpurple: #251d35;
--color-mediumblue: #11142b;
--color-darkblue: #030512;
+ --color-darkblue-2: #151324;
}
.bg-blue {
@@ -25,6 +29,18 @@
background-color: var(--color-purple);
}
+.bg-purple-2 {
+ background-color: var(--color-purple-2);
+}
+
+.bg-semidarkpurple {
+ background-color: var(--color-semidarkpurple);
+}
+
+.bg-darkpurple {
+ background-color: var(--color-darkpurple);
+}
+
.text-purple {
color: var(--color-lightpurple);
}
@@ -33,6 +49,23 @@
background-color: var(--color-darkblue);
}
+.bg-darkblue-2 {
+ background-color: var(--color-darkblue-2);
+}
+
.bg-mediumblue {
background-color: var(--color-mediumblue);
}
+
+body {
+ scrollbar-width: thin;
+ scrollbar-color: #6e52a3 transparent;
+}
+
+.font-raleway {
+ font-family: 'Raleway', sans-serif;
+}
+
+.font-quicksand {
+ font-family: 'Quicksand', sans-serif;
+}
diff --git a/app/_ui/icons.tsx b/app/_ui/icons.tsx
new file mode 100644
index 0000000..084c537
--- /dev/null
+++ b/app/_ui/icons.tsx
@@ -0,0 +1,198 @@
+import { SVGProps } from 'react';
+
+export const RoomIcon = (props: SVGProps) => (
+
+);
+
+export const AnonymousMaskIcon = (props: SVGProps) => (
+
+);
+
+export const GearIcon = (props: SVGProps) => (
+
+);
+
+export const InfoIcon = (props: SVGProps) => (
+
+);
+
+export const LinkIcon = (props: SVGProps) => (
+
+);
+
+export const DoorBellIcon = (props: SVGProps) => (
+
+);
+
+export const CancelIcon = (props: SVGProps) => (
+
+);
+
+export const SendIcon = (props: SVGProps) => (
+
+);
diff --git a/app/(nologged)/layout.tsx b/app/layout.tsx
similarity index 55%
rename from app/(nologged)/layout.tsx
rename to app/layout.tsx
index 89ed6e0..4a16b6d 100644
--- a/app/(nologged)/layout.tsx
+++ b/app/layout.tsx
@@ -1,7 +1,18 @@
import '@/app/_ui/globals.css';
-import { raleway } from '../_ui/fonts';
-import Header from '../_components/Header';
-import Footer from '../_components/Footer';
+import '@fontsource/quicksand/300.css';
+import '@fontsource/quicksand/400.css';
+import '@fontsource/quicksand/500.css';
+import '@fontsource/quicksand/600.css';
+import '@fontsource/quicksand/700.css';
+import '@fontsource/raleway/300.css';
+import '@fontsource/raleway/400.css';
+import '@fontsource/raleway/500.css';
+import '@fontsource/raleway/600.css';
+import '@fontsource/raleway/700.css';
+import '@fontsource/roboto/300.css';
+import '@fontsource/roboto/400.css';
+import '@fontsource/roboto/500.css';
+import '@fontsource/roboto/700.css';
import type { Metadata } from 'next';
export const metadata: Metadata = {
@@ -28,13 +39,7 @@ export default function RootLayout({
}>) {
return (
-
-
- {children}
-
-
+ {children}
);
}
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..c6b97ab
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,12 @@
+services:
+ frontend-dev:
+ build:
+ context: .
+ dockerfile: Dockerfile.dev
+ ports:
+ - '3000:3000'
+ volumes:
+ - .:/home/frontend-dev
+ - /home/frontend-dev/node_modules
+ environment:
+ - NODE_ENV=development
diff --git a/dockerfile.dev b/dockerfile.dev
new file mode 100644
index 0000000..2f3b06e
--- /dev/null
+++ b/dockerfile.dev
@@ -0,0 +1,15 @@
+FROM node:22-slim
+
+RUN mkdir -p /home/frontend-dev
+
+WORKDIR /home/frontend-dev
+
+COPY package.json package-lock.json ./
+RUN npm ci
+
+COPY . .
+COPY next.config.ts ./next.config.ts
+
+EXPOSE 3000
+
+CMD ["npm", "run", "dev"]
diff --git a/next.config.ts b/next.config.ts
index 5e891cf..825249b 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,7 +1,23 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
/* config options here */
+ images: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: 'picsum.photos',
+ },
+ ],
+ },
+ webpackDevMiddleware: (config: any) => {
+ config.watchOptions = {
+ poll: 1000,
+ aggregateTimeout: 300,
+ };
+ return config;
+ },
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index e2e996f..da15903 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,12 @@
"name": "parchat",
"version": "0.1.0",
"dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.0",
+ "@fontsource/quicksand": "^5.2.6",
+ "@fontsource/raleway": "^5.2.5",
+ "@fontsource/roboto": "^5.2.5",
+ "@mui/material": "^7.0.2",
"next": "^15.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
@@ -41,6 +47,137 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
+ "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "dependencies": {
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+ "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
+ "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
+ "dependencies": {
+ "@babel/types": "^7.27.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+ "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
+ "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
+ "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.27.0",
+ "@babel/parser": "^7.27.0",
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@emnapi/core": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz",
@@ -71,6 +208,139 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz",
+ "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
+ "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
@@ -199,6 +469,32 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fontsource/quicksand": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/@fontsource/quicksand/-/quicksand-5.2.6.tgz",
+ "integrity": "sha512-MI1EO6ENXbmAxzwkxhLdCUKLLP6SGQ7IHLktuwOcS7llvK4Ni6QiqFCHZg40wpOfR0Z0FMCOJAqykf3QSqYpFA==",
+ "license": "OFL-1.1",
+ "funding": {
+ "url": "https://github.com/sponsors/ayuhito"
+ }
+ },
+ "node_modules/@fontsource/raleway": {
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/@fontsource/raleway/-/raleway-5.2.5.tgz",
+ "integrity": "sha512-gVFV8Yi8Y5h8sQw0U/F6TQID53DVIWPuRxqBLOBxwLbQma42JAwqTZ+pi3eDjpm20/rjS2XoVJH5hnMx1KFB+g==",
+ "license": "OFL-1.1",
+ "funding": {
+ "url": "https://github.com/sponsors/ayuhito"
+ }
+ },
+ "node_modules/@fontsource/roboto": {
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.2.5.tgz",
+ "integrity": "sha512-70r2UZ0raqLn5W+sPeKhqlf8wGvUXFWlofaDlcbt/S3d06+17gXKr3VNqDODB0I1ASme3dGT5OJj9NABt7OTZQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ayuhito"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -617,6 +913,259 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mui/core-downloads-tracker": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.0.2.tgz",
+ "integrity": "sha512-TfeFU9TgN1N06hyb/pV/63FfO34nijZRMqgHk0TJ3gkl4Fbd+wZ73+ZtOd7jag6hMmzO9HSrBc6Vdn591nhkAg==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.2.tgz",
+ "integrity": "sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==",
+ "dependencies": {
+ "@babel/runtime": "^7.27.0",
+ "@mui/core-downloads-tracker": "^7.0.2",
+ "@mui/system": "^7.0.2",
+ "@mui/types": "^7.4.1",
+ "@mui/utils": "^7.0.2",
+ "@popperjs/core": "^2.11.8",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.1.0",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@mui/material-pigment-css": "^7.0.2",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@mui/material-pigment-css": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/react-is": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
+ "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg=="
+ },
+ "node_modules/@mui/private-theming": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.0.2.tgz",
+ "integrity": "sha512-6lt8heDC9wN8YaRqEdhqnm0cFCv08AMf4IlttFvOVn7ZdKd81PNpD/rEtPGLLwQAFyyKSxBG4/2XCgpbcdNKiA==",
+ "dependencies": {
+ "@babel/runtime": "^7.27.0",
+ "@mui/utils": "^7.0.2",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.0.2.tgz",
+ "integrity": "sha512-11Bt4YdHGlh7sB8P75S9mRCUxTlgv7HGbr0UKz6m6Z9KLeiw1Bm9y/t3iqLLVMvSHYB6zL8X8X+LmfTE++gyBw==",
+ "dependencies": {
+ "@babel/runtime": "^7.27.0",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.0.2.tgz",
+ "integrity": "sha512-yFUraAWYWuKIISPPEVPSQ1NLeqmTT4qiQ+ktmyS8LO/KwHxB+NNVOacEZaIofh5x1NxY8rzphvU5X2heRZ/RDA==",
+ "dependencies": {
+ "@babel/runtime": "^7.27.0",
+ "@mui/private-theming": "^7.0.2",
+ "@mui/styled-engine": "^7.0.2",
+ "@mui/types": "^7.4.1",
+ "@mui/utils": "^7.0.2",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.1.tgz",
+ "integrity": "sha512-gUL8IIAI52CRXP/MixT1tJKt3SI6tVv4U/9soFsTtAsHzaJQptZ42ffdHZV3niX1ei0aUgMvOxBBN0KYqdG39g==",
+ "dependencies": {
+ "@babel/runtime": "^7.27.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.0.2.tgz",
+ "integrity": "sha512-72gcuQjPzhj/MLmPHLCgZjy2VjOH4KniR/4qRtXTTXIEwbkgcN+Y5W/rC90rWtMmZbjt9svZev/z+QHUI4j74w==",
+ "dependencies": {
+ "@babel/runtime": "^7.27.0",
+ "@mui/types": "^7.4.1",
+ "@types/prop-types": "^15.7.14",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.1.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils/node_modules/react-is": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
+ "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg=="
+ },
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.7.tgz",
@@ -819,6 +1368,15 @@
"url": "https://opencollective.com/pkgr"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1104,11 +1662,20 @@
"undici-types": "~6.19.2"
}
},
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.14",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
+ "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
+ },
"node_modules/@types/react": {
"version": "19.0.12",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz",
"integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==",
- "dev": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -1122,6 +1689,14 @@
"@types/react": "^19.0.0"
}
},
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz",
@@ -1794,6 +2369,20 @@
"node": ">= 0.4"
}
},
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1916,7 +2505,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -1961,6 +2549,14 @@
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -2008,6 +2604,26 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2025,8 +2641,7 @@
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -2089,7 +2704,6 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
- "dev": true,
"dependencies": {
"ms": "^2.1.3"
},
@@ -2163,6 +2777,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2202,6 +2825,19 @@
"node": ">=10.13.0"
}
},
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/error-ex/node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
"node_modules/es-abstract": {
"version": "1.23.9",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
@@ -2381,7 +3017,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -2920,6 +3555,11 @@
"node": ">=8"
}
},
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -2987,7 +3627,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -3230,7 +3869,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -3238,6 +3876,14 @@
"node": ">= 0.4"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3251,7 +3897,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -3384,7 +4029,6 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "dev": true,
"dependencies": {
"hasown": "^2.0.2"
},
@@ -3696,8 +4340,7 @@
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "4.1.0",
@@ -3711,12 +4354,28 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true
},
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -4024,6 +4683,11 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -4049,7 +4713,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
@@ -4112,8 +4775,7 @@
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/nanoid": {
"version": "3.3.11",
@@ -4237,7 +4899,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4416,7 +5077,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -4424,6 +5084,23 @@
"node": ">=6"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4445,8 +5122,15 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "engines": {
+ "node": ">=8"
+ }
},
"node_modules/picocolors": {
"version": "1.1.1",
@@ -4548,7 +5232,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -4606,8 +5289,22 @@
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
@@ -4631,6 +5328,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -4655,7 +5357,6 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "dev": true,
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
@@ -4675,7 +5376,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
"engines": {
"node": ">=4"
}
@@ -5001,6 +5701,14 @@
"is-arrayish": "^0.3.1"
}
},
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5173,6 +5881,11 @@
}
}
},
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -5189,7 +5902,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -5582,6 +6294,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 0cbfe05..658d3ae 100644
--- a/package.json
+++ b/package.json
@@ -3,13 +3,20 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev --turbopack",
- "build": "next build",
+ "devt": "next dev --turbopack",
+ "dev": "next dev",
+ "build": "next build --no-lint",
"start": "next start",
"lint": "next lint",
"format": "prettier --write ."
},
"dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.0",
+ "@fontsource/quicksand": "^5.2.6",
+ "@fontsource/raleway": "^5.2.5",
+ "@fontsource/roboto": "^5.2.5",
+ "@mui/material": "^7.0.2",
"next": "^15.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
diff --git a/public/fonts/Quicksand/OFL.txt b/public/fonts/Quicksand/OFL.txt
new file mode 100644
index 0000000..128334c
--- /dev/null
+++ b/public/fonts/Quicksand/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2011 The Quicksand Project Authors (https://github.com/andrew-paglinawan/QuicksandFamily), with Reserved Font Name “Quicksand”.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/Quicksand/Quicksand-VariableFont_wght.ttf b/public/fonts/Quicksand/Quicksand-VariableFont_wght.ttf
new file mode 100644
index 0000000..44ecad8
Binary files /dev/null and b/public/fonts/Quicksand/Quicksand-VariableFont_wght.ttf differ
diff --git a/public/fonts/Quicksand/README.txt b/public/fonts/Quicksand/README.txt
new file mode 100644
index 0000000..33d9d26
--- /dev/null
+++ b/public/fonts/Quicksand/README.txt
@@ -0,0 +1,67 @@
+Quicksand Variable Font
+=======================
+
+This download contains Quicksand as both a variable font and static fonts.
+
+Quicksand is a variable font with this axis:
+ wght
+
+This means all the styles are contained in a single file:
+ Quicksand-VariableFont_wght.ttf
+
+If your app fully supports variable fonts, you can now pick intermediate styles
+that aren’t available as static fonts. Not all apps support variable fonts, and
+in those cases you can use the static font files for Quicksand:
+ static/Quicksand-Light.ttf
+ static/Quicksand-Regular.ttf
+ static/Quicksand-Medium.ttf
+ static/Quicksand-SemiBold.ttf
+ static/Quicksand-Bold.ttf
+
+Get started
+-----------
+
+1. Install the font files you want to use
+
+2. Use your app's font picker to view the font family and all the
+available styles
+
+Learn more about variable fonts
+-------------------------------
+
+ https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
+ https://variablefonts.typenetwork.com
+ https://medium.com/variable-fonts
+
+In desktop apps
+
+ https://theblog.adobe.com/can-variable-fonts-illustrator-cc
+ https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
+
+Online
+
+ https://developers.google.com/fonts/docs/getting_started
+ https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
+ https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
+
+Installing fonts
+
+ MacOS: https://support.apple.com/en-us/HT201749
+ Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
+ Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
+
+Android Apps
+
+ https://developers.google.com/fonts/docs/android
+ https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
+
+License
+-------
+Please read the full license text (OFL.txt) to understand the permissions,
+restrictions and requirements for usage, redistribution, and modification.
+
+You can use them in your products & projects – print or digital,
+commercial or otherwise.
+
+This isn't legal advice, please consider consulting a lawyer and see the full
+license for all details.
diff --git a/public/fonts/Quicksand/static/Quicksand-Bold.ttf b/public/fonts/Quicksand/static/Quicksand-Bold.ttf
new file mode 100644
index 0000000..0106805
Binary files /dev/null and b/public/fonts/Quicksand/static/Quicksand-Bold.ttf differ
diff --git a/public/fonts/Quicksand/static/Quicksand-Light.ttf b/public/fonts/Quicksand/static/Quicksand-Light.ttf
new file mode 100644
index 0000000..fcf86af
Binary files /dev/null and b/public/fonts/Quicksand/static/Quicksand-Light.ttf differ
diff --git a/public/fonts/Quicksand/static/Quicksand-Medium.ttf b/public/fonts/Quicksand/static/Quicksand-Medium.ttf
new file mode 100644
index 0000000..afca2d9
Binary files /dev/null and b/public/fonts/Quicksand/static/Quicksand-Medium.ttf differ
diff --git a/public/fonts/Quicksand/static/Quicksand-Regular.ttf b/public/fonts/Quicksand/static/Quicksand-Regular.ttf
new file mode 100644
index 0000000..c03548a
Binary files /dev/null and b/public/fonts/Quicksand/static/Quicksand-Regular.ttf differ
diff --git a/public/fonts/Quicksand/static/Quicksand-SemiBold.ttf b/public/fonts/Quicksand/static/Quicksand-SemiBold.ttf
new file mode 100644
index 0000000..83475ea
Binary files /dev/null and b/public/fonts/Quicksand/static/Quicksand-SemiBold.ttf differ