diff --git a/COMIT.bat b/COMIT.bat
new file mode 100644
index 0000000..fff2587
--- /dev/null
+++ b/COMIT.bat
@@ -0,0 +1,7 @@
+@echo off
+set /p msg=Commit Message:
+git add .
+git commit -m "%msg%"
+git pull --rebase
+git push
+pause
diff --git a/backend/__pycache__/main.cpython-310.pyc b/backend/__pycache__/main.cpython-310.pyc
new file mode 100644
index 0000000..8d072aa
Binary files /dev/null and b/backend/__pycache__/main.cpython-310.pyc differ
diff --git a/backend/api/v0/__pycache__/feedback.cpython-310.pyc b/backend/api/v0/__pycache__/feedback.cpython-310.pyc
new file mode 100644
index 0000000..9031a0f
Binary files /dev/null and b/backend/api/v0/__pycache__/feedback.cpython-310.pyc differ
diff --git a/backend/api/v0/__pycache__/poster.cpython-310.pyc b/backend/api/v0/__pycache__/poster.cpython-310.pyc
new file mode 100644
index 0000000..28dfac6
Binary files /dev/null and b/backend/api/v0/__pycache__/poster.cpython-310.pyc differ
diff --git a/backend/api/v0/__pycache__/user.cpython-310.pyc b/backend/api/v0/__pycache__/user.cpython-310.pyc
new file mode 100644
index 0000000..67667ab
Binary files /dev/null and b/backend/api/v0/__pycache__/user.cpython-310.pyc differ
diff --git a/backend/api/v0/feedback.py b/backend/api/v0/feedback.py
new file mode 100644
index 0000000..5d3d2d8
--- /dev/null
+++ b/backend/api/v0/feedback.py
@@ -0,0 +1,107 @@
+import json
+from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Request
+from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
+from sqlalchemy.orm import Session
+from sqlalchemy import func
+from sqlalchemy.exc import IntegrityError
+
+from core.db import get_db, Poster, FeedBack
+from core.crypt import decode_jwt, is_admin
+from core.file import get_upload_dir, save_file, get_poster_dir, generate_file_url
+from pydantic import BaseModel, EmailStr
+from datetime import datetime
+
+router = APIRouter()
+security = HTTPBearer()
+
+class Comment(BaseModel):
+ user_id: int
+ text: str
+ rating: int
+
+@router.post("/comment")
+async def create_comment(
+ comment: Comment,
+ db: Session = Depends(get_db),
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ request: Request = None,
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Пользователь не авторизован.")
+
+ if comment.rating < 1 or comment.rating > 5:
+ raise HTTPException(status_code=400, detail="Рейтинг должен быть от 1 до 5.")
+
+ new_comment = FeedBack(
+ userid=user_id,
+ text=comment.text,
+ rating=comment.rating,
+ date=datetime.utcnow()
+ )
+
+ db.add(new_comment)
+ db.commit()
+ db.refresh(new_comment)
+
+ return {"message": "Комментарий успешно добавлен.", "comment": new_comment}
+
+@router.delete("/comment/{comment_id}")
+async def delete_comment(
+ comment_id: int,
+ db: Session = Depends(get_db),
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Пользователь не авторизован.")
+
+ comment = db.query(FeedBack).filter(FeedBack.id == comment_id, FeedBack.userid == user_id).first()
+
+ if not comment:
+ raise HTTPException(status_code=404, detail="Комментарий не найден.")
+
+ if is_admin(user_id, db):
+ raise HTTPException(status_code=403, detail="Недостаточно прав для выполнения этого действия.")
+
+ db.delete(comment)
+ db.commit()
+
+ return {"message": "Комментарий успешно удален."}
+
+@router.get("/comments")
+async def get_comments(
+ db: Session = Depends(get_db),
+ request: Request = None,
+):
+
+ comments = db.query(FeedBack).order_by(FeedBack.date.desc()).all()
+
+ comments_list = []
+ for comment in comments:
+ comments_list.append({
+ "id": comment.id,
+ "userid": comment.userid,
+ "text": comment.text,
+ "date": comment.date.isoformat(),
+ "rating": comment.rating
+ })
+
+ return comments_list
+
+@router.get("/average-rating")
+async def get_average_rating(
+ db: Session = Depends(get_db),
+):
+ average_rating = db.query(func.avg(FeedBack.rating)).scalar()
+
+ if average_rating is None:
+ return {"Нет рэйтинга.": "Рейтинг", "Рейтинг": 0}
+
+ return {"Рэйтинг": round(average_rating, 2)}
diff --git a/backend/api/v0/poster.py b/backend/api/v0/poster.py
new file mode 100644
index 0000000..82c2d53
--- /dev/null
+++ b/backend/api/v0/poster.py
@@ -0,0 +1,257 @@
+import json
+from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Request
+from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
+from sqlalchemy.orm import Session
+from sqlalchemy.exc import IntegrityError
+
+from core.db import get_db, Poster, PosterLike
+from core.crypt import decode_jwt, is_admin
+from core.file import get_upload_dir, save_file, get_poster_dir, generate_file_url
+from pydantic import BaseModel, EmailStr
+from datetime import datetime
+
+router = APIRouter()
+security = HTTPBearer()
+
+@router.get("/getallposter")
+async def get_all_poster(
+ db: Session = Depends(get_db),
+):
+ posters = db.query(Poster).all()
+ if not posters:
+ raise HTTPException(status_code=404, detail="Постеры не найдены.")
+
+ return {
+ "posters": [
+ {
+ "id": poster.id,
+ "title": poster.title,
+ "description": (poster.description[:250] + "...") if poster.description and len(poster.description) > 250 else poster.description,
+ "image": poster.image,
+ "date": poster.date.isoformat() if poster.date else None,
+ "price": poster.price,
+ "like": poster.like,
+ }
+ for poster in posters
+ ]
+ }
+
+@router.post("/postercreate")
+def create_poster(
+ title: str = Form(...),
+ description: str = Form(...),
+ price: int = Form(...),
+ date: datetime = Form(...),
+ file: UploadFile = File(...),
+ db: Session = Depends(get_db),
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ request: Request = None,
+):
+
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not is_admin(user_id, db):
+ raise HTTPException(status_code=403, detail="Недостаточно прав для выполнения этого действия.")
+
+ if not file:
+ raise HTTPException(status_code=400, detail="Файл не был загружен.")
+
+ if not file.filename.endswith(('.jpg', '.jpeg', '.png')):
+ raise HTTPException(status_code=400, detail="Недопустимый формат файла. Допустимые форматы: .jpg, .jpeg, .png.")
+
+ try:
+ poster_id = db.query(Poster).count() + 1
+ upload_dir = get_poster_dir(poster_id)
+ file_path = save_file(file, upload_dir)
+ file_url = generate_file_url(request, file_path)
+
+ new_poster = Poster(
+ title=title,
+ description=description,
+ date=date,
+ price=price,
+ like=0,
+ datecreation=datetime.utcnow(),
+ image=str(file_url),
+ )
+ db.add(new_poster)
+ db.commit()
+ db.refresh(new_poster)
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail=f"Ошибка при загрузке файла: {str(e)}")
+
+ return {"message": "Постер успешно создан.", "poster": new_poster}
+
+@router.post("/posterupdate")
+def update_poster(
+ poster_id: int = Form(...),
+ title: str = Form(...),
+ description: str = Form(...),
+ price: int = Form(...),
+ date: datetime = Form(...),
+ file: UploadFile = File(None),
+ db: Session = Depends(get_db),
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ request: Request = None,
+):
+
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not is_admin(user_id, db):
+ raise HTTPException(status_code=403, detail="Недостаточно прав для выполнения этого действия.")
+
+ poster = db.query(Poster).filter(Poster.id == poster_id).first()
+
+ if not poster:
+ raise HTTPException(status_code=404, detail="Постер не найден.")
+
+ if file:
+ if not file.filename.endswith(('.jpg', '.jpeg', '.png')):
+ raise HTTPException(status_code=400, detail="Недопустимый формат файла. Допустимые форматы: .jpg, .jpeg, .png.")
+
+ try:
+ upload_dir = get_poster_dir(poster_id)
+ file_path = save_file(file, upload_dir)
+ file_url = generate_file_url(request, file_path)
+ poster.image = str(file_url)
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail=f"Ошибка при загрузке файла: {str(e)}")
+
+ poster.title = title
+ poster.description = description
+ poster.date = date
+ poster.price = price
+
+ try:
+ db.commit()
+ db.refresh(poster)
+ except IntegrityError:
+ db.rollback()
+ raise HTTPException(status_code=400, detail="Ошибка при обновлении постера.")
+
+ return {"message": "Постер успешно обновлен.", "poster": poster}
+
+
+@router.post("/setlike")
+def set_like(
+ poster_id: int = Form(...),
+ like: int = Form(...),
+ db: Session = Depends(get_db),
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Пользователь не авторизован.")
+
+ # Проверка существования постера
+ poster = db.query(Poster).filter(Poster.id == poster_id).first()
+ if not poster:
+ raise HTTPException(status_code=404, detail="Постер не найден.")
+
+ # Валидация значения like
+ if like not in [1, -1]:
+ raise HTTPException(status_code=400, detail="Значение like должно быть 1 или -1.")
+
+ try:
+ # Проверка на существующий лайк
+ existing_like = db.query(PosterLike).filter(
+ PosterLike.poster_id == poster_id,
+ PosterLike.user_id == user_id
+ ).first()
+
+ if like == 1:
+ # Пользователь хочет поставить лайк
+ if existing_like:
+ # Лайк уже есть - ничего не делаем
+ return {
+ "message": "Вы уже оценили этот постер.",
+ "poster": {
+ "id": poster.id,
+ "title": poster.title,
+ "like": poster.like,
+ "image": poster.image,
+ "price": poster.price,
+ "date": poster.date.isoformat(),
+ "description": poster.description
+ }
+ }
+ else:
+ # Добавляем лайк
+ new_like = PosterLike(poster_id=poster_id, user_id=user_id)
+ db.add(new_like)
+ poster.like += 1
+ else:
+ # Пользователь хочет убрать лайк
+ if existing_like:
+ # Удаляем запись о лайке
+ db.delete(existing_like)
+ poster.like -= 1
+ else:
+ # Лайка не было - ничего не делаем
+ return {
+ "message": "Вы еще не ставили лайк этому постеру.",
+ "poster": {
+ "id": poster.id,
+ "title": poster.title,
+ "like": poster.like,
+ "image": poster.image,
+ "price": poster.price,
+ "date": poster.date.isoformat(),
+ "description": poster.description
+ }
+ }
+
+ db.commit()
+ db.refresh(poster)
+
+ return {
+ "message": "Лайк успешно обновлен.",
+ "poster": {
+ "id": poster.id,
+ "title": poster.title,
+ "like": poster.like,
+ "image": poster.image,
+ "price": poster.price,
+ "date": poster.date.isoformat(),
+ "description": poster.description
+ }
+ }
+
+ except IntegrityError as e:
+ db.rollback()
+ print(f"Ошибка IntegrityError: {str(e)}")
+ raise HTTPException(status_code=400, detail="Ошибка при обновлении лайка постера.")
+ except Exception as e:
+ db.rollback()
+ print(f"Неожиданная ошибка: {str(e)}")
+ raise HTTPException(status_code=500, detail=f"Произошла ошибка: {str(e)}")
+
+
+@router.get("/getposter/{poster_id}")
+async def get_poster(
+ poster_id: int,
+ db: Session = Depends(get_db),
+):
+ poster = db.query(Poster).filter(Poster.id == poster_id).first()
+
+ if not poster:
+ raise HTTPException(status_code=404, detail="Постер не найден.")
+
+ return {
+ "id": poster.id,
+ "title": poster.title,
+ "description": poster.description,
+ "image": poster.image,
+ "date": poster.date.isoformat() if poster.date else None,
+ "price": poster.price,
+ "like": poster.like,
+ }
diff --git a/backend/api/v0/user.py b/backend/api/v0/user.py
new file mode 100644
index 0000000..04d4434
--- /dev/null
+++ b/backend/api/v0/user.py
@@ -0,0 +1,464 @@
+from fastapi import FastAPI, HTTPException, Depends, APIRouter, Response, Header, File, UploadFile, Request
+from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
+from pydantic import BaseModel, EmailStr
+from sqlalchemy.orm import Session
+from sqlalchemy.exc import IntegrityError
+from urllib.parse import quote, unquote
+
+from core.db import get_db, User
+from core.crypt import hash_password, verify_password, gen_jwt, decode_jwt
+from core.file import get_upload_dir, save_file, generate_file_url
+
+import os
+from pathlib import Path
+from datetime import datetime
+
+router = APIRouter()
+security = HTTPBearer()
+
+
+class RegisterRequest(BaseModel):
+ login: str
+ password: str
+ email: EmailStr
+ last_name: str
+ first_name: str
+ middle_name: str | None = None
+
+@router.post("/register")
+def register_user(user_data: RegisterRequest, db: Session = Depends(get_db)):
+
+ existing_user = db.query(User).filter(User.login == user_data.login).first()
+ if existing_user:
+ raise HTTPException(status_code=400, detail="Пользователь с таким логином уже существует.")
+
+ existing_email = db.query(User).filter(User.email == user_data.email).first()
+ if existing_email:
+ raise HTTPException(status_code=400, detail="Пользователь с такой почтой уже существует.")
+
+ hashed_password = hash_password(user_data.password)
+
+ new_user = User(
+ login=user_data.login,
+ password=hashed_password,
+ email=user_data.email,
+ last_name=user_data.last_name,
+ first_name=user_data.first_name,
+ middle_name=user_data.middle_name,
+ access_level=1,
+ )
+
+ try:
+ db.add(new_user)
+ db.commit()
+ db.refresh(new_user)
+ except IntegrityError:
+ db.rollback()
+ raise HTTPException(status_code=500, detail="Ошибка при добавлении пользователя в базу данных.")
+
+ return {"message": "Пользователь успешно зарегистрирован."}
+
+class LoginRequest(BaseModel):
+ login: str
+ password: str
+
+@router.post("/login")
+def login_user(user_data: LoginRequest, db: Session = Depends(get_db), response: Response = None):
+
+ user = db.query(User).filter(User.login == user_data.login).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователя с таким логином не существует.")
+
+ if not verify_password(user.password, user_data.password):
+ raise HTTPException(status_code=401, detail="Неверный пароль.")
+
+ JWT_token = gen_jwt(user.id, user.access_level)
+
+
+ try:
+ if response:
+
+ cookie_params = {
+ "httponly": False,
+ "samesite": "lax",
+ "max_age": 3600*24*7,
+ "path": "/",
+
+ }
+
+
+ response.set_cookie(key="id", value=str(user.id), **cookie_params)
+ response.set_cookie(key="login", value=quote(user.login), **cookie_params)
+ response.set_cookie(key="email", value=quote(user.email), **cookie_params)
+
+
+ full_name = f"{user.last_name} {user.first_name} {user.middle_name or ''}".strip()
+ response.set_cookie(key="full_name", value=quote(full_name), **cookie_params)
+
+ avatar_value = quote(user.avatar) if user.avatar else quote("/default-avatar.png")
+ response.set_cookie(key="avatar", value=avatar_value, **cookie_params)
+
+
+ response.set_cookie(key="JWT_token", value=JWT_token, **cookie_params)
+
+
+ print(f"JWT токен установлен для пользователя {user.login}: {JWT_token[:10]}...")
+ except Exception as e:
+ print(f"Ошибка при установке cookie: {str(e)}")
+
+
+ return {
+ "message": "Успешный вход.",
+ "token": JWT_token,
+ "user_id": user.id,
+ "login": user.login
+ }
+
+@router.post("/logout")
+def logout_user(response: Response):
+ response.delete_cookie(key="login")
+ response.delete_cookie(key="email")
+ response.delete_cookie(key="full_name")
+ response.delete_cookie(key="id")
+ response.delete_cookie(key="avatar")
+ response.delete_cookie(key="JWT_token")
+
+ return {"message": "Вы успешно вышли из системы."}
+
+@router.get("/user/{user_id}")
+def get_user_data(user_id: int, db: Session = Depends(get_db)):
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь с таким ID не найден.")
+
+ return {
+ "login": user.login,
+ "avatar": user.avatar,
+ "first_name": user.first_name,
+ "last_name": user.last_name,
+ "middle_name": user.middle_name,
+ "email": user.email
+ }
+
+class ChangePasswordRequest(BaseModel):
+ old_password: str
+ new_password: str
+
+@router.post("/change_password")
+def change_password(
+ password_data: ChangePasswordRequest,
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db)
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь не найден.")
+
+ if not verify_password(user.password, password_data.old_password):
+ raise HTTPException(status_code=401, detail="Неверный старый пароль.")
+
+
+
+ user.password = hash_password(password_data.new_password)
+
+ try:
+ db.commit()
+ except:
+ db.rollback()
+ raise HTTPException(status_code=500, detail="Ошибка при обновлении пароля.")
+
+ return {"message": "Пароль успешно изменён."}
+
+class ChangeUsernameRequest(BaseModel):
+ new_login: str
+
+@router.post("/change_username")
+def change_username(
+ login_data: ChangeUsernameRequest,
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db),
+ response: Response = None
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь не найден.")
+
+ existing_user = db.query(User).filter(User.login == login_data.new_login).first()
+ if existing_user:
+ raise HTTPException(status_code=400, detail="Логин уже используется другим пользователем.")
+
+ try:
+ user.login = login_data.new_login
+ db.commit()
+ db.refresh(user)
+
+ # Безопасно устанавливаем cookie
+ try:
+ if response:
+ response.set_cookie(
+ key="login",
+ value=user.login,
+ httponly=False
+ )
+ except:
+ # Игнорируем ошибку установки cookie
+ pass
+
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail="Ошибка при обновлении логина.")
+
+ return {"message": "Логин успешно изменен."}
+
+class ChangeFirstNameRequest(BaseModel):
+ new_first_name: str
+
+@router.post("/change_name")
+def change_first_name(
+ name_data: ChangeFirstNameRequest,
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db),
+ response: Response = None
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь с таким ID не найден.")
+
+ try:
+ user.first_name = name_data.new_first_name
+ db.commit()
+ db.refresh(user)
+
+ # Безопасная установка cookie с проверкой response и URL-кодированием
+ try:
+ if response:
+ full_name = f"{user.last_name} {user.first_name} {user.middle_name or ''}".strip()
+ response.set_cookie(
+ key="full_name",
+ value=quote(full_name), # URL-кодирование для поддержки кириллицы
+ httponly=False,
+ samesite="lax",
+ max_age=3600*24*30, # 30 дней
+ path="/"
+ )
+ except Exception as e:
+ print(f"Ошибка при установке cookie: {str(e)}")
+ # Продолжаем выполнение даже при ошибке cookie
+
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail=f"Ошибка при обновлении имени: {str(e)}")
+
+ # Возвращаем обновленные данные для использования на клиенте
+ return {
+ "message": "Имя успешно изменено.",
+ "full_name": f"{user.last_name} {user.first_name} {user.middle_name or ''}".strip(),
+ "updated_at": str(datetime.now())
+ }
+
+class ChangeLastNameRequest(BaseModel):
+ new_last_name: str
+
+@router.post("/change_last_name")
+def change_last_name(
+ last_name_data: ChangeLastNameRequest,
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db),
+ response: Response = None
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь с таким ID не найден.")
+
+ try:
+ user.last_name = last_name_data.new_last_name
+ db.commit()
+ db.refresh(user)
+
+ # Безопасно устанавливаем cookie
+ try:
+ if response:
+ response.set_cookie(
+ key="full_name",
+ value=f"{user.last_name} {user.first_name} {user.middle_name or ''}",
+ httponly=False
+ )
+ except:
+ # Игнорируем ошибку установки cookie
+ pass
+
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail="Ошибка при обновлении фамилии.")
+
+ return {"message": "Фамилия успешно изменена."}
+
+class ChangeMiddleNameRequest(BaseModel):
+ new_middle_name: str | None = None
+
+@router.post("/change_middle_name")
+def change_middle_name(
+ middle_name_data: ChangeMiddleNameRequest,
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db),
+ response: Response = None
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь с таким ID не найден.")
+
+ try:
+ user.middle_name = middle_name_data.new_middle_name
+ db.commit()
+ db.refresh(user)
+
+ # Безопасно устанавливаем cookie
+ try:
+ if response:
+ response.set_cookie(
+ key="full_name",
+ value=f"{user.last_name} {user.first_name} {user.middle_name or ''}",
+ httponly=False
+ )
+ except:
+ # Игнорируем ошибку установки cookie
+ pass
+
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail="Ошибка при обновлении отчества.")
+
+ return {"message": "Отчество успешно изменено."}
+
+class ChangeEmailRequest(BaseModel):
+ new_email: EmailStr
+
+@router.post("/change_email")
+def change_email(
+ email_data: ChangeEmailRequest,
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db),
+ response: Response = None
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь с таким ID не найден.")
+
+ existing_user = db.query(User).filter(User.email == email_data.new_email).first()
+ if existing_user:
+ raise HTTPException(status_code=400, detail="Почта уже используется другим пользователем.")
+
+ try:
+ user.email = email_data.new_email
+ db.commit()
+ db.refresh(user)
+
+ # Безопасно устанавливаем cookie
+ try:
+ if response:
+ response.set_cookie(
+ key="email",
+ value=user.email,
+ httponly=False
+ )
+ except:
+ # Игнорируем ошибку установки cookie
+ pass
+
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail="Ошибка при обновлении почты.")
+
+ return {"message": "Почта успешно изменена."}
+
+
+@router.post("/upload_avatar")
+def upload_avatar(
+ file: UploadFile = File(...),
+ credentials: HTTPAuthorizationCredentials = Depends(security),
+ db: Session = Depends(get_db),
+ request: Request = None,
+ response: Response = None
+):
+ token = credentials.credentials
+ decoded_data = decode_jwt(token)
+ user_id = decoded_data.get("user_id")
+
+ if not user_id:
+ raise HTTPException(status_code=401, detail="Недействительный токен.")
+
+ user = db.query(User).filter(User.id == user_id).first()
+ if not user:
+ raise HTTPException(status_code=404, detail="Пользователь с таким ID не найден.")
+
+ if not file.content_type.startswith("image/"):
+ raise HTTPException(status_code=400, detail="Файл должен быть изображением.")
+
+ try:
+ upload_dir = get_upload_dir(user_id)
+ file_path = save_file(file, upload_dir)
+
+ file_url = generate_file_url(request, file_path)
+
+ user.avatar = str(file_url)
+ db.commit()
+ db.refresh(user)
+
+ # Безопасно устанавливаем cookie
+ try:
+ if response:
+ response.set_cookie(
+ key="avatar",
+ value=file_url,
+ httponly=False
+ )
+ except:
+ # Игнорируем ошибку установки cookie
+ pass
+
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=500, detail=f"Ошибка при загрузке файла: {str(e)}")
+
+ return {"message": "Аватар успешно загружен.", "file_url": file_url}
\ No newline at end of file
diff --git a/backend/core/__pycache__/crypt.cpython-310.pyc b/backend/core/__pycache__/crypt.cpython-310.pyc
new file mode 100644
index 0000000..db6e4b1
Binary files /dev/null and b/backend/core/__pycache__/crypt.cpython-310.pyc differ
diff --git a/backend/core/__pycache__/db.cpython-310.pyc b/backend/core/__pycache__/db.cpython-310.pyc
new file mode 100644
index 0000000..5bd4b6b
Binary files /dev/null and b/backend/core/__pycache__/db.cpython-310.pyc differ
diff --git a/backend/core/__pycache__/file.cpython-310.pyc b/backend/core/__pycache__/file.cpython-310.pyc
new file mode 100644
index 0000000..30db6e6
Binary files /dev/null and b/backend/core/__pycache__/file.cpython-310.pyc differ
diff --git a/backend/core/crypt.py b/backend/core/crypt.py
new file mode 100644
index 0000000..6357d14
--- /dev/null
+++ b/backend/core/crypt.py
@@ -0,0 +1,72 @@
+import hashlib
+from core.db import User, SessionLocal
+from sqlalchemy.orm import Session
+from jwt import encode, decode, ExpiredSignatureError, InvalidTokenError
+from fastapi import HTTPException
+from datetime import datetime, timedelta
+
+SECRET_KEY = "SosttetHuhhhy"
+ALGORITM = "HS256"
+
+"""
+Функция для генерации JWT токена.
+При вводе аргументов user_id и access_level, функция возвращает JWT токен.
+"""
+def gen_jwt(user_id: int, access_level: int) -> str:
+ payload = {
+ "user_id": user_id,
+ "access_level": access_level,
+ "exp": datetime.utcnow() + timedelta(days=1)
+ }
+ token = encode(payload, SECRET_KEY, algorithm=ALGORITM)
+ return token
+
+"""
+Функция для расшифровки JWT токена.
+Принимает токен и возвращает словарь с user_id и access_level.
+"""
+
+def decode_jwt(token: str) -> dict:
+ try:
+ payload = decode(token, SECRET_KEY, algorithms=[ALGORITM])
+ return {
+ "user_id": payload.get("user_id"),
+ "access_level": payload.get("access_level")
+ }
+ except ExpiredSignatureError:
+ raise HTTPException(status_code=401, detail="Срок действия токена истёк.")
+ except InvalidTokenError:
+ raise HTTPException(status_code=401, detail="Неверный токен.")
+
+"""
+Функция для хеширования пароля с использованием SHA-256.
+При вводе аргумента password, функция возвращает его хеш.
+"""
+
+def hash_password(password: str) -> str:
+ sha256_hash = hashlib.sha256()
+ sha256_hash.update(password.encode('utf-8'))
+ return sha256_hash.hexdigest()
+
+"""
+Функция для проверки пароля.
+При вводе аргументов stored_password и provided_password, функция
+возвращает True, если хеши совпадают, и False в противном случае.
+"""
+
+def verify_password(stored_password: str, provided_password: str) -> bool:
+ return stored_password == hash_password(provided_password)
+
+
+"""
+Функция для определения уровня доступа пользователя.
+"""
+
+def is_admin(user_id: int, db: Session) -> bool:
+ user = db.query(User).filter(User.id == user_id).first()
+ if user.access_level ==1:
+ return False
+ elif user.access_level == 2:
+ return True
+
+
diff --git a/backend/core/db.py b/backend/core/db.py
new file mode 100644
index 0000000..62544ab
--- /dev/null
+++ b/backend/core/db.py
@@ -0,0 +1,83 @@
+from sqlalchemy import create_engine, Column, String, Integer, DateTime, ForeignKey, UniqueConstraint
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+from datetime import datetime
+
+DATABASE_URL = "sqlite:///c:/Users/rbiter/Desktop/AboutMe/backend/database.db"
+
+engine = create_engine(DATABASE_URL)
+
+Base = declarative_base()
+
+class User(Base):
+ __tablename__ = 'users'
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ login = Column(String, nullable=False, unique=True)
+ password = Column(String, nullable=False)
+ email = Column(String, nullable=False, unique=True)
+ last_name = Column(String, nullable=False)
+ first_name = Column(String, nullable=False)
+ middle_name = Column(String, nullable=True)
+ access_level = Column(Integer, nullable=False, default=1)
+ avatar = Column(String, nullable=True)
+ dateregistration = Column(DateTime, default=datetime.utcnow)
+
+class Poster(Base):
+ __tablename__ = 'posters'
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ title = Column(String, nullable=False)
+ description = Column(String, nullable=False)
+ image = Column(String, nullable=False)
+ date = Column(DateTime, nullable=False)
+ price = Column(Integer, nullable=False, default=0)
+ like = Column(Integer, nullable=False, default=0)
+ datecreation = Column(DateTime, default=datetime.utcnow)
+
+class Sale(Base):
+ __tablename__ = "sales"
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ amount = Column(Integer, nullable=False)
+ status = Column(String(50), nullable=False, default="pending") # pending, paid, failed, canceled
+ created_at = Column(DateTime, default=datetime.utcnow)
+ description = Column(String(255), nullable=False)
+
+ poster_id = Column(Integer, ForeignKey("posters.id"), nullable=True)
+ user_id = Column(Integer, nullable=False)
+ user_email = Column(String, ForeignKey("users.email"), nullable=False)
+ payment_id = Column(String, nullable=False, unique=True)
+ confirmation_url = Column(String, nullable=True)
+
+
+
+class PosterLike(Base):
+ __tablename__ = 'poster_likes'
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ poster_id = Column(Integer, ForeignKey('posters.id'), nullable=False)
+ user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
+ date_created = Column(DateTime, default=datetime.utcnow)
+
+
+ __table_args__ = (UniqueConstraint('poster_id', 'user_id', name='uix_poster_user'),)
+class FeedBack(Base):
+ __tablename__ = "feedback"
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ userid = Column(Integer, ForeignKey("users.id"), nullable=False)
+ text = Column(String, nullable=False)
+ date = Column(DateTime, default=datetime.utcnow)
+ rating = Column(Integer, nullable=False)
+
+def initialize_database():
+ Base.metadata.create_all(engine)
+ with engine.connect() as connection:
+ print("База данных успешно создана или подключена!")
+
+SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+
+def get_db():
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
\ No newline at end of file
diff --git a/backend/core/file.py b/backend/core/file.py
new file mode 100644
index 0000000..1b4941a
--- /dev/null
+++ b/backend/core/file.py
@@ -0,0 +1,32 @@
+from fastapi import File, UploadFile, HTTPException, Depends
+from fastapi.security import HTTPAuthorizationCredentials
+from sqlalchemy.orm import Session
+from pathlib import Path
+from urllib.parse import urljoin
+from fastapi import Request
+
+BASE_URL = "http://127.0.0.1:8000"
+
+def get_upload_dir(user_id: int) -> Path:
+ """Возвращает путь для сохранения файлов пользователя."""
+ upload_dir = Path(f"res/user/{user_id}")
+ upload_dir.mkdir(parents=True, exist_ok=True)
+ return upload_dir
+
+def get_poster_dir(poster_id: int) -> Path:
+ """Возвращает путь для сохранения обложек постеров."""
+ poster_dir = Path(f"res/poster/{poster_id}")
+ poster_dir.mkdir(parents=True, exist_ok=True)
+ return poster_dir
+
+def save_file(file: UploadFile, target_dir: Path) -> Path:
+ """Сохраняет файл в указанной директории и возвращает путь к файлу."""
+ file_path = target_dir / file.filename
+ with open(file_path, "wb") as f:
+ f.write(file.file.read())
+ return file_path
+
+def generate_file_url(request: Request, file_path: Path) -> str:
+ """Генерирует URL для доступа к файлу."""
+ relative_path = file_path.relative_to(Path("res"))
+ return urljoin(str(request.base_url), f"res/{relative_path}")
diff --git a/backend/database.db b/backend/database.db
new file mode 100644
index 0000000..7ec9e2d
Binary files /dev/null and b/backend/database.db differ
diff --git a/backend/main.py b/backend/main.py
new file mode 100644
index 0000000..14bc02a
--- /dev/null
+++ b/backend/main.py
@@ -0,0 +1,30 @@
+import core.db as db
+import uvicorn
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from api.v0.user import router as user_router
+from fastapi.staticfiles import StaticFiles
+from api.v0.poster import router as poster_router
+from api.v0.feedback import router as feedback_router
+
+db.initialize_database()
+
+
+
+app = FastAPI()
+app.include_router(user_router)
+app.include_router(poster_router)
+app.include_router(feedback_router)
+app.mount("/res", StaticFiles(directory="res"), name="res")
+
+
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["http://localhost:3000"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+if __name__ == "__main__":
+ uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True, workers=1)
\ No newline at end of file
diff --git a/backend/pay.py b/backend/pay.py
new file mode 100644
index 0000000..3df97ce
--- /dev/null
+++ b/backend/pay.py
@@ -0,0 +1,39 @@
+import uuid
+import requests
+import base64
+
+shop_id = '1017909'
+api_key = 'test_udCBy7nXqGavVoj3RrzIRXN9UL02_UnF0FBBykxWH60'
+
+auth_token = base64.b64encode(f'{shop_id}:{api_key}'.encode()).decode()
+
+idempotence_key = str(uuid.uuid4())
+
+headers = {
+ 'Content-Type': 'application/json',
+ 'Authorization': f'Basic {auth_token}',
+ 'Idempotence-Key': idempotence_key
+}
+
+data = {
+ "amount": {
+ "value": "1500.00",
+ "currency": "RUB"
+ },
+ "confirmation": {
+ "type": "redirect",
+ "return_url": "https://example.com/return"
+ },
+ "capture": True,
+ "description": "Зеленый слоник 2",
+ "metadata": {
+ "order_id": str(uuid.uuid4())
+ }
+}
+
+response = requests.post("https://api.yookassa.ru/v3/payments", json=data, headers=headers)
+
+if response.status_code in [200, 201]:
+ print(response.json()["confirmation"]["confirmation_url"])
+else:
+ print(f"Ошибка {response.status_code}: {response.text}")
diff --git a/backend/res/poster/1/zs2.png b/backend/res/poster/1/zs2.png
new file mode 100644
index 0000000..264ef57
Binary files /dev/null and b/backend/res/poster/1/zs2.png differ
diff --git a/backend/res/poster/2/Cropped_desktop_wallpaper.png b/backend/res/poster/2/Cropped_desktop_wallpaper.png
new file mode 100644
index 0000000..44fe05d
Binary files /dev/null and b/backend/res/poster/2/Cropped_desktop_wallpaper.png differ
diff --git a/backend/res/user/1/17428267109723.png b/backend/res/user/1/17428267109723.png
new file mode 100644
index 0000000..82e8eda
Binary files /dev/null and b/backend/res/user/1/17428267109723.png differ
diff --git a/backend/res/user/1/Cropped_desktop_wallpaper.png b/backend/res/user/1/Cropped_desktop_wallpaper.png
new file mode 100644
index 0000000..44fe05d
Binary files /dev/null and b/backend/res/user/1/Cropped_desktop_wallpaper.png differ
diff --git a/backend/res/user/1/_fnB1AnXl_E.jpg b/backend/res/user/1/_fnB1AnXl_E.jpg
new file mode 100644
index 0000000..297b6a8
Binary files /dev/null and b/backend/res/user/1/_fnB1AnXl_E.jpg differ
diff --git a/backend/res/user/1/ktoH5_kF8W7U2K-4bqJ37Auwls3a5XeYib85nHkrZAcfLnmsBRF_UaNdqxPSzwXZUMr2o1FCOgamLzDk-BvS79Tx.jpg b/backend/res/user/1/ktoH5_kF8W7U2K-4bqJ37Auwls3a5XeYib85nHkrZAcfLnmsBRF_UaNdqxPSzwXZUMr2o1FCOgamLzDk-BvS79Tx.jpg
new file mode 100644
index 0000000..9a8fa52
Binary files /dev/null and b/backend/res/user/1/ktoH5_kF8W7U2K-4bqJ37Auwls3a5XeYib85nHkrZAcfLnmsBRF_UaNdqxPSzwXZUMr2o1FCOgamLzDk-BvS79Tx.jpg differ
diff --git a/backend/res/user/1/photo_2025-02-02_18-44-41.jpg b/backend/res/user/1/photo_2025-02-02_18-44-41.jpg
new file mode 100644
index 0000000..aeaa00a
Binary files /dev/null and b/backend/res/user/1/photo_2025-02-02_18-44-41.jpg differ
diff --git a/backend/res/user/2/17428267109723.png b/backend/res/user/2/17428267109723.png
new file mode 100644
index 0000000..82e8eda
Binary files /dev/null and b/backend/res/user/2/17428267109723.png differ
diff --git a/backend/res/user/4/17428267109723.png b/backend/res/user/4/17428267109723.png
new file mode 100644
index 0000000..82e8eda
Binary files /dev/null and b/backend/res/user/4/17428267109723.png differ
diff --git a/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 0000000..5ef6a52
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 0000000..e215bc4
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs
new file mode 100644
index 0000000..c85fb67
--- /dev/null
+++ b/frontend/eslint.config.mjs
@@ -0,0 +1,16 @@
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+import { FlatCompat } from "@eslint/eslintrc";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+const compat = new FlatCompat({
+ baseDirectory: __dirname,
+});
+
+const eslintConfig = [
+ ...compat.extends("next/core-web-vitals", "next/typescript"),
+];
+
+export default eslintConfig;
diff --git a/frontend/next.config.ts b/frontend/next.config.ts
new file mode 100644
index 0000000..e9ffa30
--- /dev/null
+++ b/frontend/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+};
+
+export default nextConfig;
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
new file mode 100644
index 0000000..2f85c0f
--- /dev/null
+++ b/frontend/package-lock.json
@@ -0,0 +1,6812 @@
+{
+ "name": "frontend",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.1.0",
+ "dependencies": {
+ "@types/leaflet": "^1.9.17",
+ "date-fns": "^4.1.0",
+ "frontend": "file:",
+ "leaflet": "^1.9.4",
+ "next": "15.3.2",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-leaflet": "^4.2.1"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
+ "@types/date-fns": "^2.6.3",
+ "@types/google.maps": "^3.58.1",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.3.2",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
+ "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.0.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
+ "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz",
+ "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.20.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
+ "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
+ "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+ "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.26.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
+ "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
+ "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.13.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz",
+ "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz",
+ "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz",
+ "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz",
+ "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz",
+ "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz",
+ "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz",
+ "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz",
+ "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz",
+ "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz",
+ "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz",
+ "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz",
+ "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz",
+ "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz",
+ "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz",
+ "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz",
+ "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz",
+ "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz",
+ "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.4.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz",
+ "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz",
+ "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.3.tgz",
+ "integrity": "sha512-rmOWVRUbUJD7iSvJugjUbFZshTAuJ48MXoZ80Osx1GM0K/H1w7rSEvmw8m6vdWxNASgtaHIhAgre4H/E9GJiYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "express": "^5.0.1",
+ "express-rate-limit": "^7.5.0",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.23.8",
+ "zod-to-json-schema": "^3.24.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz",
+ "integrity": "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.0",
+ "@emnapi/runtime": "^1.4.0",
+ "@tybys/wasm-util": "^0.9.0"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz",
+ "integrity": "sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==",
+ "license": "MIT"
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.2.tgz",
+ "integrity": "sha512-ijVRTXBgnHT33aWnDtmlG+LJD+5vhc9AKTJPquGG5NKXjpKNjc62woIhFtrAcWdBobt8kqjCoaJ0q6sDQoX7aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "3.3.1"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.2.tgz",
+ "integrity": "sha512-2DR6kY/OGcokbnCsjHpNeQblqCZ85/1j6njYSkzRdpLn5At7OkSdmk7WyAmB9G0k25+VgqVZ/u356OSoQZ3z0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.2.tgz",
+ "integrity": "sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.2.tgz",
+ "integrity": "sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.2.tgz",
+ "integrity": "sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.2.tgz",
+ "integrity": "sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.2.tgz",
+ "integrity": "sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.2.tgz",
+ "integrity": "sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.2.tgz",
+ "integrity": "sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
+ "node_modules/@react-leaflet/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
+ "license": "Hippocratic-2.1",
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rushstack/eslint-patch": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz",
+ "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.6.tgz",
+ "integrity": "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "enhanced-resolve": "^5.18.1",
+ "jiti": "^2.4.2",
+ "lightningcss": "1.29.2",
+ "magic-string": "^0.30.17",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.6"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz",
+ "integrity": "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.6",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.6",
+ "@tailwindcss/oxide-darwin-x64": "4.1.6",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.6",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.6",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.6",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.6",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.6",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.6",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.6",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.6"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.6.tgz",
+ "integrity": "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.6.tgz",
+ "integrity": "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.6.tgz",
+ "integrity": "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.6.tgz",
+ "integrity": "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.6.tgz",
+ "integrity": "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.6.tgz",
+ "integrity": "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.6.tgz",
+ "integrity": "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.6.tgz",
+ "integrity": "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.6.tgz",
+ "integrity": "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.6.tgz",
+ "integrity": "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@emnapi/wasi-threads": "^1.0.2",
+ "@napi-rs/wasm-runtime": "^0.2.9",
+ "@tybys/wasm-util": "^0.9.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.6.tgz",
+ "integrity": "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.6.tgz",
+ "integrity": "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.6.tgz",
+ "integrity": "sha512-ELq+gDMBuRXPJlpE3PEen+1MhnHAQQrh2zF0dI1NXOlEWfr2qWf2CQdr5jl9yANv8RErQaQ2l6nIFO9OSCVq/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.6",
+ "@tailwindcss/oxide": "4.1.6",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.6"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/date-fns": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/@types/date-fns/-/date-fns-2.6.3.tgz",
+ "integrity": "sha512-Ke1lw2Ni1t/wMUoLtKFmSNCLozcTBd6vmMqFP4hRzXn6qzkNt97bPAX0x5Y/c15DP43kKvwW1ycStD5+43jVQA==",
+ "deprecated": "This is a stub types definition. date-fns provides its own type definitions, so you do not need this installed.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "date-fns": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/google.maps": {
+ "version": "3.58.1",
+ "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz",
+ "integrity": "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/leaflet": {
+ "version": "1.9.17",
+ "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.17.tgz",
+ "integrity": "sha512-IJ4K6t7I3Fh5qXbQ1uwL3CFVbCi6haW9+53oLWgdKlLP7EaS21byWFJxxqOx9y8I0AP0actXSJLVMbyvxhkUTA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.17.47",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.47.tgz",
+ "integrity": "sha512-3dLX0Upo1v7RvUimvxLeXqwrfyKxUINk0EAM83swP2mlSUcwV73sZy8XhNz8bcZ3VbsfQyC/y6jRdL5tgCNpDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.4",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz",
+ "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.5",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz",
+ "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
+ "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.32.1",
+ "@typescript-eslint/type-utils": "8.32.1",
+ "@typescript-eslint/utils": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
+ "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
+ "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.32.1",
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/typescript-estree": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
+ "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
+ "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.32.1",
+ "@typescript-eslint/utils": "8.32.1",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
+ "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
+ "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/visitor-keys": "8.32.1",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
+ "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.32.1",
+ "@typescript-eslint/types": "8.32.1",
+ "@typescript-eslint/typescript-estree": "8.32.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.32.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
+ "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.32.1",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz",
+ "integrity": "sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.2.tgz",
+ "integrity": "sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.2.tgz",
+ "integrity": "sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.2.tgz",
+ "integrity": "sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.2.tgz",
+ "integrity": "sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.2.tgz",
+ "integrity": "sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.2.tgz",
+ "integrity": "sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.2.tgz",
+ "integrity": "sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.2.tgz",
+ "integrity": "sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.2.tgz",
+ "integrity": "sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.2.tgz",
+ "integrity": "sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz",
+ "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz",
+ "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.2.tgz",
+ "integrity": "sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.9"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.2.tgz",
+ "integrity": "sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.2.tgz",
+ "integrity": "sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz",
+ "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.10.3",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
+ "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+ "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.0",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.6.3",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.0",
+ "type-is": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001718",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
+ "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
+ "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.23.9",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
+ "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.0",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-regex": "^1.2.1",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.0",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.3",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.3",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.18"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
+ "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.6",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.4",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.26.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz",
+ "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.20.0",
+ "@eslint/config-helpers": "^0.2.1",
+ "@eslint/core": "^0.13.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.26.0",
+ "@eslint/plugin-kit": "^0.2.8",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@modelcontextprotocol/sdk": "^1.8.0",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.3.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "zod": "^3.24.2"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.2.tgz",
+ "integrity": "sha512-FerU4DYccO4FgeYFFglz0SnaKRe1ejXQrDb8kWUkTAg036YWi+jUsgg4sIGNCDhAsDITsZaL4MzBWKB6f4G1Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@next/eslint-plugin-next": "15.3.2",
+ "@rushstack/eslint-patch": "^1.10.3",
+ "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-import-resolver-typescript": "^3.5.2",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsx-a11y": "^6.10.0",
+ "eslint-plugin-react": "^7.37.0",
+ "eslint-plugin-react-hooks": "^5.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0",
+ "typescript": ">=3.3.1"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
+ "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.31.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
+ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.8",
+ "array.prototype.findlastindex": "^1.2.5",
+ "array.prototype.flat": "^1.3.2",
+ "array.prototype.flatmap": "^1.3.2",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.0",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.15.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.0",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.8",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aria-query": "^5.3.2",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventsource-parser": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz",
+ "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
+ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.0",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
+ "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": "^4.11 || 5 || ^5.0.0-beta.1"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/frontend": {
+ "resolved": "",
+ "link": true
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
+ "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
+ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/leaflet": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
+ "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz",
+ "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.29.2",
+ "lightningcss-darwin-x64": "1.29.2",
+ "lightningcss-freebsd-x64": "1.29.2",
+ "lightningcss-linux-arm-gnueabihf": "1.29.2",
+ "lightningcss-linux-arm64-gnu": "1.29.2",
+ "lightningcss-linux-arm64-musl": "1.29.2",
+ "lightningcss-linux-x64-gnu": "1.29.2",
+ "lightningcss-linux-x64-musl": "1.29.2",
+ "lightningcss-win32-arm64-msvc": "1.29.2",
+ "lightningcss-win32-x64-msvc": "1.29.2"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz",
+ "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz",
+ "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz",
+ "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz",
+ "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz",
+ "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz",
+ "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz",
+ "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz",
+ "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz",
+ "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz",
+ "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-postinstall": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.4.tgz",
+ "integrity": "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.3.2.tgz",
+ "integrity": "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.3.2",
+ "@swc/counter": "0.1.3",
+ "@swc/helpers": "0.5.15",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.3.2",
+ "@next/swc-darwin-x64": "15.3.2",
+ "@next/swc-linux-arm64-gnu": "15.3.2",
+ "@next/swc-linux-arm64-musl": "15.3.2",
+ "@next/swc-linux-x64-gnu": "15.3.2",
+ "@next/swc-linux-x64-musl": "15.3.2",
+ "@next/swc-win32-arm64-msvc": "15.3.2",
+ "@next/swc-win32-x64-msvc": "15.3.2",
+ "sharp": "^0.34.1"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/object-assign": {
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pkce-challenge": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
+ "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
+ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.6.3",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/react-leaflet": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
+ "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
+ "license": "Hippocratic-2.1",
+ "dependencies": {
+ "@react-leaflet/core": "^2.1.0"
+ },
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "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==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "devOptional": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/sharp": {
+ "version": "0.34.1",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz",
+ "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.3",
+ "semver": "^7.7.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.1",
+ "@img/sharp-darwin-x64": "0.34.1",
+ "@img/sharp-libvips-darwin-arm64": "1.1.0",
+ "@img/sharp-libvips-darwin-x64": "1.1.0",
+ "@img/sharp-libvips-linux-arm": "1.1.0",
+ "@img/sharp-libvips-linux-arm64": "1.1.0",
+ "@img/sharp-libvips-linux-ppc64": "1.1.0",
+ "@img/sharp-libvips-linux-s390x": "1.1.0",
+ "@img/sharp-libvips-linux-x64": "1.1.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.1.0",
+ "@img/sharp-linux-arm": "0.34.1",
+ "@img/sharp-linux-arm64": "0.34.1",
+ "@img/sharp-linux-s390x": "0.34.1",
+ "@img/sharp-linux-x64": "0.34.1",
+ "@img/sharp-linuxmusl-arm64": "0.34.1",
+ "@img/sharp-linuxmusl-x64": "0.34.1",
+ "@img/sharp-wasm32": "0.34.1",
+ "@img/sharp-win32-ia32": "0.34.1",
+ "@img/sharp-win32-x64": "0.34.1"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+ "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz",
+ "integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
+ "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/unrs-resolver": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz",
+ "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.2.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/JounQin"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-darwin-arm64": "1.7.2",
+ "@unrs/resolver-binding-darwin-x64": "1.7.2",
+ "@unrs/resolver-binding-freebsd-x64": "1.7.2",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.7.2",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.7.2",
+ "@unrs/resolver-binding-linux-x64-musl": "1.7.2",
+ "@unrs/resolver-binding-wasm32-wasi": "1.7.2",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.7.2"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.24.5",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
+ "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
+ "dev": true,
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.24.1"
+ }
+ }
+ }
+}
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..fa95e8e
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "frontend",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@types/leaflet": "^1.9.17",
+ "date-fns": "^4.1.0",
+ "frontend": "file:",
+ "leaflet": "^1.9.4",
+ "next": "15.3.2",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-leaflet": "^4.2.1"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
+ "@types/date-fns": "^2.6.3",
+ "@types/google.maps": "^3.58.1",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.3.2",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs
new file mode 100644
index 0000000..c7bcb4b
--- /dev/null
+++ b/frontend/postcss.config.mjs
@@ -0,0 +1,5 @@
+const config = {
+ plugins: ["@tailwindcss/postcss"],
+};
+
+export default config;
diff --git a/frontend/public/icon/like.svg b/frontend/public/icon/like.svg
new file mode 100644
index 0000000..6c524d9
--- /dev/null
+++ b/frontend/public/icon/like.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/icon/login.svg b/frontend/public/icon/login.svg
new file mode 100644
index 0000000..cea3b0d
--- /dev/null
+++ b/frontend/public/icon/login.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/public/icon/logo.svg b/frontend/public/icon/logo.svg
new file mode 100644
index 0000000..42874f8
--- /dev/null
+++ b/frontend/public/icon/logo.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/public/icon/ticket.svg b/frontend/public/icon/ticket.svg
new file mode 100644
index 0000000..063db0c
--- /dev/null
+++ b/frontend/public/icon/ticket.svg
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/public/icon/user.svg b/frontend/public/icon/user.svg
new file mode 100644
index 0000000..1e88629
--- /dev/null
+++ b/frontend/public/icon/user.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/public/poster/zs2.png b/frontend/public/poster/zs2.png
new file mode 100644
index 0000000..264ef57
Binary files /dev/null and b/frontend/public/poster/zs2.png differ
diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx
new file mode 100644
index 0000000..1198f63
--- /dev/null
+++ b/frontend/src/app/about/page.tsx
@@ -0,0 +1,560 @@
+'use client';
+import { useEffect, CSSProperties } from 'react';
+import Header from '@/components/Header';
+import Fotter from '@/components/Fotter';
+
+// Стили для страницы About
+const styles = `
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes fadeInRight {
+ from {
+ opacity: 0;
+ transform: translateX(30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes glowPulse {
+ 0% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ text-shadow: 0 0 20px rgba(255,127,39,0.6);
+ }
+ 100% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+@keyframes borderGlow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 15px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+.page-container {
+ background: radial-gradient(circle at top right, rgba(30,30,30,0.8) 0%, rgba(15,15,15,0.8) 100%);
+ min-height: 100vh;
+ position: relative;
+ overflow-x: hidden;
+}
+
+.page-container::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url('/noise-texture.png'), linear-gradient(145deg, rgba(255,127,39,0.1) 0%, rgba(0,0,0,0) 70%);
+ opacity: 0.4;
+ pointer-events: none;
+ z-index: -1;
+}
+
+.content-container {
+ animation: fadeInUp 0.6s ease-out forwards;
+ width: 100%;
+}
+
+.section-title {
+ color: #ff7f27;
+ font-size: 2.5rem;
+ margin-bottom: 1.5rem;
+ font-weight: bold;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: glowPulse 3s infinite;
+ text-align: center;
+ letter-spacing: -0.5px;
+}
+
+.section-subtitle {
+ font-size: 1.5rem;
+ margin: 2rem 0 1rem;
+ color: #ff7f27;
+ font-weight: 600;
+}
+
+.about-section {
+ margin-bottom: 3rem;
+ background: rgba(0,0,0,0.3);
+ border-radius: 1rem;
+ padding: 2rem;
+ border: 1px solid rgba(255,127,39,0.2);
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+}
+
+.about-intro {
+ text-align: center;
+ max-width: 800px;
+ margin: 0 auto 3rem;
+ line-height: 1.7;
+ font-size: 1.1rem;
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+ animation-delay: 0.2s;
+ color: rgba(255,255,255,0.9);
+}
+
+.team-container {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 2rem;
+ margin: 2rem 0;
+}
+
+.team-member {
+ background: linear-gradient(145deg, rgba(30,30,30,0.6), rgba(15,15,15,0.8));
+ border-radius: 1rem;
+ padding: 1.5rem;
+ width: 280px;
+ text-align: center;
+ border: 1px solid rgba(255,127,39,0.2);
+ opacity: 0;
+ animation: fadeInUp 0.5s forwards;
+ transition: all 0.3s ease;
+}
+
+.team-member:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 20px rgba(0,0,0,0.3);
+ border-color: rgba(255,127,39,0.5);
+}
+
+.member-avatar {
+ width: 120px;
+ height: 120px;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 3px solid #ff7f27;
+ padding: 3px;
+ margin: 0 auto 1rem;
+}
+
+.member-name {
+ font-size: 1.3rem;
+ font-weight: bold;
+ margin-bottom: 0.5rem;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.member-position {
+ color: rgba(255,255,255,0.7);
+ font-size: 0.9rem;
+ margin-bottom: 1rem;
+}
+
+.contact-item {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ margin-bottom: 1rem;
+ padding: 1rem;
+ background: rgba(0,0,0,0.2);
+ border-radius: 0.8rem;
+ transition: all 0.3s ease;
+}
+
+.contact-item:hover {
+ background: rgba(255,127,39,0.1);
+ transform: translateX(5px);
+}
+
+.mission-box {
+ border-left: 4px solid #ff7f27;
+ padding-left: 1.5rem;
+ margin: 2rem 0;
+ position: relative;
+ opacity: 0;
+ animation: fadeInLeft 0.8s forwards;
+ animation-delay: 0.4s;
+}
+
+.mission-box::before {
+ content: '❝';
+ position: absolute;
+ left: -1.5rem;
+ top: -1rem;
+ font-size: 3rem;
+ color: #ff7f27;
+ opacity: 0.5;
+}
+
+.values-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: 1.5rem;
+ margin: 2rem 0;
+}
+
+.value-item {
+ background: rgba(0,0,0,0.2);
+ border-radius: 0.8rem;
+ padding: 1.5rem;
+ border: 1px solid rgba(255,127,39,0.1);
+ transition: all 0.3s ease;
+ opacity: 0;
+ animation: fadeInRight 0.6s forwards;
+}
+
+.value-item:hover {
+ background: rgba(0,0,0,0.3);
+ border-color: #ff7f27;
+ transform: translateY(-5px);
+ box-shadow: 0 5px 15px rgba(255,127,39,0.2);
+}
+
+.value-icon {
+ background: linear-gradient(145deg, #ff7f27, #ff5500);
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 1rem;
+}
+`;
+
+// Стили для главного контейнера
+const mainContentStyle: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ width: '100%',
+ maxWidth: '90vw',
+ margin: '0 auto',
+ flex: 1,
+ paddingTop: '76px',
+ paddingBottom: '30px',
+};
+
+export default function About() {
+ // Эффект для анимации элементов при прокрутке
+ useEffect(() => {
+ const handleAnimateElements = () => {
+ const elements = document.querySelectorAll('.animate-on-scroll');
+ elements.forEach((el, index) => {
+ if (el instanceof HTMLElement) {
+ el.style.animationDelay = `${index * 0.1}s`;
+ }
+ });
+ };
+
+ handleAnimateElements();
+
+ // Дополнительная анимация при прокрутке (для будущего расширения)
+ const handleScroll = () => {
+ // Будущая логика анимации при прокрутке
+ };
+
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+
+ return (
+
+
+
+
+
+ О нашем проекте
+
+
+ Мы — команда энтузиастов, создающих уникальные культурные события,
+ которые объединяют людей и дарят незабываемые впечатления. Наша платформа
+ помогает находить и приобретать билеты на самые яркие мероприятия вашего города.
+
+
+
+ Наша миссия
+
+
+
+ Мы стремимся сделать культурную жизнь доступной каждому, предоставляя
+ удобный сервис для поиска и покупки билетов на разнообразные мероприятия.
+ Наша цель — создать пространство, где каждый найдет что-то по душе,
+ расширит свой кругозор и получит новые впечатления.
+
+
+
+ Наши ценности
+
+ {[
+ {
+ title: 'Доступность',
+ icon: '✨',
+ desc: 'Мы делаем культурные события доступными для всех',
+ delay: '0.3s'
+ },
+ {
+ title: 'Разнообразие',
+ icon: '🎭',
+ desc: 'Поддерживаем широкий спектр мероприятий для любых интересов',
+ delay: '0.4s'
+ },
+ {
+ title: 'Качество',
+ icon: '🏆',
+ desc: 'Тщательно отбираем события, чтобы предложить лучшее',
+ delay: '0.5s'
+ },
+ {
+ title: 'Инновации',
+ icon: '💡',
+ desc: 'Постоянно совершенствуем нашу платформу и сервисы',
+ delay: '0.6s'
+ }
+ ].map((value, index) => (
+
+
+ {value.icon}
+
+
+ {value.title}
+
+
+ {value.desc}
+
+
+ ))}
+
+
+
+
+ Наша команда
+
+
+ {[
+ {
+ name: 'Вальтер Владислав',
+ position: 'Разработчик',
+ photo: 'https://sun9-73.userapi.com/impg/0L7o8ihONzvGVLWaps-rW_EpVxnF6eRxlkZbmQ/_fnB1AnXl_E.jpg?size=721x1080&quality=95&sign=cc610fe8deefb4ea1a92be84d0df65a7&type=album',
+ delay: '0.2s'
+ },
+ {
+ name: 'Изосимова Татьяна',
+ position: 'Преподователь "Интернет Программирование"',
+ photo: 'https://sun9-5.userapi.com/impg/VLRZ9cfoeqZsX8cND0-_t6UBa2R18ixtbOsrUg/IyYDh9y4_Oo.jpg?size=810x1080&quality=96&sign=ee2565303309ea04e802010d67090ea0&type=album',
+ delay: '0.3s'
+ },
+ {
+ name: 'Володин Сергей',
+ position: 'Преподователь "Разработка корпоритивных информационных систем"',
+ photo: 'https://www.mfua.ru/upload/resize_cache/iblock/294/468_576_1/wd0mv0y90q2thfyzpr71uvzz37lbn7u7.jpg',
+ delay: '0.4s'
+ },
+ {
+ name: 'Пахом',
+ position: 'Талисман',
+ photo: 'https://img.championat.com/i/l/v/16518528031486776581.jpg',
+ delay: '0.5s'
+ }
+ ].map((member, index) => (
+
+
+
{member.name}
+
{member.position}
+
+ {member.name === 'Вальтер Владислав' ? (
+ <>
+
+ {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.3)';
+ e.currentTarget.style.transform = 'translateY(-3px)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.15)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ }}
+ >
+
+
+
+
+
+
+ {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.3)';
+ e.currentTarget.style.transform = 'translateY(-3px)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.15)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ }}
+ >
+
+
+
+
+
+
+
+ {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.3)';
+ e.currentTarget.style.transform = 'translateY(-3px)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.15)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ }}
+ >
+
+
+
+
+
+
+ >
+ ) : member.name === 'Изосимова Татьяна' ? (
+
+ {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.3)';
+ e.currentTarget.style.transform = 'translateY(-3px)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.15)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ }}
+ >
+
+
+
+
+
+ ) : null /* Для Володина Сергея и Пахома не показываем иконки */}
+
+
+ ))}
+
+
+
+ {/* Секция "Связаться с нами" удалена, поскольку эта информация будет на отдельной странице */}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/app/contactsback/MapComponent.tsx b/frontend/src/app/contactsback/MapComponent.tsx
new file mode 100644
index 0000000..ed5dd82
--- /dev/null
+++ b/frontend/src/app/contactsback/MapComponent.tsx
@@ -0,0 +1,86 @@
+import { useEffect, useRef } from 'react';
+import L from 'leaflet';
+import 'leaflet/dist/leaflet.css';
+
+interface MapComponentProps {
+ position: [number, number];
+}
+
+export default function MapComponent({ position }: MapComponentProps) {
+ const mapRef = useRef(null);
+ const mapContainerRef = useRef(null);
+
+ useEffect(() => {
+ // Проверяем, что контейнер существует и карта еще не создана
+ if (!mapRef.current && mapContainerRef.current) {
+ // Создаем карту
+ mapRef.current = L.map(mapContainerRef.current, {
+ center: position,
+ zoom: 15,
+ scrollWheelZoom: false,
+ attributionControl: false,
+ doubleClickZoom: false,
+ dragging: true,
+ keyboard: false,
+ touchZoom: false,
+ });
+
+ // Добавляем слой тайлов
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ attribution: '© OpenStreetMap contributors',
+ }).addTo(mapRef.current);
+
+ // Создаем оранжевый маркер с белой окантовкой
+ const orangeIcon = L.divIcon({
+ html: `
`,
+ className: 'custom-marker',
+ iconSize: [24, 24],
+ iconAnchor: [12, 12]
+ });
+
+ // Добавляем маркер
+ const marker = L.marker(position, { icon: orangeIcon }).addTo(mapRef.current);
+
+ // Добавляем всплывающее окно
+ marker.bindPopup(`
+
+ AboutMe Events
+ Культурные мероприятия и события
+
+ `);
+ }
+
+ // Очищаем ресурсы при размонтировании компонента
+ return () => {
+ if (mapRef.current) {
+ mapRef.current.remove();
+ mapRef.current = null;
+ }
+ };
+ }, [position]);
+
+ return (
+ <>
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/app/contactsback/page.tsx b/frontend/src/app/contactsback/page.tsx
new file mode 100644
index 0000000..ead6900
--- /dev/null
+++ b/frontend/src/app/contactsback/page.tsx
@@ -0,0 +1,485 @@
+'use client';
+import { useEffect, CSSProperties } from 'react';
+import Header from '@/components/Header';
+import Fotter from '@/components/Fotter';
+import dynamic from 'next/dynamic';
+
+// Стили для страницы контактов (аналогичные стилям страницы About)
+const styles = `
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes fadeInRight {
+ from {
+ opacity: 0;
+ transform: translateX(30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes glowPulse {
+ 0% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ text-shadow: 0 0 20px rgba(255,127,39,0.6);
+ }
+ 100% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+@keyframes borderGlow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 15px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+.page-container {
+ background: radial-gradient(circle at top right, rgba(30,30,30,0.8) 0%, rgba(15,15,15,0.8) 100%);
+ min-height: 100vh;
+ position: relative;
+ overflow-x: hidden;
+}
+
+.page-container::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url('/noise-texture.png'), linear-gradient(145deg, rgba(255,127,39,0.1) 0%, rgba(0,0,0,0) 70%);
+ opacity: 0.4;
+ pointer-events: none;
+ z-index: -1;
+}
+
+.content-container {
+ animation: fadeInUp 0.6s ease-out forwards;
+ width: 100%;
+}
+
+.section-title {
+ color: #ff7f27;
+ font-size: 3rem;
+ margin-bottom: 2rem;
+ font-weight: bold;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: glowPulse 3s infinite;
+ text-align: center;
+ letter-spacing: -0.5px;
+}
+
+.section-subtitle {
+ font-size: 1.8rem;
+ margin: 2rem 0 1.5rem;
+ color: #ff7f27;
+ font-weight: 600;
+}
+
+.contact-section {
+ margin-bottom: 3rem;
+ background: rgba(0,0,0,0.3);
+ border-radius: 1.5rem;
+ padding: 4rem; // Увеличиваем внутренний отступ
+ border: 1px solid rgba(255,127,39,0.2);
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+ box-shadow: 0 10px 25px rgba(0,0,0,0.2);
+ width: 100%; // Занимает всю доступную ширину
+}
+
+.contact-intro {
+ text-align: center;
+ max-width: 900px;
+ margin: 0 auto 4rem;
+ line-height: 1.8;
+ font-size: 1.25rem;
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+ animation-delay: 0.2s;
+ color: rgba(255,255,255,0.9);
+}
+
+.contact-item {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+ margin-bottom: 2rem;
+ padding: 2.5rem; // Увеличиваем размер контактных элементов
+ background: rgba(0,0,0,0.2);
+ border-radius: 1rem;
+ transition: all 0.3s ease;
+ opacity: 0;
+ animation: fadeInLeft 0.6s forwards;
+ border-left: 1px solid rgba(255,127,39,0.1);
+}
+
+.contact-item:hover {
+ background: rgba(255,127,39,0.1);
+ transform: translateY(-5px);
+ border-left: 3px solid #ff7f27;
+ box-shadow: 0 10px 20px rgba(0,0,0,0.2);
+}
+
+.contact-item-icon {
+ min-width: 70px;
+ height: 70px;
+ border-radius: 50%;
+ background: rgba(255,127,39,0.15);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s ease;
+}
+
+.contact-item:hover .contact-item-icon {
+ background: rgba(255,127,39,0.3);
+ transform: scale(1.1);
+}
+
+.map-container {
+ width: 100%;
+ height: 700px; // Делаем карту выше
+ border-radius: 1.5rem;
+ overflow: hidden;
+ margin-top: 3rem;
+ border: 1px solid rgba(255,127,39,0.2);
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+ animation-delay: 0.6s;
+ position: relative;
+ box-shadow: 0 15px 30px rgba(0,0,0,0.3);
+}
+
+.map-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ pointer-events: none;
+ box-shadow: inset 0 0 20px 10px rgba(0,0,0,0.2);
+ border-radius: 1.5rem;
+ z-index: 100;
+}
+
+.leaflet-container {
+ width: 100%;
+ height: 100%;
+ border-radius: 1.5rem;
+ z-index: 1;
+}
+
+.social-container {
+ display: flex;
+ justify-content: center;
+ gap: 2rem;
+ margin: 3rem 0;
+}
+
+.social-button {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ background: rgba(255,127,39,0.15);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ opacity: 0;
+ animation: fadeInUp 0.5s forwards;
+}
+
+.social-button:hover {
+ background: rgba(255,127,39,0.3);
+ transform: translateY(-8px);
+ box-shadow: 0 15px 25px rgba(0,0,0,0.3);
+}
+
+@keyframes bounce {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-10px);
+ }
+}
+
+.custom-icon {
+ animation: bounce 2s infinite ease-in-out;
+}
+
+.contact-value {
+ font-size: 1.2rem;
+ color: rgba(255,255,255,0.9);
+ margin-top: 0.3rem;
+}
+
+.contact-title {
+ color: #ff7f27;
+ margin-bottom: 0.5rem;
+ font-size: 1.4rem;
+ font-weight: 600;
+}
+
+.contact-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr); // Фиксируем 2 колонки для более широкого блока
+ gap: 2rem;
+ width: 100%;
+}
+`;
+
+// Стили для главного контейнера
+const mainContentStyle: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ width: '100%',
+ maxWidth: '80%', // Изменено с 95vw на 80%
+ margin: '0 auto',
+ flex: 1,
+ paddingTop: '76px',
+ paddingBottom: '50px',
+};
+
+// Используем только один динамический импорт для нашего компонента карты
+const MapWithNoSSR = dynamic(
+ () => import('./MapComponent'),
+ {
+ ssr: false,
+ loading: () => (
+
+
+
+
+
+
+
+
Загрузка карты...
+
+
+ )
+ }
+);
+
+// Основной компонент страницы
+export default function Contacts() {
+ // Координаты местоположения (Москва)
+ const position: [number, number] = [55.7558, 37.6173];
+
+ useEffect(() => {
+ // Только анимация элементов
+ const handleAnimateElements = () => {
+ const elements = document.querySelectorAll('.animate-on-scroll');
+ elements.forEach((el, index) => {
+ if (el instanceof HTMLElement) {
+ el.style.animationDelay = `${index * 0.1}s`;
+ }
+ });
+ };
+
+ handleAnimateElements();
+ }, []);
+
+ // Контактные данные для отображения
+ const contactInfo = [
+ {
+ icon: 'mail',
+ title: 'Электронная почта',
+ value: 'info@aboutme-events.ru',
+ delay: '0.3s'
+ },
+ {
+ icon: 'phone',
+ title: 'Телефон',
+ value: '+7 (800) 123-45-67',
+ delay: '0.4s'
+ },
+ {
+ icon: 'map',
+ title: 'Адрес',
+ value: 'г. Москва, ул. Творческая, д. 42, офис 314',
+ delay: '0.5s'
+ },
+ {
+ icon: 'clock',
+ title: 'Время работы',
+ value: 'Пн-Пт: 10:00 - 19:00, Сб: 11:00 - 17:00',
+ delay: '0.6s'
+ }
+ ];
+
+ // Данные о социальных сетях
+ const socials = [
+ { name: 'vk', icon: 'vk', delay: '0.4s' },
+ { name: 'telegram', icon: 'telegram', delay: '0.5s' },
+ { name: 'youtube', icon: 'youtube', delay: '0.6s' },
+ { name: 'instagram', icon: 'instagram', delay: '0.7s' }
+ ];
+
+ return (
+
+
+
+
+
+ Контакты
+
+
+ Мы всегда рады общению! Свяжитесь с нами любым удобным способом,
+ и мы ответим на все ваши вопросы о мероприятиях, сотрудничестве
+ или предложениях.
+
+
+
+ Наши контакты
+
+ {/* Заменяем инлайн-стиль на класс */}
+
+ {contactInfo.map((contact, index) => (
+
+
+ {contact.icon === 'mail' && (
+
+
+
+
+ )}
+ {contact.icon === 'phone' && (
+
+
+
+ )}
+ {contact.icon === 'map' && (
+
+
+
+
+ )}
+ {contact.icon === 'clock' && (
+
+
+
+
+ )}
+
+
+
+
+ {contact.title}
+
+
{contact.value}
+
+
+ ))}
+
+
+ Мы в социальных сетях
+
+
+ {socials.map((social, index) => (
+
+ {social.icon === 'vk' && (
+
+
+
+
+
+ )}
+ {social.icon === 'telegram' && (
+
+
+
+
+ )}
+ {social.icon === 'youtube' && (
+
+
+
+
+ )}
+ {social.icon === 'instagram' && (
+
+
+
+
+
+ )}
+
+ ))}
+
+
+ Наше расположение
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/app/favicon.ico b/frontend/src/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/frontend/src/app/favicon.ico differ
diff --git a/frontend/src/app/feedback/page.tsx b/frontend/src/app/feedback/page.tsx
new file mode 100644
index 0000000..f086478
--- /dev/null
+++ b/frontend/src/app/feedback/page.tsx
@@ -0,0 +1,1106 @@
+'use client';
+import React, { useEffect, useState, CSSProperties, useRef } from 'react';
+import Header from '@/components/Header';
+import Fotter from '@/components/Fotter';
+import { format } from 'date-fns';
+import { ru } from 'date-fns/locale';
+
+// Интерфейсы для типизации данных
+interface User {
+ login: string;
+ avatar: string;
+ first_name: string;
+ last_name: string;
+ middle_name?: string;
+}
+
+interface Comment {
+ id: number;
+ userid: number;
+ text: string;
+ date: string;
+ rating: number; // Добавлено поле рейтинга
+ user?: User;
+}
+
+// Стили для страницы Feedback
+const styles = `
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes slideInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes glowPulse {
+ 0% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ text-shadow: 0 0 20px rgba(255,127,39,0.6);
+ }
+ 100% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+@keyframes borderGlow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 15px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+.page-container {
+ background: radial-gradient(circle at top right, rgba(30,30,30,0.8) 0%, rgba(15,15,15,0.8) 100%);
+ min-height: 100vh;
+ position: relative;
+ overflow-x: hidden;
+}
+
+.page-container::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url('/noise-texture.png'), linear-gradient(145deg, rgba(255,127,39,0.1) 0%, rgba(0,0,0,0) 70%);
+ opacity: 0.4;
+ pointer-events: none;
+ z-index: -1;
+}
+
+.content-container {
+ animation: fadeInUp 0.6s ease-out forwards;
+ width: 100%;
+}
+
+.section-title {
+ color: #ff7f27;
+ font-size: 2.5rem;
+ margin-bottom: 1.5rem;
+ font-weight: bold;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: glowPulse 3s infinite;
+ text-align: center;
+ letter-spacing: -0.5px;
+}
+
+.section-intro {
+ text-align: center;
+ max-width: 800px;
+ margin: 0 auto 3rem;
+ line-height: 1.7;
+ font-size: 1.1rem;
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+ animation-delay: 0.2s;
+ color: rgba(255,255,255,0.9);
+}
+
+.comments-container {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ margin: 2rem 0;
+ opacity: 0;
+ animation: fadeIn 0.8s forwards;
+ animation-delay: 0.3s;
+ width: 100%; /* Обеспечиваем полную ширину контейнера */
+}
+
+.comment-card {
+ background: linear-gradient(145deg, rgba(30,30,30,0.6), rgba(15,15,15,0.8));
+ border-radius: 1rem;
+ padding: 2rem; /* Увеличен padding */
+ border: 1px solid rgba(255,127,39,0.2);
+ transition: all 0.3s ease;
+ opacity: 0;
+ animation: slideInLeft 0.5s forwards;
+ position: relative;
+ overflow: hidden;
+ width: 100%; /* Важно для использования всей доступной ширины */
+ box-sizing: border-box; /* Включает padding в общую ширину элемента */
+}
+
+.comment-card:hover {
+ transform: translateY(-3px);
+ border-color: rgba(255,127,39,0.5);
+ box-shadow: 0 8px 20px rgba(0,0,0,0.2);
+}
+
+.comment-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 3px;
+ height: 100%;
+ background: linear-gradient(to bottom, #ff7f27, transparent);
+}
+
+.comment-header {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ margin-bottom: 1rem;
+}
+
+.user-avatar {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 2px solid #ff7f27;
+ padding: 2px;
+ background: rgba(0,0,0,0.2);
+}
+
+.user-info {
+ flex: 1;
+}
+
+.user-name {
+ font-size: 1.1rem;
+ font-weight: bold;
+ margin-bottom: 0.2rem;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.comment-date {
+ font-size: 0.85rem;
+ color: rgba(255,255,255,0.6);
+}
+
+.comment-text {
+ line-height: 1.7; /* Увеличен line-height */
+ color: rgba(255,255,255,0.9);
+ font-size: 1.1rem; /* Увеличен размер шрифта */
+}
+
+.comment-form-container {
+ background: rgba(0,0,0,0.3);
+ border-radius: 1rem;
+ padding: 2rem;
+ border: 1px solid rgba(255,127,39,0.2);
+ margin-top: 3rem;
+ opacity: 0;
+ animation: fadeInUp 0.8s forwards;
+ animation-delay: 0.5s;
+ width: 100%; /* Увеличиваем ширину формы комментария */
+ box-sizing: border-box; /* Включает padding в общую ширину элемента */
+}
+
+.auth-message {
+ text-align: center;
+ padding: 2rem;
+ background: rgba(0,0,0,0.2);
+ border-radius: 1rem;
+ border: 1px dashed rgba(255,127,39,0.3);
+ color: rgba(255,255,255,0.8);
+ font-style: italic;
+}
+
+.form-title {
+ font-size: 1.8rem;
+ margin-bottom: 1.5rem;
+ color: #ff7f27;
+ font-weight: 600;
+ text-align: center;
+}
+
+.comment-textarea {
+ width: 100%;
+ min-height: 180px; /* Увеличена высота */
+ background: rgba(0,0,0,0.2);
+ border: 1px solid rgba(255,127,39,0.4);
+ border-radius: 0.8rem;
+ padding: 1.2rem; /* Увеличен padding */
+ color: #fff;
+ font-size: 1.1rem; /* Увеличен размер шрифта */
+ resize: vertical;
+ margin-bottom: 1.5rem;
+ transition: all 0.3s ease;
+}
+
+.comment-textarea:focus {
+ border-color: #ff7f27;
+ box-shadow: 0 0 0 2px rgba(255,127,39,0.2);
+ outline: none;
+}
+
+.submit-button {
+ background: linear-gradient(145deg, #ff7f27, #ff5500);
+ color: #000;
+ font-weight: bold;
+ padding: 0.8rem 2rem;
+ border: none;
+ border-radius: 0.8rem;
+ cursor: pointer;
+ font-size: 1rem;
+ transition: all 0.3s ease;
+ display: block;
+ margin: 0 auto;
+}
+
+.submit-button:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 5px 15px rgba(255,127,39,0.4);
+}
+
+.submit-button:active {
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(255,127,39,0.4);
+}
+
+.submit-button:disabled {
+ background: rgba(255,127,39,0.3);
+ cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
+}
+
+.comment-action {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ background: rgba(0,0,0,0.3);
+ border: 1px solid rgba(255,255,255,0.2);
+ border-radius: 50%;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.comment-action:hover {
+ background: rgba(255,69,58,0.2);
+ border-color: rgba(255,69,58,0.5);
+}
+
+.loading {
+ text-align: center;
+ padding: 2rem;
+ color: rgba(255,255,255,0.7);
+ font-style: italic;
+}
+
+.error-message {
+ background: rgba(255,69,58,0.1);
+ border: 1px solid rgba(255,69,58,0.3);
+ color: #ff453a;
+ padding: 1rem;
+ border-radius: 0.8rem;
+ margin-bottom: 1.5rem;
+ text-align: center;
+}
+
+.success-message {
+ background: rgba(48,209,88,0.1);
+ border: 1px solid rgba(48,209,88,0.3);
+ color: #30d158;
+ padding: 1rem;
+ border-radius: 0.8rem;
+ margin-bottom: 1.5rem;
+ text-align: center;
+ animation: fadeIn 0.3s forwards;
+}
+
+.empty-comments {
+ text-align: center;
+ padding: 3rem 0;
+ color: rgba(255,255,255,0.7);
+ font-style: italic;
+ background: rgba(0,0,0,0.2);
+ border-radius: 1rem;
+ border: 1px dashed rgba(255,127,39,0.3);
+}
+
+/* Стилизация скроллбара */
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.3);
+ border-radius: 10px;
+ margin: 5px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: linear-gradient(145deg, #ff7f27, #e96c00);
+ border-radius: 10px;
+ border: 2px solid rgba(0, 0, 0, 0.3);
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(145deg, #ff9540, #ff7f27);
+}
+
+::-webkit-scrollbar-corner {
+ background: transparent;
+}
+
+/* Стили для капчи */
+.captcha-container {
+ margin: 0 auto 20px; /* Центрирование блока */
+ width: 35%; /* Уменьшение ширины до 65% */
+ border: 1px solid rgba(255,127,39,0.3);
+ border-radius: 12px;
+ padding: 15px;
+ background: rgba(255,127,39,0.05);
+}
+
+.captcha-canvas {
+ width: 100%;
+ height: 80px;
+ margin-bottom: 10px;
+ border-radius: 8px;
+ background: rgba(0,0,0,0.3);
+ border: 1px solid rgba(255,127,39,0.3);
+}
+
+.captcha-input-container {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 15px;
+}
+
+.captcha-input {
+ flex: 1;
+ padding: 12px 16px;
+ border: 1px solid rgba(255,127,39,0.6);
+ border-radius: 12px;
+ background: rgba(0,0,0,0.2);
+ color: #fff;
+ font-size: 15px;
+ outline: none;
+}
+
+.captcha-button {
+ background: transparent;
+ border: 1px solid #ff7f27;
+ border-radius: 8px;
+ color: #ff7f27;
+ cursor: pointer;
+ padding: 0 15px;
+ transition: all 0.2s ease;
+}
+
+.captcha-button:hover {
+ background: rgba(255,127,39,0.1);
+}
+
+.captcha-verify {
+ background: rgba(255,127,39,0.1);
+ border: 1px solid #ff7f27;
+ border-radius: 8px;
+ color: #ff7f27;
+ cursor: pointer;
+ padding: 0 15px;
+}
+
+.captcha-verify:hover {
+ background: rgba(255,127,39,0.2);
+}
+
+.captcha-success {
+ color: #4caf50;
+ margin-top: 10px;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.captcha-success span {
+ font-size: 16px;
+ font-weight: bold;
+}
+
+/* Обновленные стили для звездочных рейтингов */
+.rating-stars {
+ display: flex;
+ flex-direction: row-reverse; /* Меняем направление отображения */
+ gap: 4px;
+ margin: 8px 0;
+ justify-content: flex-end; /* Выравниваем по правому краю */
+}
+
+.star {
+ color: rgba(255,127,39,0.2);
+ font-size: 20px;
+ transition: all 0.2s ease;
+}
+
+.star.filled {
+ color: #ff7f27;
+}
+
+.rating-input {
+ display: flex;
+ flex-direction: row-reverse; /* Меняем направление на обратное */
+ gap: 8px;
+ margin: 15px 0;
+ justify-content: center;
+}
+
+.rating-input label {
+ cursor: pointer;
+ font-size: 32px; /* Увеличенный размер звездочек */
+ color: rgba(255,127,39,0.2);
+ transition: all 0.3s ease;
+}
+
+/* Исправляем логику подсветки при наведении */
+.rating-input label:hover,
+.rating-input label:hover ~ label {
+ color: #ff7f27;
+ transform: scale(1.1);
+}
+
+.rating-input input:checked ~ label {
+ color: #ff7f27;
+}
+`;
+
+// Стили для главного контейнера
+const mainContentStyle: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ width: '100%',
+ maxWidth: '90%', // Увеличено с 80% до 90%
+ margin: '0 auto',
+ flex: 1,
+ paddingTop: '76px',
+ paddingBottom: '30px',
+};
+
+// Компонент для отображения рейтинга звездочками
+const StarRating = ({ rating }: { rating: number }) => {
+ return (
+
+ {[1, 2, 3, 4, 5].map((star) => (
+
+ ★
+
+ ))}
+
+ );
+};
+
+export default function Feedback() {
+ // Существующие состояния
+ const [comments, setComments] = useState([]);
+ const [commentText, setCommentText] = useState('');
+ const [loading, setLoading] = useState(true);
+ const [submitting, setSubmitting] = useState(false);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(null);
+ const [user, setUser] = useState<{login: string; avatar: string} | null>(null);
+
+ // Новые состояния для капчи
+ const [captchaText, setCaptchaText] = useState('');
+ const [userCaptcha, setUserCaptcha] = useState('');
+ const [captchaValidated, setCaptchaValidated] = useState(false);
+
+ // Новое состояние для рейтинга
+ const [rating, setRating] = useState(5);
+ const [averageRating, setAverageRating] = useState(null);
+
+ // Добавляем реф для canvas
+ const canvasRef = useRef(null);
+
+ // Генерация капчи при загрузке компонента
+ useEffect(() => {
+ // Добавляем таймаут, чтобы убедиться, что DOM загружен
+ const timer = setTimeout(() => {
+ generateCaptcha();
+ }, 500);
+
+ return () => clearTimeout(timer);
+ }, []); // Пустой массив зависимостей означает, что эффект выполняется только при монтировании
+
+ // Улучшаем функцию генерации капчи
+ const generateCaptcha = () => {
+ const canvas = document.getElementById('captchaCanvasFeedback') as HTMLCanvasElement;
+ if (!canvas) {
+ console.error('Canvas element not found!');
+ // Пробуем снова через небольшую задержку
+ setTimeout(generateCaptcha, 200);
+ return;
+ }
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ console.error('Cannot get canvas context!');
+ return;
+ }
+
+ console.log('Generating CAPTCHA...'); // Для отладки
+
+ // Сбрасываем состояния капчи
+ setUserCaptcha('');
+ setCaptchaValidated(false);
+
+ // Остальной код генерации капчи
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Установка фона
+ ctx.fillStyle = 'rgba(0,0,0,0.7)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Генерация случайного текста
+ const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
+ let captchaString = '';
+
+ for (let i = 0; i < 6; i++) {
+ captchaString += chars.charAt(Math.floor(Math.random() * chars.length));
+ }
+
+ setCaptchaText(captchaString);
+ console.log('CAPTCHA set to:', captchaString); // Для отладки
+
+ // Отрисовка текста
+ ctx.font = 'bold 28px Arial';
+ ctx.fillStyle = '#ff7f27';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+
+ // Добавляем искажения для защиты
+ for (let i = 0; i < captchaString.length; i++) {
+ const x = (canvas.width / (captchaString.length + 1)) * (i + 1);
+ const y = canvas.height / 2 + Math.random() * 10 - 5;
+ const angle = Math.random() * 0.4 - 0.2;
+
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(angle);
+ ctx.fillText(captchaString[i], 0, 0);
+ ctx.restore();
+ }
+
+ // Добавляем шум
+ for (let i = 0; i < 100; i++) {
+ ctx.fillStyle = `rgba(255,127,39,${Math.random() * 0.3})`;
+ ctx.fillRect(Math.random() * canvas.width, Math.random() * canvas.height, 2, 2);
+ }
+
+ // Добавляем линии
+ for (let i = 0; i < 4; i++) {
+ ctx.beginPath();
+ ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
+ ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
+ ctx.strokeStyle = `rgba(255,127,39,${Math.random() * 0.5})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+
+ // Сбрасываем ввод пользователя
+ setUserCaptcha('');
+ setCaptchaValidated(false);
+ };
+
+ // Валидация капчи
+ const validateCaptcha = () => {
+ if (userCaptcha.toLowerCase() === captchaText.toLowerCase()) {
+ setCaptchaValidated(true);
+ return true;
+ } else {
+ setCaptchaValidated(false);
+ setError('Неверный код с картинки. Попробуйте еще раз.');
+ generateCaptcha();
+ return false;
+ }
+ };
+
+ // Получаем данные о текущем пользователе из cookie
+ useEffect(() => {
+ const loginCookie = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("login="))
+ ?.split("=")[1];
+
+ const avatarCookie = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("avatar="))
+ ?.split("=")[1];
+
+ if (loginCookie) {
+ try {
+ setUser({
+ login: decodeURIComponent(loginCookie),
+ avatar: avatarCookie ? decodeURIComponent(avatarCookie) : '/default-avatar.png'
+ });
+ } catch (e) {
+ console.error("Ошибка декодирования cookie:", e);
+ }
+ }
+ }, []);
+
+ // Получаем список комментариев с сервера
+ const fetchComments = async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('http://localhost:8000/comments', {
+ method: 'GET',
+ credentials: 'include',
+ });
+
+ if (!response.ok) {
+ throw new Error('Не удалось загрузить комментарии');
+ }
+
+ const data = await response.json();
+
+ // Получаем информацию о пользователях для каждого комментария
+ const commentsWithUsers = await Promise.all(
+ data.map(async (comment: Comment) => {
+ try {
+ const userResponse = await fetch(`http://localhost:8000/user/${comment.userid}`, {
+ credentials: 'include',
+ });
+
+ if (userResponse.ok) {
+ const userData = await userResponse.json();
+ return { ...comment, user: userData };
+ }
+ return comment;
+ } catch (e) {
+ console.error("Ошибка при получении данных пользователя:", e);
+ return comment;
+ }
+ })
+ );
+
+ setComments(commentsWithUsers);
+ } catch (e) {
+ console.error("Ошибка при загрузке комментариев:", e);
+ setError("Не удалось загрузить комментарии. Пожалуйста, попробуйте позже.");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // Получаем средний рейтинг
+ const fetchAverageRating = async () => {
+ try {
+ const response = await fetch('http://localhost:8000/average-rating', {
+ method: 'GET',
+ credentials: 'include',
+ });
+
+ if (!response.ok) {
+ console.error('Не удалось загрузить средний рейтинг');
+ return;
+ }
+
+ const data = await response.json();
+ if (data && data.Рейтинг !== undefined) {
+ setAverageRating(data.Рейтинг);
+ }
+ } catch (e) {
+ console.error("Ошибка при получении среднего рейтинга:", e);
+ }
+ };
+
+ useEffect(() => {
+ fetchComments();
+ fetchAverageRating();
+ }, []);
+
+ // Форматирование даты
+ const formatDate = (dateString: string) => {
+ try {
+ const date = new Date(dateString);
+ return format(date, "d MMMM yyyy, HH:mm", { locale: ru });
+ } catch {
+ return dateString;
+ }
+ };
+
+ // Отправка нового комментария
+ const handleSubmitComment = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!commentText.trim()) return;
+ if (!user) {
+ setError("Необходимо войти в систему, чтобы оставить комментарий");
+ return;
+ }
+
+ // Проверяем капчу
+ if (!captchaValidated && !validateCaptcha()) {
+ return;
+ }
+
+ setSubmitting(true);
+ setError(null);
+
+ try {
+ // Получаем JWT токен из cookie
+ const jwtToken = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("JWT_token="))
+ ?.split("=")[1];
+
+ if (!jwtToken) {
+ throw new Error("Токен авторизации не найден. Пожалуйста, войдите снова.");
+ }
+
+ const userId = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("id="))
+ ?.split("=")[1];
+
+ if (!userId) {
+ throw new Error("ID пользователя не найден. Пожалуйста, войдите снова.");
+ }
+
+ const response = await fetch('http://localhost:8000/comment', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${jwtToken}`
+ },
+ body: JSON.stringify({
+ user_id: parseInt(userId),
+ text: commentText,
+ rating: rating // Добавляем рейтинг в запрос
+ }),
+ credentials: 'include',
+ });
+
+ if (!response.ok) {
+ const data = await response.json();
+ throw new Error(data.detail || 'Не удалось отправить комментарий');
+ }
+
+ setCommentText('');
+ setRating(5); // Сбрасываем рейтинг на 5 звезд
+ setSuccess("Ваш комментарий успешно добавлен!");
+ setCaptchaValidated(false);
+ generateCaptcha();
+
+ // Убираем сообщение об успехе через 3 секунды
+ setTimeout(() => {
+ setSuccess(null);
+ }, 3000);
+
+ // Обновляем список комментариев и средний рейтинг
+ fetchComments();
+ fetchAverageRating();
+ } catch (e) {
+ console.error("Ошибка при отправке комментария:", e);
+ if (e instanceof Error) {
+ setError(e.message);
+ } else {
+ setError("Произошла неизвестная ошибка. Пожалуйста, попробуйте позже.");
+ }
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ // Удаление комментария
+ const handleDeleteComment = async (commentId: number) => {
+ if (!confirm("Вы уверены, что хотите удалить этот комментарий?")) {
+ return;
+ }
+
+ try {
+ // Получаем JWT токен из cookie
+ const jwtToken = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("JWT_token="))
+ ?.split("=")[1];
+
+ if (!jwtToken) {
+ throw new Error("Токен авторизации не найден. Пожалуйста, войдите снова.");
+ }
+
+ const response = await fetch(`http://localhost:8000/comment/${commentId}`, {
+ method: 'DELETE',
+ headers: {
+ 'Authorization': `Bearer ${jwtToken}`
+ },
+ credentials: 'include',
+ });
+
+ if (!response.ok) {
+ const data = await response.json();
+ throw new Error(data.detail || 'Не удалось удалить комментарий');
+ }
+
+ setSuccess("Комментарий успешно удален!");
+
+ // Убираем сообщение об успехе через 3 секунды
+ setTimeout(() => {
+ setSuccess(null);
+ }, 3000);
+
+ // Обновляем список комментариев и средний рейтинг
+ fetchComments();
+ fetchAverageRating();
+ } catch (e) {
+ console.error("Ошибка при удалении комментария:", e);
+ if (e instanceof Error) {
+ setError(e.message);
+ } else {
+ setError("Произошла неизвестная ошибка. Пожалуйста, попробуйте позже.");
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
+ Отзывы наших пользователей
+
+ {/* Отображаем средний рейтинг, если он доступен */}
+ {averageRating !== null && (
+
+
+ Средняя оценка:
+
+
+
+ {averageRating.toFixed(1)}
+
+
+ )}
+
+
+ Мнение наших пользователей — самое ценное для нас. Мы постоянно работаем над улучшением сервиса,
+ учитывая ваши отзывы и предложения. Вот что говорят о нас те, кто уже воспользовался нашим сервисом.
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {success && (
+
+ {success}
+
+ )}
+
+ {loading ? (
+
+ Загрузка комментариев...
+
+ ) : comments.length === 0 ? (
+
+ Пока нет комментариев. Будьте первым, кто оставит отзыв!
+
+ ) : (
+
+ {comments.map((comment, index) => (
+
+
+
{
+ (e.target as HTMLImageElement).src = '/default-avatar.png';
+ }}
+ />
+
+
+
+ {comment.user ? `${comment.user.last_name} ${comment.user.first_name}` : 'Пользователь'}
+
+
+ {formatDate(comment.date)}
+
+ {/* Отображаем рейтинг */}
+
+
+
+ {/* Кнопка удаления (видна только для собственных комментариев) */}
+ {user && user.login === comment.user?.login && (
+
handleDeleteComment(comment.id)}
+ title="Удалить комментарий"
+ >
+
+
+
+
+ )}
+
+
+
+ {comment.text}
+
+
+ ))}
+
+ )}
+
+
+
Поделитесь своим мнением
+
+ {user ? (
+
+ ) : (
+
+ Чтобы оставить отзыв, пожалуйста,
+ {
+ // Вызываем глобальное событие для открытия модального окна логина
+ window.dispatchEvent(new Event('open-login-modal'));
+ }}
+ >
+ войдите в систему
+
+
+ )}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
new file mode 100644
index 0000000..2e4fa96
--- /dev/null
+++ b/frontend/src/app/globals.css
@@ -0,0 +1,26 @@
+@import "tailwindcss";
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0c0000;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx
new file mode 100644
index 0000000..f7fa87e
--- /dev/null
+++ b/frontend/src/app/layout.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "./globals.css";
+
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
new file mode 100644
index 0000000..648b389
--- /dev/null
+++ b/frontend/src/app/page.tsx
@@ -0,0 +1,139 @@
+'use client';
+import Header from '@/components/Header';
+import Footer from '@/components/Fotter';
+import PosterSlider from '@/components/Slideposter';
+import RandomComment from '@/components/RandomComment';
+
+// Дополнительные стили для улучшения внешнего вида
+const styles = `
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes gradientAnimation {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+@keyframes glowPulse {
+ 0% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ text-shadow: 0 0 20px rgba(255,127,39,0.6);
+ }
+ 100% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+.page-container {
+ background: radial-gradient(circle at top right, rgba(30,30,30,0.8) 0%, rgba(15,15,15,0.8) 100%);
+ min-height: 100vh;
+ position: relative;
+ overflow-x: hidden;
+}
+
+.page-container::before {
+ content: '';
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url('/noise-texture.png'), linear-gradient(145deg, rgba(255,127,39,0.1) 0%, rgba(0,0,0,0) 70%);
+ opacity: 0.4;
+ pointer-events: none;
+ z-index: -1;
+}
+
+.content-container {
+ animation: fadeInUp 0.6s ease-out forwards;
+ width: 100%;
+}
+
+.section-title {
+ color: #ff7f27;
+ font-size: 2.5rem;
+ margin-bottom: 1.5rem;
+ font-weight: bold;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: glowPulse 3s infinite;
+ text-align: center;
+}
+
+.welcome-section {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.welcome-text {
+ max-width: 800px;
+ margin: 0 auto;
+ font-size: 1.1rem;
+ line-height: 1.7;
+ color: rgba(255,255,255,0.9);
+ opacity: 0;
+ animation: fadeInUp 0.6s forwards;
+ animation-delay: 0.3s;
+}
+
+/* Добавьте это для улучшения работы модальных окон */
+.modal-backdrop {
+ isolation: isolate;
+ transform: translateZ(0);
+ -webkit-transform: translateZ(0);
+ will-change: backdrop-filter;
+}
+
+@media screen and (max-width: 768px) {
+ .modal-backdrop {
+ backdrop-filter: blur(15px);
+ -webkit-backdrop-filter: blur(15px);
+ }
+}
+`;
+
+// Стили для главного контейнера
+const mainContentStyle = {
+ display: 'flex' as const,
+ flexDirection: 'column' as const,
+ alignItems: 'center' as const,
+ width: '100%',
+ maxWidth: '90vw',
+ margin: '0 auto',
+ flex: 1,
+ paddingTop: '76px',
+ paddingBottom: '30px',
+};
+
+export default function Home() {
+ return (
+
+ );
+}
diff --git a/frontend/src/components/Fotter.tsx b/frontend/src/components/Fotter.tsx
new file mode 100644
index 0000000..5bdb5eb
--- /dev/null
+++ b/frontend/src/components/Fotter.tsx
@@ -0,0 +1,23 @@
+'use client';
+
+export default function Footer() {
+ return (
+
+ Все права опущены во имя свободы
+
+ );
+}
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
new file mode 100644
index 0000000..482319b
--- /dev/null
+++ b/frontend/src/components/Header.tsx
@@ -0,0 +1,508 @@
+// frontend/src/components/Header.tsx
+'use client';
+import React, {
+ useEffect,
+ useState,
+ useCallback,
+ useRef,
+ CSSProperties,
+} from 'react';
+import ReactDOM from 'react-dom/client';
+import LoginModal from './modal/Login';
+import RegisterModal from './modal/Reg';
+import ProfileModal from './modal/Profile';
+import SettingsModal from './modal/Settings';
+
+interface User {
+ login: string;
+ avatar: string;
+}
+
+/* helpers */
+const readCookie = (n: string) => {
+ const value = document.cookie
+ .split('; ')
+ .find((c) => c.startsWith(`${n}=`))
+ ?.split('=')[1];
+
+ // Декодируем URL-кодированное значение
+ if (value) {
+ try {
+ return decodeURIComponent(value);
+ } catch {
+ return value;
+ }
+ }
+
+ return null;
+}
+
+/* palette */
+const ACCENT = 'rgb(240,140,50)';
+
+/* static styles */
+const stHeader: CSSProperties = {
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ width: '100%',
+ height: 56, // Увеличено для лучшей видимости
+ background: 'linear-gradient(to bottom, #111111, #000000)',
+ color: '#fff',
+ padding: '0 20px',
+ zIndex: 1000,
+ display: 'flex',
+ alignItems: 'center',
+ boxShadow: '0 2px 15px rgba(0,0,0,0.5)',
+};
+
+const stLink: CSSProperties = {
+ color: ACCENT,
+ textDecoration: 'none',
+ fontSize: 14,
+ fontWeight: 'bold',
+ transition: 'all .3s',
+ padding: '5px 10px',
+ borderRadius: 8,
+ position: 'relative',
+};
+
+// Улучшенный стиль аватара пользователя
+const stAvatarBase: CSSProperties = {
+ marginLeft: 'auto',
+ marginRight: 10,
+ display: 'flex',
+ alignItems: 'center',
+ position: 'relative',
+ cursor: 'pointer',
+ borderRadius: 24,
+ border: 0,
+ padding: '6px 12px',
+ userSelect: 'none',
+ transition: 'all 0.3s',
+ background: 'linear-gradient(145deg, #ff7f27, #ff5500)',
+ boxShadow: '0 2px 10px rgba(255,127,39,0.4)',
+};
+
+// Улучшенный стиль меню пользователя
+const stMenu: CSSProperties = {
+ position: 'absolute',
+ top: 46,
+ right: 0,
+ background: 'linear-gradient(145deg, #0a0a0a, #1a1a1a)',
+ border: '1px solid #ff7f27',
+ borderRadius: 12,
+ padding: 6,
+ boxShadow: '0 8px 20px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,127,39,0.2)',
+ zIndex: 1000,
+ minWidth: 160,
+ animation: 'menuAppear 0.25s forwards',
+ transformOrigin: 'top right',
+};
+
+// Улучшенный стиль кнопок меню
+const stBtn: CSSProperties = {
+ display: 'block',
+ width: '100%',
+ padding: '10px 16px',
+ background: 'transparent',
+ border: 0,
+ textAlign: 'left',
+ fontSize: 14,
+ fontWeight: 'bold',
+ color: ACCENT,
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ borderRadius: 8,
+ margin: '2px 0',
+};
+
+// Стили для анимаций
+const styles = `
+@keyframes menuAppear {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes buttonGlow {
+ 0% {
+ box-shadow: 0 2px 10px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 2px 15px rgba(255,127,39,0.6);
+ }
+ 100% {
+ box-shadow: 0 2px 10px rgba(255,127,39,0.3);
+ }
+}
+
+@keyframes linkPulse {
+ 0% {
+ text-shadow: 0 0 3px rgba(255,127,39,0);
+ }
+ 50% {
+ text-shadow: 0 0 8px rgba(255,127,39,0.5);
+ }
+ 100% {
+ text-shadow: 0 0 3px rgba(255,127,39,0);
+ }
+}
+
+@keyframes avatarGlow {
+ 0% {
+ box-shadow: 0 2px 10px rgba(255,127,39,0.4);
+ }
+ 50% {
+ box-shadow: 0 2px 20px rgba(255,127,39,0.7);
+ }
+ 100% {
+ box-shadow: 0 2px 10px rgba(255,127,39,0.4);
+ }
+}
+`;
+
+// Улучшенный компонент кнопки меню
+const MenuButton = ({ label, onClick }: { label: string; onClick?: () => void }) => {
+ const [h, setH] = useState(false);
+ return (
+ setH(true)}
+ onMouseLeave={() => setH(false)}
+ onMouseDown={() => setH(false)}
+ onMouseUp={() => setH(true)}
+ onClick={onClick}
+ >
+ {label}
+
+ );
+};
+
+// Улучшенный компонент меню пользователя
+function UserMenu({
+ user,
+ onLogout,
+ onProfile,
+ onSettings,
+}: {
+ user: User;
+ onLogout: () => void;
+ onProfile: () => void;
+ onSettings: () => void;
+}) {
+ const [open, setOpen] = useState(false);
+ const [tone, setTone] = useState<'idle' | 'hover' | 'active'>('idle');
+ const ref = useRef(null);
+ const timer = useRef | null>(null);
+
+ useEffect(() => {
+ const close = (e: globalThis.MouseEvent) =>
+ ref.current && !ref.current.contains(e.target as Node) && setOpen(false);
+ document.addEventListener('mousedown', close);
+ return () => document.removeEventListener('mousedown', close);
+ }, []);
+
+ const startHide = () => (timer.current = setTimeout(() => setOpen(false), 300));
+ const stopHide = () => {
+ if (timer.current) clearTimeout(timer.current);
+ };
+
+ const avatarStyle: CSSProperties = {
+ height: 36,
+ width: 36,
+ borderRadius: '50%',
+ border: '2px solid #000',
+ objectFit: 'cover',
+ boxShadow: '0 2px 5px rgba(0,0,0,0.3)',
+ transition: 'transform 0.2s',
+ transform: open ? 'scale(1.1)' : 'scale(1)',
+ };
+
+ const bg = tone === 'active'
+ ? 'linear-gradient(145deg, #e96c00, #cc5e00)'
+ : tone === 'hover'
+ ? 'linear-gradient(145deg, #ff7f27, #e96c00)'
+ : 'linear-gradient(145deg, #ff7f27, #ff5500)';
+
+ const boxShadow = tone === 'hover' || open
+ ? '0 4px 15px rgba(255,127,39,0.5)'
+ : '0 2px 10px rgba(255,127,39,0.4)';
+
+ return (
+ setOpen((v) => !v)}
+ onMouseEnter={() => setTone('hover')}
+ onMouseLeave={() => setTone('idle')}
+ onMouseDown={() => setTone('active')}
+ onMouseUp={() => setTone('hover')}
+ >
+
+
+ {user.login}
+
+
+ {open && (
+
+ )}
+
+ );
+}
+
+export default function Header() {
+ const [user, setUser] = useState(null);
+ const [ready, setReady] = useState(false);
+
+ /* cookies → state */
+ useEffect(() => {
+ const login = readCookie('login');
+ const avatar = readCookie('avatar');
+ if (login && avatar)
+ setUser({
+ login,
+ avatar: avatar.replace(/\\\\/g, '/').replace(/"/g, ''),
+ });
+ setReady(true);
+ }, []);
+
+ /* live updates (аватар, логин) */
+ useEffect(() => {
+ const f = (e: Event) =>
+ setUser((u) => (u ? { ...u, ...(e as CustomEvent).detail } : u));
+ window.addEventListener('user-update', f);
+ return () => window.removeEventListener('user-update', f);
+ }, []);
+
+ /* -------- modal helpers -------- */
+const openPortal = (cmp: React.ReactElement) => {
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const root = ReactDOM.createRoot(host);
+ const close = () => {
+ root.unmount();
+ host.remove();
+ };
+ root.render(cmp);
+ return close;
+};
+
+ const openLoginModal = useCallback(() => {
+ const close = openPortal(
+ {
+ setUser(u);
+ window.dispatchEvent(new CustomEvent('user-update', { detail: u }));
+ }}
+ onClose={() => close()}
+ onRegisterClick={() => {
+ close(); // Закрываем окно логина
+ openRegisterModal(); // Открываем окно регистрации
+ }}
+ />,
+ );
+ return close;
+ }, []);
+
+ const openRegisterModal = useCallback(() => {
+ const close = openPortal(
+ {
+ setUser(u);
+ window.dispatchEvent(new CustomEvent('user-update', { detail: u }));
+ }}
+ onClose={() => close()}
+ onLoginClick={() => {
+ close(); // Закрываем окно регистрации
+ openLoginModal(); // Открываем окно логина
+ }}
+ />,
+ );
+ return close;
+ }, []);
+
+ const openSettingsModal = useCallback(() => {
+ if (!user) return;
+
+ // Получаем id из cookie (removed unused userId variable)
+
+ const close = openPortal(
+ close()}
+ onUpdate={(patch) => setUser((u) => (u ? { ...u, ...patch } : u))}
+ />,
+ );
+ return close; // Важно вернуть функцию закрытия!
+ }, [user]);
+
+ const openProfileModal = useCallback(() => {
+ if (!user) return;
+
+ // Получаем id из cookie
+ // Removed unused userId variable assignment
+
+ const close = openPortal(
+ close()}
+ onSettings={() => {
+ close();
+ openSettingsModal();
+ }}
+ />,
+ );
+ return close; // Важно вернуть функцию закрытия!
+ }, [user, openSettingsModal]);
+
+ const handleLogout = useCallback(async () => {
+ await fetch('http://localhost:8000/logout', { method: 'POST', credentials: 'include' });
+ ['login', 'avatar', 'full_name', 'email', 'id', 'JWT_token'].forEach(
+ (n) => (document.cookie = `${n}=; Max-Age=0; path=/`),
+ );
+ setUser(null);
+ }, []);
+
+ if (!ready) return null;
+
+ return (
+
+
+ e.preventDefault()}
+ onDragStart={(e) => e.preventDefault()}
+ />
+
+
+ БИЛЕТопад
+
+
+
+ {Object.entries({ Афиша: '/', 'О нас': '/about', Отзывы: '/feedback', Контакты: '/contacts' }).map(
+ ([txt, href]) => (
+ {
+ e.currentTarget.style.background = 'rgba(255,127,39,0.1)';
+ e.currentTarget.style.animation = 'linkPulse 2s infinite';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.background = 'transparent';
+ e.currentTarget.style.animation = 'none';
+ }}
+ onContextMenu={(e) => e.preventDefault()}
+ >
+ {txt}
+
+ ),
+ )}
+
+
+ {user ? (
+
+ ) : (
+ {
+ e.currentTarget.style.background = 'linear-gradient(145deg, #ff7f27, #e96c00)';
+ e.currentTarget.style.transform = 'translateY(-1px)';
+ e.currentTarget.style.boxShadow = '0 4px 15px rgba(255,127,39,0.5)';
+ e.currentTarget.style.animation = 'buttonGlow 2s infinite';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.background = 'linear-gradient(145deg, #ff7f27, #ff5500)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ e.currentTarget.style.boxShadow = '0 2px 10px rgba(255,127,39,0.3)';
+ e.currentTarget.style.animation = 'none';
+ }}
+ onMouseDown={(e) => {
+ e.currentTarget.style.background = 'linear-gradient(145deg, #e96c00, #cc5e00)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ e.currentTarget.style.boxShadow = '0 2px 5px rgba(255,127,39,0.2)';
+ }}
+ onMouseUp={(e) => {
+ e.currentTarget.style.background = 'linear-gradient(145deg, #ff7f27, #e96c00)';
+ e.currentTarget.style.transform = 'translateY(-1px)';
+ e.currentTarget.style.boxShadow = '0 4px 15px rgba(255,127,39,0.5)';
+ }}
+ >
+
+ Войти
+
+ )}
+
+ );
+}
diff --git a/frontend/src/components/RandomComment.tsx b/frontend/src/components/RandomComment.tsx
new file mode 100644
index 0000000..31636c4
--- /dev/null
+++ b/frontend/src/components/RandomComment.tsx
@@ -0,0 +1,494 @@
+'use client';
+import { useState, useEffect } from 'react';
+import { format } from 'date-fns';
+import { ru } from 'date-fns/locale';
+
+// Интерфейсы для типизации данных
+interface User {
+ login: string;
+ avatar: string;
+ first_name: string;
+ last_name: string;
+ middle_name?: string;
+}
+
+interface Comment {
+ id: number;
+ userid: number;
+ text: string;
+ date: string;
+ rating: number; // Добавляем поле рейтинга
+ user?: User;
+}
+
+// Компонент для отображения звездочек
+const StarRating = ({ rating }: { rating: number }) => {
+ return (
+
+ {[1, 2, 3, 4, 5].map((star) => (
+
+ ★
+
+ ))}
+
+ );
+};
+
+const DEMO_AVERAGE_RATING = 4.7; // Демонстрационное значение среднего рейтинга
+
+const RandomComment = () => {
+ const [comments, setComments] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [averageRating, setAverageRating] = useState(DEMO_AVERAGE_RATING); // Устанавливаем начальное значение
+ const [ratingLoaded, setRatingLoaded] = useState(false);
+
+ // Загрузка комментариев с сервера
+ useEffect(() => {
+ const fetchRandomComments = async () => {
+ try {
+ setLoading(true);
+ // Получаем все комментарии
+ const response = await fetch('http://localhost:8000/comments', {
+ credentials: 'include',
+ });
+
+ if (!response.ok) {
+ throw new Error('Не удалось загрузить комментарии');
+ }
+
+ const allComments = await response.json();
+
+ // Выбираем случайные комментарии (от 1 до 4)
+ const shuffled = [...allComments].sort(() => 0.5 - Math.random());
+ const count = Math.floor(Math.random() * 4) + 1; // от 1 до 4
+ const selectedComments = shuffled.slice(0, Math.min(count, shuffled.length));
+
+ // Добавляем информацию о пользователях
+ const commentsWithUsers = await Promise.all(
+ selectedComments.map(async (comment: Comment) => {
+ try {
+ const userResponse = await fetch(`http://localhost:8000/user/${comment.userid}`, {
+ credentials: 'include',
+ });
+
+ if (userResponse.ok) {
+ const userData = await userResponse.json();
+ return { ...comment, user: userData };
+ }
+ return comment;
+ } catch (e) {
+ console.error("Ошибка при получении данных пользователя:", e);
+ return comment;
+ }
+ })
+ );
+
+ setComments(commentsWithUsers);
+ } catch (error) {
+ console.error('Ошибка при загрузке комментариев:', error);
+ setError('Не удалось загрузить отзывы');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchRandomComments();
+ }, []);
+
+ // Загружаем средний рейтинг
+ useEffect(() => {
+ const fetchAverageRating = async () => {
+ try {
+ // Добавляем сигнал для прерывания запроса через 5 секунд
+ const controller = new AbortController();
+ const signal = controller.signal;
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
+
+ const response = await fetch('http://localhost:8000/average-rating', {
+ credentials: 'include',
+ signal
+ });
+
+ clearTimeout(timeoutId);
+
+ if (!response.ok) {
+ console.warn('Не удалось загрузить средний рейтинг с сервера');
+ return;
+ }
+
+ const data = await response.json();
+ if (data && data.Рейтинг !== undefined) {
+ setAverageRating(data.Рейтинг);
+ setRatingLoaded(true);
+ }
+ } catch (e) {
+ console.warn("Ошибка при получении среднего рейтинга:", e);
+ // При ошибке оставляем демо-значение
+ }
+ };
+
+ fetchAverageRating();
+ }, []);
+
+ // Форматирование даты в читаемый вид
+ const formatDate = (dateString: string) => {
+ try {
+ const date = new Date(dateString);
+ return format(date, "d MMMM yyyy, HH:mm", { locale: ru });
+ } catch {
+ return dateString;
+ }
+ };
+
+ // Обрезка длинного текста комментария
+ const truncateText = (text: string, maxLength: number = 150) => {
+ if (text.length <= maxLength) return text;
+ return text.substring(0, maxLength) + '...';
+ };
+
+ // Обновленные стили для карточек комментариев
+ const styles = `
+ @keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes glow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 15px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ }
+
+ @keyframes glowPulse {
+ 0% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ text-shadow: 0 0 20px rgba(255,127,39,0.6);
+ }
+ 100% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ }
+
+ .random-comments-container {
+ width: 100%;
+ margin: 3rem auto 2rem;
+ padding: 0;
+ max-width: 1400px;
+ }
+
+ .random-comments-title {
+ color: #ff7f27;
+ font-size: 2.5rem;
+ margin-bottom: 1.5rem;
+ font-weight: bold;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: glowPulse 3s infinite;
+ text-align: center;
+ letter-spacing: -0.5px;
+ }
+
+ .comments-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 20px;
+ width: 100%;
+ justify-content: center;
+ }
+
+ .comment-card {
+ background: linear-gradient(145deg, rgba(30,30,30,0.6), rgba(15,15,15,0.8));
+ border-radius: 1.2rem;
+ border: 1px solid rgba(255,127,39,0.2);
+ padding: 1.5rem 1rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+ animation: fadeInUp 0.5s forwards;
+ animation-delay: calc(var(--index) * 0.1s);
+ transform: translateY(20px);
+ opacity: 0;
+ height: 100%;
+ min-height: 320px;
+ max-width: none;
+ margin: 0;
+ }
+
+ .comment-card:hover {
+ transform: translateY(-5px);
+ border-color: rgba(255,127,39,0.5);
+ box-shadow: 0 8px 20px rgba(0,0,0,0.2);
+ z-index: 1;
+ }
+
+ .comment-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 3px;
+ background: linear-gradient(to right, transparent, #ff7f27, transparent);
+ }
+
+ .avatar-container {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+ overflow: hidden;
+ border: 3px solid #ff7f27;
+ padding: 2px;
+ background: rgba(0,0,0,0.2);
+ margin-bottom: 1rem;
+ box-shadow: 0 5px 15px rgba(255,127,39,0.3);
+ }
+
+ .user-avatar {
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ object-fit: cover;
+ }
+
+ .user-info {
+ margin-bottom: 0.4rem;
+ width: 100%;
+ }
+
+ .user-name {
+ font-size: 1.2rem;
+ font-weight: bold;
+ margin-bottom: 0.2rem;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+
+ .comment-date {
+ font-size: 0.8rem;
+ color: rgba(255,255,255,0.6);
+ }
+
+ .comment-text {
+ line-height: 1.5;
+ color: rgba(255,255,255,0.85);
+ font-size: 0.95rem;
+ position: relative;
+ margin-top: 0.5rem;
+ font-style: italic;
+ }
+
+ .quote-icon {
+ position: absolute;
+ bottom: 10px;
+ right: 10px;
+ opacity: 0.1;
+ color: #ff7f27;
+ font-size: 24px;
+ transform: rotate(180deg);
+ }
+
+ .loading-container {
+ text-align: center;
+ padding: 2rem;
+ font-style: italic;
+ color: rgba(255,255,255,0.6);
+ }
+
+ /* Обновленные стили для среднего рейтинга - сделаем его более заметным */
+ .average-rating-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 15px;
+ margin: -5px auto 30px;
+ animation: fadeInUp 0.6s forwards;
+ background: rgba(0,0,0,0.3);
+ border-radius: 15px;
+ padding: 15px 30px;
+ max-width: 400px;
+ box-shadow: 0 0 20px rgba(255,127,39,0.15);
+ border: 1px solid rgba(255,127,39,0.2);
+ }
+
+ .average-rating-container:hover {
+ animation: glow 1.5s infinite; /* Добавляем анимацию при наведении */
+ border-color: rgba(255,127,39,0.3);
+ transform: translateY(-2px);
+ }
+
+ .rating-stars {
+ display: flex;
+ gap: 6px;
+ }
+
+ .star {
+ color: rgba(255,127,39,0.2);
+ font-size: 28px; /* Увеличенный размер звезд */
+ transition: all 0.2s ease;
+ }
+
+ .star.filled {
+ color: #ff7f27;
+ }
+
+ .average-rating-label {
+ color: rgba(255,255,255,0.9);
+ font-size: 1.25rem;
+ font-weight: 500;
+ }
+
+ .average-rating-value {
+ color: #ff7f27;
+ font-weight: bold;
+ font-size: 1.5rem;
+ text-shadow: 0 0 10px rgba(255,127,39,0.4);
+ }
+
+ /* Новый стиль для контейнера рейтинга пользователя */
+ .user-rating {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ margin: 10px 0 15px;
+ }
+
+ @media (max-width: 768px) {
+ .comments-grid {
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ }
+ }
+
+ @media (max-width: 480px) {
+ .comments-grid {
+ grid-template-columns: 1fr;
+ gap: 15px;
+ }
+
+ .avatar-container {
+ width: 90px;
+ height: 90px;
+ }
+ }
+ `;
+
+ if (loading) {
+ return (
+
+
+
+ Загрузка отзывов...
+
+
+ );
+ }
+
+ if (error || comments.length === 0) {
+ return (
+
+
+ Отзывы пользователей
+
+ {/* Отображаем средний рейтинг даже если нет комментариев */}
+
+
+
+
+ {averageRating.toFixed(1)}
+
+
+
+ {error ? (
+
+ Произошла ошибка при загрузке отзывов.
+
+ ) : (
+
+ Отзывы пока отсутствуют. Будьте первым!
+
+ )}
+
+ );
+ }
+
+ return (
+
+
+ Отзывы пользователей
+
+
+
+
+ {comments.map((comment, index) => (
+
+ {/* Аватар вверху */}
+
+
{
+ (e.target as HTMLImageElement).src = '/default-avatar.png';
+ }}
+ />
+
+
+ {/* Имя и дата под аватаром */}
+
+
+ {comment.user ? `${comment.user.first_name} ${comment.user.last_name.charAt(0)}.` : 'Пользователь'}
+
+
+ {formatDate(comment.date)}
+
+
+
+ {/* Рейтинг в отдельном блоке по центру */}
+ {comment.rating > 0 && (
+
+
+
+ )}
+
+ {/* Текст отзыва внизу */}
+
+ "{truncateText(comment.text)}"
+
+
+
❝
+
+ ))}
+
+
+ );
+};
+
+export default RandomComment;
\ No newline at end of file
diff --git a/frontend/src/components/Slideposter.tsx b/frontend/src/components/Slideposter.tsx
new file mode 100644
index 0000000..82c77ad
--- /dev/null
+++ b/frontend/src/components/Slideposter.tsx
@@ -0,0 +1,620 @@
+'use client';
+import { useState, useEffect, useRef } from 'react';
+import { format } from 'date-fns';
+import { ru } from 'date-fns/locale';
+import PosterModal from './modal/Poster';
+import ReactDOM from 'react-dom/client';
+
+interface Poster {
+ id: number;
+ title: string;
+ description: string;
+ image: string;
+ date: string | null;
+ price: number;
+ like: number;
+}
+
+interface DetailedPosterData {
+ id: number;
+ img: string; // адаптируем имя поля для модального окна
+ title: string;
+ likes: number; // адаптируем имя поля для модального окна
+ desc: string; // адаптируем имя поля для модального окна
+ price: number;
+ date: string;
+}
+
+const PosterSlider = () => {
+ const [posters, setPosters] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [translateX, setTranslateX] = useState(0);
+ const [activeIndex, setActiveIndex] = useState(0);
+ const sliderRef = useRef(null);
+ const containerRef = useRef(null);
+
+ // Состояния для модального окна
+ // Removed unused isModalOpen state
+ // Removed unused selectedPoster state
+ const [loadingPosterDetails, setLoadingPosterDetails] = useState(false);
+
+ // Загрузка данных с сервера
+ useEffect(() => {
+ const fetchPosters = async () => {
+ try {
+ setLoading(true);
+ const response = await fetch('http://localhost:8000/getallposter', {
+ credentials: 'include'
+ });
+
+ if (!response.ok) {
+ throw new Error('Не удалось загрузить постеры');
+ }
+
+ const data = await response.json();
+ setPosters(data.posters);
+ } catch (error) {
+ console.error('Ошибка при загрузке постеров:', error);
+ setError('Не удалось загрузить постеры. Пожалуйста, попробуйте позже.');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchPosters();
+ }, []);
+
+ // Добавляем функцию для создания портала
+ const openPortal = (cmp: React.ReactElement) => {
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const root = ReactDOM.createRoot(host);
+ const close = () => {
+ root.unmount();
+ host.remove();
+ };
+ root.render(cmp);
+ return close;
+ };
+
+ // Модифицируем функцию для открытия модального окна
+ const handlePosterClick = (poster: Poster) => {
+ setLoadingPosterDetails(true);
+
+ fetchPosterDetails(poster.id)
+ .then(posterData => {
+ if (posterData) {
+ const closeModal = openPortal(
+ {
+ closeModal();
+ // Удаляем эту строку, т.к. функция setSelectedPoster больше не существует
+ // setSelectedPoster(null);
+ }}
+ />
+ );
+ }
+ })
+ .finally(() => {
+ setLoadingPosterDetails(false);
+ });
+ };
+
+ // Модифицируем функцию загрузки деталей постера
+ const fetchPosterDetails = async (posterId: number): Promise => {
+ try {
+ const response = await fetch(`http://localhost:8000/getposter/${posterId}`, {
+ credentials: 'include'
+ });
+
+ if (!response.ok) {
+ throw new Error('Не удалось загрузить информацию о мероприятии');
+ }
+
+ const posterData = await response.json();
+
+ // Адаптируем данные для модального окна
+ return {
+ id: posterData.id,
+ img: posterData.image,
+ title: posterData.title,
+ likes: posterData.like,
+ desc: posterData.description,
+ price: posterData.price,
+ date: posterData.date || '',
+ };
+ } catch (error) {
+ console.error('Ошибка при загрузке детальной информации о постере:', error);
+ alert('Не удалось загрузить информацию о мероприятии. Пожалуйста, попробуйте позже.');
+ return null;
+ }
+ };
+
+ // Установка начального активного слайда и обновление при изменении размера окна
+ useEffect(() => {
+ if (containerRef.current && posters.length > 0) {
+ // Устанавливаем начальный активный индекс в центр, если есть достаточно постеров
+ const initialIndex = Math.min(Math.floor(posters.length / 2), 1);
+ centerActiveSlide(initialIndex);
+ }
+
+ const handleResize = () => {
+ centerActiveSlide(activeIndex);
+ };
+
+ window.addEventListener('resize', handleResize);
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, [posters]);
+
+ // Пересчет позиции при изменении активного индекса
+ useEffect(() => {
+ if (containerRef.current && posters.length > 0) {
+ centerActiveSlide(activeIndex);
+ }
+ }, [activeIndex]);
+
+ // Форматирование даты и цены
+ const formatDate = (dateStr: string | null) => {
+ if (!dateStr) return 'Дата не указана';
+ const date = new Date(dateStr);
+ return format(date, "d MMMM yyyy, HH:mm", { locale: ru });
+ };
+
+ const formatPrice = (price: number) => {
+ return price.toLocaleString('ru-RU', {
+ style: 'currency',
+ currency: 'RUB',
+ maximumFractionDigits: 0
+ });
+ };
+
+ // Улучшенная функция центрирования активного слайда
+ const centerActiveSlide = (index: number) => {
+ if (!sliderRef.current || !containerRef.current || posters.length === 0) return;
+
+ const containerWidth = containerRef.current.clientWidth;
+ const cardWidth = 320; // Ширина карточки + отступ
+
+ // Строго центрируем выбранную карточку
+ const offset = (containerWidth / 2) - (cardWidth / 2);
+ let newTranslateX = (index * cardWidth) - offset;
+
+ // Проверяем, достаточно ли слайдов для прокрутки
+ const totalSlidesWidth = posters.length * cardWidth;
+ const canSlide = totalSlidesWidth > containerWidth;
+
+ // Если слайдов мало или они помещаются на экране - центрируем контейнер целиком
+ if (!canSlide) {
+ // Вычисляем смещение для центрирования группы карточек
+ const centeringOffset = (containerWidth - totalSlidesWidth) / 2;
+ newTranslateX = 0; // Нет смещения
+
+ // Применяем центрирующее смещение через стиль margin вместо transform
+ if (sliderRef.current) {
+ sliderRef.current.style.marginLeft = `${centeringOffset}px`;
+ }
+ } else {
+ // Возвращаем margin к нормальному значению
+ if (sliderRef.current) {
+ sliderRef.current.style.marginLeft = '0px';
+ }
+
+ // Ограничиваем максимальный сдвиг для прокрутки
+ const maxTranslateX = Math.max(0, (cardWidth * posters.length) - containerWidth);
+ newTranslateX = Math.max(0, Math.min(newTranslateX, maxTranslateX));
+ }
+
+ // Важное изменение: для первого и последнего слайда делаем особое смещение
+ if (index === 0) {
+ newTranslateX = 0; // Смещение для первого слайда
+ } else if (index === posters.length - 1 && canSlide) {
+ newTranslateX = totalSlidesWidth - containerWidth; // Смещение для последнего слайда
+ }
+
+ setTranslateX(newTranslateX);
+ setActiveIndex(index);
+ };
+
+ // Обработка колесика мыши для прокрутки слайдера
+ const handleWheel = (e: React.WheelEvent) => {
+ e.preventDefault(); // Предотвращаем стандартную прокрутку страницы
+
+ if (e.deltaY > 0 && activeIndex < posters.length - 1) {
+ // Прокрутка вниз/вправо
+ setActiveIndex(prevIndex => Math.min(prevIndex + 1, posters.length - 1));
+ } else if (e.deltaY < 0 && activeIndex > 0) {
+ // Прокрутка вверх/влево
+ setActiveIndex(prevIndex => Math.max(prevIndex - 1, 0));
+ }
+ };
+
+ // Обработка наведения и клика
+ const handlePosterHover = (index: number) => {
+ setActiveIndex(index); // Активация слайда при наведении
+ };
+
+ // Закрытие модального окна (удалено, так как не используется)
+
+ // Расчет дистанции от активного слайда для эффекта затемнения
+ const calculateDimFactor = (index: number) => {
+ if (index === activeIndex) return 1; // Активный слайд на 100% яркий
+
+ const distance = Math.abs(index - activeIndex);
+
+ // Чем дальше от активного, тем больше затемнение
+ if (distance === 1) return 0.5; // Соседние слайды на 50% яркости
+ if (distance === 2) return 0.3; // Слайды через один на 30% яркости
+
+ return 0.2; // Остальные слайды почти невидимы
+ };
+
+ // Стили для компонентов с прозрачным фоном
+ const styles = `
+ @keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes pulseGlow {
+ 0% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ text-shadow: 0 0 20px rgba(255,127,39,0.6);
+ }
+ 100% {
+ text-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ }
+
+ .slider-section {
+ width: 90vw;
+ margin: 0 auto;
+ position: relative;
+ padding: 20px 0 40px;
+ background: transparent;
+ }
+
+ .slider-title {
+ font-size: 2.2rem;
+ margin-bottom: 1.8rem;
+ font-weight: bold;
+ text-align: center;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: pulseGlow 3s infinite;
+ letter-spacing: -0.5px;
+ }
+
+ .slider-container-outer {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+ padding: 40px 0;
+ background: transparent;
+ }
+
+ .slider-container {
+ position: relative;
+ overflow: visible;
+ width: 100%;
+ margin: 0 auto;
+ padding: 0;
+ background: transparent;
+ }
+
+ .slider-wrapper {
+ display: flex;
+ transition: transform 0.7s cubic-bezier(0.33, 1, 0.68, 1);
+ padding-bottom: 15px;
+ will-change: transform;
+ }
+
+ .poster-card {
+ flex: 0 0 300px;
+ height: 420px;
+ margin-right: 20px;
+ border-radius: 16px;
+ overflow: hidden;
+ position: relative;
+ cursor: pointer;
+ transition: all 0.4s cubic-bezier(0.2, 0, 0.2, 1), filter 0.4s ease;
+ animation: fadeIn 0.5s forwards;
+ background: linear-gradient(145deg, #0a0a0a, #1a1a1a);
+ border: 1px solid rgba(255,127,39,0.2);
+ filter: brightness(0.3) saturate(0.3);
+ transform-origin: center center;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
+ will-change: transform, filter, box-shadow;
+ }
+
+ .poster-card.active {
+ filter: brightness(1) saturate(1) !important; /* Яркий активный слайд */
+ border: 1px solid rgba(255,127,39,1);
+ transform: translateY(-8px) scale(1.08); /* Увеличенное выделение */
+ box-shadow: 0 10px 30px rgba(0,0,0,0.4), 0 0 25px rgba(255,127,39,0.4);
+ z-index: 11 !important; /* Всегда поверх остальных */
+ }
+
+ .poster-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ position: absolute;
+ z-index: 1;
+ transition: all 0.4s ease;
+ }
+
+ .poster-gradient {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 70%;
+ background: linear-gradient(to top, rgba(0,0,0,0.95) 10%, rgba(0,0,0,0.7) 50%, rgba(0,0,0,0) 100%);
+ z-index: 2;
+ }
+
+ .poster-content {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 20px;
+ color: #fff;
+ z-index: 3;
+ transform: translateY(0);
+ transition: transform 0.4s ease;
+ }
+
+ .poster-card.active .poster-content {
+ transform: translateY(-5px);
+ }
+
+ .poster-title {
+ font-size: 1.5rem;
+ font-weight: bold;
+ margin-bottom: 8px;
+ color: #ff7f27;
+ text-shadow: 0 2px 4px rgba(0,0,0,0.5);
+ transition: color 0.3s ease;
+ }
+
+ .poster-card.active .poster-title {
+ color: #ffa54f;
+ }
+
+ .poster-description {
+ font-size: 0.9rem;
+ margin-bottom: 16px;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ opacity: 0.8;
+ transition: opacity 0.3s ease;
+ }
+
+ .poster-card.active .poster-description {
+ opacity: 1;
+ }
+
+ .poster-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 12px;
+ }
+
+ .poster-price {
+ font-size: 1.2rem;
+ font-weight: bold;
+ color: #ff7f27;
+ transition: all 0.3s ease;
+ }
+
+ .poster-card.active .poster-price {
+ transform: scale(1.05);
+ }
+
+ .poster-date {
+ font-size: 0.85rem;
+ color: rgba(255,255,255,0.7);
+ }
+
+ /* Индикаторы для навигации */
+ .slider-indicators {
+ display: flex;
+ justify-content: center;
+ margin-top: 20px;
+ gap: 8px;
+ }
+
+ .slider-indicator {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: rgba(255,127,39,0.2);
+ cursor: pointer;
+ transition: all 0.3s ease;
+ }
+
+ .slider-indicator.active {
+ width: 20px;
+ border-radius: 5px;
+ background: #ff7f27;
+ }
+
+ .loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 300px;
+ width: 100%;
+ }
+
+ .loading-spinner {
+ width: 50px;
+ height: 50px;
+ border: 4px solid rgba(255,127,39,0.3);
+ border-radius: 50%;
+ border-top-color: #ff7f27;
+ animation: spin 1s infinite linear;
+ }
+
+ @keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+ }
+
+ .error-container {
+ text-align: center;
+ padding: 50px;
+ color: #ff453a;
+ background: rgba(255,69,58,0.1);
+ border: 1px solid rgba(255,69,58,0.3);
+ border-radius: 16px;
+ margin: 0 60px;
+ }
+
+ .title {
+ color: #ff7f27;
+ font-size: 2.5rem;
+ margin-bottom: 1.5rem;
+ font-weight: bold;
+ background: linear-gradient(to right, #ff7f27, #ff5500);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ animation: glowPulse 3s infinite;
+ text-align: center;
+ letter-spacing: -0.5px;
+ }
+ `;
+
+ // Проверка на малое количество постеров
+ const hasLimitedPosters = posters.length <= 3;
+
+return (
+
+
+
Афиша
+
+
+
+
+ {posters.map((poster, index) => {
+ const dimFactor = calculateDimFactor(index);
+ return (
+
handlePosterClick(poster)}
+ onMouseEnter={() => handlePosterHover(index)}
+ style={{
+ animationDelay: `${index * 0.1}s`,
+ zIndex: index === activeIndex ? 10 : posters.length - Math.abs(index - activeIndex),
+ filter: `brightness(${dimFactor}) saturate(${dimFactor})`,
+ opacity: hasLimitedPosters ? 1 : 0.4 + dimFactor * 0.6,
+ }}
+ >
+
{
+ (e.target as HTMLImageElement).src = '/default-poster.jpg';
+ }}
+ />
+
+
+
{poster.title}
+
{poster.description}
+
+
{formatPrice(poster.price)}
+
{formatDate(poster.date)}
+
+
+
+ );
+ })}
+
+
+
+ {/* Индикаторы для навигации */}
+ {posters.length > 1 && (
+
+ {posters.map((_, index) => (
+
setActiveIndex(index)}
+ />
+ ))}
+
+ )}
+
+ {/* Индикатор загрузки для модального окна */}
+ {loadingPosterDetails && (
+
+ )}
+
+
+ {!loading && !error && posters.length === 0 && (
+
+
Пока нет доступных мероприятий
+
+ )}
+
+);
+};
+
+// Removed unused selectedPoster state
+
+export default PosterSlider;
+
+
+// Removed unused setSelectedPoster function
diff --git a/frontend/src/components/modal/Login.tsx b/frontend/src/components/modal/Login.tsx
new file mode 100644
index 0000000..b6ea2e6
--- /dev/null
+++ b/frontend/src/components/modal/Login.tsx
@@ -0,0 +1,585 @@
+// frontend/src/components/modal/Login.tsx
+'use client';
+import { useState, CSSProperties, useCallback, useEffect } from 'react';
+
+// Улучшенные стили с градиентами и эффектами
+const wrapper: CSSProperties = {
+ position: 'fixed',
+ inset: 0,
+ background: 'rgba(0,0,0,.7)',
+ backdropFilter: 'blur(12px)',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 1000,
+ transition: 'all 0.3s ease',
+};
+
+const modal: CSSProperties = {
+ position: 'relative',
+ background: 'linear-gradient(145deg, #0a0a0a, #1a1a1a)',
+ border: '1px solid #ff7f27',
+ borderRadius: 24,
+ padding: 40,
+ minWidth: 400,
+ maxWidth: '90vw',
+ color: '#ff7f27',
+ boxShadow: '0 8px 40px rgba(255,127,39,.25), 0 0 0 1px rgba(255,127,39,0.1)',
+ transformOrigin: 'center',
+ transition: 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
+ animation: 'loginCardAppear 0.4s forwards',
+};
+
+const closeBtn: CSSProperties = {
+ position: 'absolute',
+ top: 16,
+ right: 16,
+ background: 'rgba(0,0,0,0.4)',
+ border: '1px solid rgba(255,127,39,0.3)',
+ fontSize: 20,
+ color: '#ff7f27',
+ cursor: 'pointer',
+ width: 36,
+ height: 36,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: '50%',
+ transition: 'all 0.2s',
+};
+
+const input: CSSProperties = {
+ width: '100%',
+ padding: '12px 16px',
+ margin: '8px 0 20px',
+ border: '1px solid rgba(255,127,39,0.6)',
+ borderRadius: 12,
+ background: 'rgba(0,0,0,0.2)',
+ color: '#fff',
+ fontSize: 15,
+ outline: 'none',
+ transition: 'all 0.2s',
+ boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)',
+};
+
+const label: CSSProperties = {
+ fontSize: 14,
+ fontWeight: 'bold',
+ color: 'rgba(255,255,255,0.9)',
+ letterSpacing: '0.5px',
+};
+
+// Стили для капчи
+const captchaContainer: CSSProperties = {
+ marginBottom: 20,
+ border: '1px solid rgba(255,127,39,0.3)',
+ borderRadius: 12,
+ padding: 15,
+ background: 'rgba(255,127,39,0.05)',
+};
+
+const captchaCanvas: CSSProperties = {
+ width: '100%',
+ height: 80,
+ marginBottom: 10,
+ borderRadius: 8,
+ background: 'rgba(0,0,0,0.3)',
+ border: '1px solid rgba(255,127,39,0.3)',
+};
+
+const styles = `
+@keyframes loginCardAppear {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes pulseGlow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 20px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+input:focus {
+ border-color: #ff7f27;
+ box-shadow: 0 0 0 2px rgba(255,127,39,0.2), inset 0 1px 3px rgba(0,0,0,0.2);
+}
+
+button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+`;
+
+interface Props {
+ onSuccess: (u: { login: string; avatar: string }) => void;
+ onClose: () => void;
+ onRegisterClick?: () => void; // Делаем необязательным
+}
+
+/* Исправленные вспомогательные функции */
+const readCookie = (name: string) => {
+ const value = document.cookie
+ .split('; ')
+ .find((c) => c.startsWith(`${name}=`))
+ ?.split('=')[1];
+
+ if (!value) return null;
+
+ // Декодируем URL-закодированное значение
+ try {
+ return decodeURIComponent(value);
+ } catch {
+ return value; // Если не URL-кодированное значение, вернуть как есть
+ }
+};
+
+const sanitizeAvatar = (v: string) => v.replace(/"/g, '').replace(/\\\\/g, '/');
+
+export default function LoginModal({ onSuccess, onClose, onRegisterClick }: Props) {
+ const [login, setLogin] = useState('');
+ const [password, setPassword] = useState('');
+ const [error, setError] = useState
(null);
+ const [loading, setLoading] = useState(false);
+
+ // Состояния для капчи
+ const [captchaText, setCaptchaText] = useState('');
+ const [userCaptcha, setUserCaptcha] = useState('');
+ const [captchaValidated, setCaptchaValidated] = useState(false);
+
+ // Генерация капчи при загрузке компонента
+ useEffect(() => {
+ generateCaptcha();
+ }, []);
+
+ // Функция генерации капчи
+ const generateCaptcha = () => {
+ const canvas = document.getElementById('captchaCanvasLogin') as HTMLCanvasElement;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Установка фона
+ ctx.fillStyle = 'rgba(0,0,0,0.7)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Генерация случайного текста
+ const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
+ let captchaString = '';
+
+ for (let i = 0; i < 6; i++) {
+ captchaString += chars.charAt(Math.floor(Math.random() * chars.length));
+ }
+
+ setCaptchaText(captchaString);
+
+ // Отрисовка текста
+ ctx.font = 'bold 28px Arial';
+ ctx.fillStyle = '#ff7f27';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+
+ // Добавляем искажения для защиты
+ for (let i = 0; i < captchaString.length; i++) {
+ const x = (canvas.width / (captchaString.length + 1)) * (i + 1);
+ const y = canvas.height / 2 + Math.random() * 10 - 5;
+ const angle = Math.random() * 0.4 - 0.2;
+
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(angle);
+ ctx.fillText(captchaString[i], 0, 0);
+ ctx.restore();
+ }
+
+ // Добавляем шум
+ for (let i = 0; i < 100; i++) {
+ ctx.fillStyle = `rgba(255,127,39,${Math.random() * 0.3})`;
+ ctx.fillRect(Math.random() * canvas.width, Math.random() * canvas.height, 2, 2);
+ }
+
+ // Добавляем линии
+ for (let i = 0; i < 4; i++) {
+ ctx.beginPath();
+ ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
+ ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
+ ctx.strokeStyle = `rgba(255,127,39,${Math.random() * 0.5})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+
+ // Сбрасываем ввод пользователя
+ setUserCaptcha('');
+ setCaptchaValidated(false);
+ };
+
+ // Валидация капчи
+ const validateCaptcha = () => {
+ if (userCaptcha.toLowerCase() === captchaText.toLowerCase()) {
+ setCaptchaValidated(true);
+ return true;
+ } else {
+ setCaptchaValidated(false);
+ setError('Неверный код с картинки. Попробуйте еще раз.');
+ generateCaptcha();
+ return false;
+ }
+ };
+
+ const handleLogin = useCallback(async () => {
+ if (!login || !password || loading) return;
+
+ // Проверяем капчу
+ if (!captchaValidated && !validateCaptcha()) {
+ return;
+ }
+
+ setLoading(true);
+ setError(null);
+
+ try {
+ const res = await fetch('http://localhost:8000/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ login, password }),
+ credentials: 'include', // Важно для сохранения и отправки куки
+ });
+
+ const data = await res.json();
+ if (!res.ok) throw new Error(data.detail || 'Ошибка авторизации');
+
+ console.log("Ответ от сервера:", data);
+
+ // Проверка, сохранился ли JWT в куки
+ setTimeout(() => {
+ const jwtCookie = document.cookie
+ .split('; ')
+ .find(c => c.startsWith('JWT_token='))
+ ?.split('=')[1];
+
+ console.log("JWT токен в куки:", jwtCookie ? `${jwtCookie.substring(0, 10)}...` : "отсутствует");
+
+ // Если JWT нет в куках, но он есть в ответе, сохраним его вручную
+ if (!jwtCookie && data.token) {
+ console.log("Сохраняем JWT вручную");
+ document.cookie = `JWT_token=${data.token}; path=/; max-age=${60*60*24*7}`;
+ }
+
+ const userLogin = readCookie('login') || data.login;
+ const userAvatar = readCookie('avatar') || '/default-avatar.png';
+
+ if (userLogin) {
+ onSuccess({
+ login: userLogin,
+ avatar: userAvatar ? sanitizeAvatar(userAvatar) : '/default-avatar.png'
+ });
+ onClose();
+ } else {
+ throw new Error('Не удалось получить данные пользователя');
+ }
+ }, 300);
+
+ } catch (e: unknown) {
+ if (e instanceof Error) {
+ setError(e.message);
+ } else {
+ setError('Произошла неизвестная ошибка');
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [login, password, loading, captchaValidated, onSuccess, onClose, validateCaptcha]);
+
+ // Обработчик нажатия Enter
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ handleLogin();
+ }
+ };
+
+ // Обработчик клика на фоне
+ const handleWrapperClick = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget) {
+ onClose();
+ }
+ };
+
+ return (
+
+
+
+
{
+ e.currentTarget.style.background = 'rgba(255,127,39,0.2)';
+ e.currentTarget.style.transform = 'scale(1.1)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(0,0,0,0.4)';
+ e.currentTarget.style.transform = 'scale(1)';
+ }}
+ >
+ ✕
+
+
+
+ Авторизация
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
Логин
+
setLogin(e.target.value)}
+ onKeyDown={handleKeyDown}
+ />
+
+
Пароль
+
setPassword(e.target.value)}
+ onKeyDown={handleKeyDown}
+ />
+
+ {/* Капча вместо чекбокса "Я человек" */}
+
+
+
+
+
+ setUserCaptcha(e.target.value)}
+ />
+
+ ↻
+
+
+ Проверить
+
+
+
+ {captchaValidated && (
+
+ ✓
+ Проверка пройдена
+
+ )}
+
+
+
+
+
+
+
+
+
{
+ onClose();
+ // Добавляем проверку, чтобы избежать ошибки
+ if (typeof onRegisterClick === 'function') {
+ onRegisterClick();
+ }
+ }}
+ />
+
+
+ );
+}
+
+type BtnProps = { label: string; filled?: boolean; onClick?: () => void; disabled?: boolean };
+
+function HoverButton({ label, filled = false, onClick, disabled = false }: BtnProps) {
+ const [state, set] = useState<'idle' | 'hover' | 'active'>('idle');
+
+ const base: CSSProperties = {
+ width: '100%',
+ padding: 14,
+ borderRadius: 12,
+ fontWeight: 'bold',
+ fontSize: 16,
+ cursor: disabled ? 'default' : 'pointer',
+ transition: 'all .3s',
+ marginTop: 8,
+ };
+
+ const filledBg = state === 'active'
+ ? '#cc5e00'
+ : state === 'hover'
+ ? 'linear-gradient(145deg, #ff7f27, #e96c00)'
+ : 'linear-gradient(145deg, #ff7f27, #ff5500)';
+
+ const style: CSSProperties = filled
+ ? {
+ ...base,
+ background: filledBg,
+ color: '#000',
+ border: 0,
+ boxShadow: state === 'hover' ? '0 4px 15px rgba(255,127,39,0.4)' : '0 2px 10px rgba(255,127,39,0.3)',
+ transform: state === 'hover' && !disabled ? 'translateY(-2px)' : 'translateY(0)',
+ }
+ : {
+ ...base,
+ background:
+ state === 'active'
+ ? 'rgba(255,127,39,.2)'
+ : state === 'hover'
+ ? 'rgba(255,127,39,.1)'
+ : 'transparent',
+ border: '1px solid #ff7f27',
+ color: '#fff',
+ transform: state === 'hover' && !disabled ? 'translateY(-1px)' : 'translateY(0)',
+ };
+
+ return (
+ !disabled && set('hover')}
+ onMouseLeave={() => !disabled && set('idle')}
+ onMouseDown={() => !disabled && set('active')}
+ onMouseUp={() => !disabled && set('hover')}
+ onClick={disabled ? undefined : onClick}
+ >
+ {label}
+
+ );
+}
diff --git a/frontend/src/components/modal/Poster.tsx b/frontend/src/components/modal/Poster.tsx
new file mode 100644
index 0000000..05735fc
--- /dev/null
+++ b/frontend/src/components/modal/Poster.tsx
@@ -0,0 +1,442 @@
+'use client';
+import { useState, useCallback, useEffect } from 'react';
+import { format } from 'date-fns';
+import { ru } from 'date-fns/locale';
+
+type PosterModalProps = {
+ data: {
+ id: number;
+ img: string;
+ title: string;
+ likes: number;
+ desc: string;
+ price: number;
+ date: string;
+ };
+ isOpen: boolean;
+ onClose: () => void;
+};
+
+export default function PosterModal({ data, isOpen, onClose }: PosterModalProps) {
+ const [liked, setLiked] = useState(false);
+ const [likesCount, setLikesCount] = useState(data.likes);
+ const [loading, setLoading] = useState(false);
+ const [message, setMessage] = useState<{text: string, type: 'success' | 'error' | 'info' | null}>({
+ text: '',
+ type: null
+ });
+
+ // Блокировка прокрутки страницы при открытом модальном окне
+ useEffect(() => {
+ if (isOpen) {
+ document.body.style.overflow = 'hidden';
+ } else {
+ document.body.style.overflow = '';
+ }
+ return () => {
+ document.body.style.overflow = '';
+ };
+ }, [isOpen]);
+
+ // Обработка клавиши Escape
+ useEffect(() => {
+ const handleEscKey = (e: KeyboardEvent) => {
+ if (e.key === 'Escape' && isOpen) {
+ onClose();
+ }
+ };
+ window.addEventListener('keydown', handleEscKey);
+ return () => {
+ window.removeEventListener('keydown', handleEscKey);
+ };
+ }, [isOpen, onClose]);
+
+ // Функция для форматирования даты
+ const formatDate = (dateStr: string) => {
+ const date = new Date(dateStr);
+ return format(date, "d MMMM yyyy 'г. в' HH:mm", { locale: ru });
+ };
+
+ // Функция обработки лайков
+ const handleLike = useCallback(async () => {
+ if (loading) return;
+
+ try {
+ setLoading(true);
+
+ // Получаем JWT токен из cookie
+ const token = document.cookie
+ .split('; ')
+ .find(row => row.startsWith('JWT_token='))
+ ?.split('=')[1];
+
+ if (!token) {
+ setMessage({
+ text: 'Необходимо авторизоваться для оценки мероприятия',
+ type: 'error'
+ });
+ setTimeout(() => setMessage({text: '', type: null}), 3000);
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append('poster_id', String(data.id));
+ formData.append('like', liked ? '-1' : '1');
+
+ const response = await fetch('http://localhost:8000/setlike', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ },
+ body: formData,
+ credentials: 'include'
+ });
+
+ const responseData = await response.json();
+
+ if (response.ok) {
+ setLikesCount(responseData.poster.like);
+ setMessage({
+ text: responseData.message,
+ type: 'success'
+ });
+
+ if (responseData.message !== "Вы уже оценили этот постер." &&
+ responseData.message !== "Вы еще не ставили лайк этому постеру.") {
+ setLiked(!liked);
+ }
+ } else {
+ setMessage({
+ text: responseData.detail || 'Произошла ошибка при оценке мероприятия',
+ type: 'error'
+ });
+ }
+
+ setTimeout(() => setMessage({text: '', type: null}), 3000);
+ } catch (error) {
+ console.error('Ошибка при отправке лайка:', error);
+ setMessage({
+ text: 'Не удалось обработать запрос. Попробуйте позже.',
+ type: 'error'
+ });
+ setTimeout(() => setMessage({text: '', type: null}), 3000);
+ } finally {
+ setLoading(false);
+ }
+ }, [data.id, liked, loading]);
+
+ if (!isOpen) return null;
+
+ // Расширенные стили с усиленными эффектами размытия и поддержкой кросс-браузерности
+ const styles = `
+ @keyframes fadeIn {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ @keyframes heartbeat {
+ 0% { transform: scale(1); }
+ 15% { transform: scale(1.25); }
+ 25% { transform: scale(1.15); }
+ 35% { transform: scale(1.25); }
+ 100% { transform: scale(1); }
+ }
+
+ @keyframes spin {
+ to { transform: rotate(360deg); }
+ }
+
+ .loading-spinner {
+ animation: spin 1s linear infinite;
+ }
+
+ /* Улучшенная поддержка эффекта размытия для разных браузеров */
+ @supports (-webkit-backdrop-filter: none) or (backdrop-filter: none) {
+ .modal-backdrop {
+ -webkit-backdrop-filter: blur(25px) saturate(120%);
+ backdrop-filter: blur(25px) saturate(120%);
+ background-color: rgba(0,0,0,.65);
+ }
+ }
+
+ @supports not ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) {
+ .modal-backdrop {
+ background-color: rgba(0,0,0,.9);
+ }
+ }
+ `;
+
+ const modalContainerStyle = {
+ position: 'fixed' as const,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 2001, // Повышенный z-index
+ pointerEvents: 'none' as const,
+ };
+
+ return (
+ <>
+
+
+
+
e.stopPropagation()}
+ style={{
+ display: 'flex',
+ background: 'rgba(15,15,15,0.35)',
+ border: '1px solid rgba(255,127,39,0.5)',
+ borderRadius: 24,
+ overflow: 'hidden',
+ boxShadow: '0 15px 35px rgba(0,0,0,0.2)',
+ maxWidth: '90vw',
+ maxHeight: '85vh',
+ width: 'auto',
+ height: 'auto',
+ position: 'relative',
+ animation: 'fadeIn 0.3s forwards',
+ pointerEvents: 'auto',
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ {data.title}
+
+
+
+
+
+
+
+
+
+
+ {likesCount}
+
+
+ {loading && (
+
+
+
+
+
+
+ )}
+
+ {message.text && (
+
+ {message.text}
+
+ )}
+
+
+
+
+
+
+
+
+
+ {formatDate(data.date)}
+
+
+
+
+ {data.desc}
+
+
+
+
+ {data.price.toLocaleString('ru-RU', { style: 'currency', currency: 'RUB' })}
+
+
+
alert('Функция покупки билетов будет доступна в ближайшее время!')}
+ onMouseOver={(e) => {
+ e.currentTarget.style.transform = 'translateY(-2px)';
+ e.currentTarget.style.boxShadow = '0 4px 15px rgba(255,127,39,0.5)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.transform = 'translateY(0)';
+ e.currentTarget.style.boxShadow = '0 2px 10px rgba(255,127,39,0.4)';
+ }}
+ >
+ Купить билет
+
+
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/modal/Profile.tsx b/frontend/src/components/modal/Profile.tsx
new file mode 100644
index 0000000..3de8fde
--- /dev/null
+++ b/frontend/src/components/modal/Profile.tsx
@@ -0,0 +1,507 @@
+'use client';
+import { CSSProperties, useState, useEffect } from 'react';
+
+// Улучшенные стили с градиентами и эффектами
+const wrapper: CSSProperties = {
+ position: 'fixed',
+ inset: 0,
+ background: 'rgba(0,0,0,.7)',
+ backdropFilter: 'blur(12px)',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 1000,
+ transition: 'all 0.3s ease',
+ padding: '20px',
+};
+
+const modal: CSSProperties = {
+ position: 'relative',
+ background: 'linear-gradient(145deg, #0a0a0a, #1a1a1a)',
+ border: '1px solid #ff7f27',
+ borderRadius: 24,
+ padding: 40,
+ minWidth: 700, // Увеличено для более широкого профиля
+ maxWidth: '90vw',
+ width: '100%', // Добавлено для лучшего контроля ширины
+ color: '#ff7f27',
+ boxShadow: '0 8px 40px rgba(255,127,39,.25), 0 0 0 1px rgba(255,127,39,0.1)',
+ transformOrigin: 'center',
+ transition: 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
+ animation: 'profileCardAppear 0.4s forwards',
+};
+
+const profileHeader: CSSProperties = {
+ position: 'relative',
+ marginBottom: 30,
+ display: 'flex',
+ alignItems: 'center',
+ gap: 30,
+};
+
+const avatarContainer: CSSProperties = {
+ position: 'relative',
+ width: 140,
+ height: 140,
+ borderRadius: '50%',
+ background: 'linear-gradient(145deg, #ff7f27, #f9560b)',
+ boxShadow: '0 5px 15px rgba(255, 127, 39, 0.3)',
+ animation: 'pulseGlow 3s infinite',
+ overflow: 'hidden',
+};
+
+const avatarImage: CSSProperties = {
+ width: '100%',
+ height: '100%',
+ objectFit: 'cover',
+ borderRadius: '50%',
+};
+
+const profileInfo: CSSProperties = {
+ flex: 1,
+};
+
+const username: CSSProperties = {
+ fontSize: 28,
+ marginBottom: 12,
+ fontWeight: 'bold',
+ textShadow: '0 2px 10px rgba(255,127,39,0.3)',
+ background: 'linear-gradient(to right, #ff7f27, #ff5500)',
+ WebkitBackgroundClip: 'text',
+ WebkitTextFillColor: 'transparent',
+};
+
+const fullname: CSSProperties = {
+ fontSize: 18,
+ marginBottom: 12,
+ color: 'rgba(255,255,255,0.9)',
+};
+
+const closeBtn: CSSProperties = {
+ position: 'absolute',
+ top: 16,
+ right: 16,
+ background: 'rgba(0,0,0,0.4)',
+ border: '1px solid rgba(255,127,39,0.3)',
+ fontSize: 20,
+ color: '#ff7f27',
+ cursor: 'pointer',
+ width: 36,
+ height: 36,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: '50%',
+ transition: 'all 0.2s',
+};
+
+const divider: CSSProperties = {
+ width: '100%',
+ height: 2,
+ background: 'linear-gradient(to right, transparent, rgba(255,127,39,0.5), transparent)',
+ margin: '20px 0',
+ borderRadius: 2,
+};
+
+const tabsContainer: CSSProperties = {
+ display: 'flex',
+ borderBottom: '1px solid rgba(255,127,39,0.3)',
+ marginBottom: 20,
+};
+
+const tab: CSSProperties = {
+ padding: '12px 20px',
+ color: 'rgba(255,255,255,0.6)',
+ cursor: 'pointer',
+ transition: 'all 0.3s',
+ borderBottom: '2px solid transparent',
+ fontSize: 16,
+ fontWeight: 'bold',
+};
+
+const tabActive: CSSProperties = {
+ color: '#ff7f27',
+ borderBottom: '2px solid #ff7f27',
+ background: 'rgba(255,127,39,0.1)',
+};
+
+const tabContent: CSSProperties = {
+ minHeight: 200,
+ padding: '20px 0',
+};
+
+const settingsButton: CSSProperties = {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: 8,
+ width: '100%',
+ padding: '12px 0',
+ borderRadius: 12,
+ background: 'linear-gradient(145deg, #ff7f27, #ff5500)',
+ color: '#000',
+ fontWeight: 'bold',
+ fontSize: 16,
+ cursor: 'pointer',
+ border: 0,
+ boxShadow: '0 4px 12px rgba(255,127,39,0.4)',
+ transition: 'all 0.2s',
+ marginTop: 10,
+};
+
+const logoutButton: CSSProperties = {
+ display: 'block',
+ margin: '12px auto 0',
+ padding: '8px 12px',
+ background: 'transparent',
+ border: '1px solid rgba(255,127,39,0.5)',
+ color: '#ff7f27',
+ borderRadius: 8,
+ fontSize: 14,
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+};
+
+const emptyState: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: '40px 0',
+ color: 'rgba(255,255,255,0.5)',
+ textAlign: 'center',
+};
+
+const ticketCard: CSSProperties = {
+ background: 'linear-gradient(145deg, rgba(40,40,40,0.7), rgba(20,20,20,0.9))',
+ borderRadius: 16,
+ padding: 24,
+ marginBottom: 16,
+ border: '1px solid rgba(255,127,39,0.3)',
+ boxShadow: '0 5px 15px rgba(0,0,0,0.3)',
+ position: 'relative',
+ overflow: 'hidden',
+ animation: 'fadeIn 0.5s forwards',
+};
+
+const ticketHeader: CSSProperties = {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ marginBottom: 12,
+};
+
+const ticketTitle: CSSProperties = {
+ fontSize: 22,
+ fontWeight: 'bold',
+ color: '#fff',
+ marginBottom: 8,
+};
+
+const ticketNumber: CSSProperties = {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: '#ff7f27',
+ padding: '6px 12px',
+ background: 'rgba(255,127,39,0.15)',
+ borderRadius: 10,
+ letterSpacing: 1,
+};
+
+const ticketDetails: CSSProperties = {
+ display: 'flex',
+ justifyContent: 'space-between',
+ padding: '12px 0',
+ borderTop: '1px dashed rgba(255,255,255,0.2)',
+ borderBottom: '1px dashed rgba(255,255,255,0.2)',
+ margin: '15px 0',
+};
+
+const ticketDetail: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+};
+
+const ticketLabel: CSSProperties = {
+ fontSize: 12,
+ color: 'rgba(255,255,255,0.5)',
+ marginBottom: 5,
+};
+
+const ticketValue: CSSProperties = {
+ fontSize: 14,
+ color: 'rgba(255,255,255,0.9)',
+};
+
+
+const ticketPaymentId: CSSProperties = {
+ fontSize: 12,
+ color: 'rgba(255,255,255,0.5)',
+ marginTop: 10,
+ wordBreak: 'break-all',
+};
+
+
+
+// Добавим интерфейс для более полных данных пользователя
+interface UserData {
+ login: string;
+ avatar: string;
+ first_name: string;
+ last_name: string;
+ middle_name: string | null;
+ email: string;
+ full_name?: string; // Для обратной совместимости
+}
+
+export default function ProfileModal({
+ user,
+ onClose,
+ onSettings,
+}: {
+ user: { avatar: string; login: string; full_name: string };
+ onClose: () => void;
+ onSettings: () => void;
+}) {
+ const [activeTab, setActiveTab] = useState('tickets');
+ const [userData, setUserData] = useState(null);
+ // Removed unused loading state
+
+ // Получение ID пользователя из cookie
+ const userId = document.cookie
+ .split('; ')
+ .find(row => row.startsWith('id='))
+ ?.split('=')[1];
+
+ // Получение токена из cookie (removed unused token variable)
+
+ // Загрузка полных данных пользователя при открытии профиля
+ useEffect(() => {
+ if (userId) {
+ fetchUserData(userId);
+ } else {
+ setLoading(false);
+ }
+ }, [userId]);
+
+ // Функция получения данных пользователя
+ const fetchUserData = async (id: string) => {
+ try {
+ const response = await fetch(`http://localhost:8000/user/${id}`, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'include',
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ console.log("Получены данные пользователя:", data);
+ setUserData(data);
+ } else {
+ console.error("Ошибка при получении данных пользователя");
+ }
+ } catch (error) {
+ console.error("Ошибка запроса:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // Функция форматирования полного имени
+ const formatName = () => {
+ if (userData) {
+ const parts = [];
+ if (userData.last_name) parts.push(userData.last_name);
+ if (userData.first_name) parts.push(userData.first_name);
+ if (userData.middle_name) parts.push(userData.middle_name);
+ return parts.join(' ');
+ }
+
+ // Fallback к старому формату, если нет новых данных
+ return user.full_name || '';
+ };
+
+ return (
+ e.target === e.currentTarget && onClose()}
+ >
+
+
+
{
+ e.currentTarget.style.background = 'rgba(255,127,39,0.2)';
+ e.currentTarget.style.transform = 'scale(1.1)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(0,0,0,0.4)';
+ e.currentTarget.style.transform = 'scale(1)';
+ }}
+ >
+ ✕
+
+
+
+
+
e.preventDefault()}
+ onDragStart={(e) => e.preventDefault()}
+ />
+
+
+
+
+ {userData?.login || user.login}
+
+
+ {formatName() && (
+
+ {formatName()}
+
+ )}
+
+ {/* Добавляем отображение email */}
+ {userData?.email && (
+
+
+
+
+
+ {userData.email}
+
+ )}
+
+
+
+
+
+ {/* Вкладки */}
+
+
setActiveTab('tickets')}
+ >
+ Мои билеты
+
+
setActiveTab('history')}
+ >
+ История заказов
+
+
+
+ {/* Содержимое вкладок */}
+
+ {activeTab === 'tickets' && (
+
+
+
+
+
+
+ ДАТА
+ 21.05.2025
+
+
+
+ ВРЕМЯ
+ 19:30
+
+
+
+ ЗАЛ
+ Главный
+
+
+
+ МЕСТО
+ VIP 12
+
+
+
+
+ Код платежа:
+ 2fbd2cd4-000f-5001-9000-1fb8d8bcd8a9
+
+
+
+
+
+ )}
+
+ {activeTab === 'history' && (
+
+
+
+
+
История заказов пуста
+
Здесь появится история ваших прошлых заказов
+
+ )}
+
+
+
{
+ e.currentTarget.style.transform = 'translateY(-2px)';
+ e.currentTarget.style.boxShadow = '0 6px 15px rgba(255,127,39,0.5)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.transform = 'translateY(0)';
+ e.currentTarget.style.boxShadow = '0 4px 12px rgba(255,127,39,0.4)';
+ }}
+ >
+
+
+
+
+ Настройки профиля
+
+
+
{
+ e.currentTarget.style.background = 'rgba(255,127,39,0.1)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'transparent';
+ }}
+ >
+ Выйти
+
+
+
+ );
+}
+function setLoading(isLoading: boolean) {
+ console.log(`Loading state set to: ${isLoading}`);
+}
+
diff --git a/frontend/src/components/modal/Reg.tsx b/frontend/src/components/modal/Reg.tsx
new file mode 100644
index 0000000..cad0a0d
--- /dev/null
+++ b/frontend/src/components/modal/Reg.tsx
@@ -0,0 +1,964 @@
+'use client';
+
+import { useState, useCallback, useEffect } from 'react';
+import type { CSSProperties } from 'react';
+
+/* Вспомогательные функции */
+const readCookie = (name: string) => {
+ const value = document.cookie
+ .split('; ')
+ .find((c) => c.startsWith(`${name}=`))
+ ?.split('=')[1];
+
+ if (!value) return null;
+
+ // Декодируем URL-закодированное значение
+ try {
+ return decodeURIComponent(value);
+ } catch {
+ return value; // Если не URL-кодированное значение, вернуть как есть
+ }
+};
+
+// Removed unused sanitizeAvatar function
+
+// Улучшенные стили с градиентами и эффектами
+const wrapper: CSSProperties = {
+ position: 'fixed',
+ inset: 0,
+ background: 'rgba(0,0,0,.7)',
+ backdropFilter: 'blur(12px)',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 1000,
+ transition: 'all 0.3s ease',
+};
+
+const modal: CSSProperties = {
+ position: 'relative',
+ background: 'linear-gradient(145deg, #0a0a0a, #1a1a1a)',
+ border: '1px solid #ff7f27',
+ borderRadius: 24,
+ padding: 40,
+ minWidth: 500,
+ maxWidth: '90vw',
+ maxHeight: '90vh',
+ overflowY: 'auto',
+ color: '#ff7f27',
+ boxShadow: '0 8px 40px rgba(255,127,39,.25), 0 0 0 1px rgba(255,127,39,0.1)',
+ transformOrigin: 'center',
+ transition: 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
+ animation: 'regCardAppear 0.4s forwards',
+};
+
+const closeBtn: CSSProperties = {
+ position: 'absolute',
+ top: 16,
+ right: 16,
+ background: 'rgba(0,0,0,0.4)',
+ border: '1px solid rgba(255,127,39,0.3)',
+ fontSize: 20,
+ color: '#ff7f27',
+ cursor: 'pointer',
+ width: 36,
+ height: 36,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: '50%',
+ transition: 'all 0.2s',
+};
+
+const input: CSSProperties = {
+ width: '100%',
+ padding: '12px 16px',
+ margin: '8px 0 20px',
+ border: '1px solid rgba(255,127,39,0.6)',
+ borderRadius: 12,
+ background: 'rgba(0,0,0,0.2)',
+ color: '#fff',
+ fontSize: 15,
+ outline: 'none',
+ transition: 'all 0.2s',
+ boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)',
+};
+
+const label: CSSProperties = {
+ fontSize: 14,
+ fontWeight: 'bold',
+ color: 'rgba(255,255,255,0.9)',
+ letterSpacing: '0.5px',
+};
+
+const offerBtn: CSSProperties = {
+ background: 'none',
+ border: 'none',
+ color: '#ff7f27',
+ textDecoration: 'underline',
+ cursor: 'pointer',
+ padding: 0,
+ fontSize: 14,
+ fontWeight: 'bold',
+ marginLeft: 5,
+};
+
+const offerModal: CSSProperties = {
+ position: 'fixed',
+ inset: 0,
+ background: 'rgba(0,0,0,.9)',
+ backdropFilter: 'blur(12px)',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ zIndex: 1100,
+ transition: 'all 0.3s ease',
+};
+
+const offerContent: CSSProperties = {
+ background: 'linear-gradient(145deg, #0a0a0a, #1a1a1a)',
+ border: '1px solid #ff7f27',
+ borderRadius: 24,
+ padding: 30,
+ width: '80%',
+ maxWidth: 700,
+ maxHeight: '80vh',
+ overflowY: 'auto',
+ color: '#fff',
+ boxShadow: '0 8px 40px rgba(255,127,39,.25)',
+ animation: 'fadeIn 0.3s forwards',
+};
+
+// Стили для капчи
+const captchaContainer: CSSProperties = {
+ marginBottom: 20,
+ width: '45%',
+ border: '1px solid rgba(255,127,39,0.3)',
+ borderRadius: 12,
+ padding: 15,
+ background: 'rgba(255,127,39,0.05)',
+ marginLeft: 'auto', // Добавлено для центрирования
+ marginRight: 'auto', // Добавлено для центрирования
+};
+
+const captchaCanvas: CSSProperties = {
+ width: '100%',
+ height: 80,
+ marginBottom: 10,
+ borderRadius: 8,
+ background: 'rgba(0,0,0,0.3)',
+ border: '1px solid rgba(255,127,39,0.3)',
+};
+
+const styles = `
+@keyframes regCardAppear {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes pulseGlow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 20px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+/* Стилизация скроллбара */
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.3);
+ border-radius: 10px;
+ margin: 5px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: linear-gradient(145deg, #ff7f27, #e96c00);
+ border-radius: 10px;
+ border: 2px solid rgba(0, 0, 0, 0.3);
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(145deg, #ff9540, #ff7f27);
+}
+
+::-webkit-scrollbar-corner {
+ background: transparent;
+}
+
+input:focus {
+ border-color: #ff7f27;
+ box-shadow: 0 0 0 2px rgba(255,127,39,0.2), inset 0 1px 3px rgba(0,0,0,0.2);
+}
+
+button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+`;
+
+interface Props {
+ onSuccess: (u: { login: string; avatar: string }) => void;
+ onClose: () => void;
+ onLoginClick: () => void;
+}
+
+export default function RegisterModal({ onSuccess, onClose, onLoginClick }: Props) {
+ const [login, setLogin] = useState('');
+ const [password, setPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [email, setEmail] = useState('');
+ const [lastName, setLastName] = useState('');
+ const [firstName, setFirstName] = useState('');
+ const [middleName, setMiddleName] = useState('');
+
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [showOffer, setShowOffer] = useState(false);
+ const [offerAccepted, setOfferAccepted] = useState(false);
+ const [offerRead, setOfferRead] = useState(false);
+
+ // Капча
+ const [captchaText, setCaptchaText] = useState('');
+ const [userCaptcha, setUserCaptcha] = useState('');
+ const [captchaValidated, setCaptchaValidated] = useState(false);
+
+ // Генерация капчи при загрузке компонента
+ useEffect(() => {
+ generateCaptcha();
+ }, []);
+
+ // Функция генерации капчи
+ const generateCaptcha = () => {
+ const canvas = document.getElementById('captchaCanvas') as HTMLCanvasElement;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Установка фона
+ ctx.fillStyle = 'rgba(0,0,0,0.7)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Генерация случайного текста
+ const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
+ let captchaString = '';
+
+ for (let i = 0; i < 6; i++) {
+ captchaString += chars.charAt(Math.floor(Math.random() * chars.length));
+ }
+
+ setCaptchaText(captchaString);
+
+ // Отрисовка текста
+ ctx.font = 'bold 28px Arial';
+ ctx.fillStyle = '#ff7f27';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+
+ // Добавляем искажения для защиты
+ for (let i = 0; i < captchaString.length; i++) {
+ const x = (canvas.width / (captchaString.length + 1)) * (i + 1);
+ const y = canvas.height / 2 + Math.random() * 10 - 5;
+ const angle = Math.random() * 0.4 - 0.2;
+
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(angle);
+ ctx.fillText(captchaString[i], 0, 0);
+ ctx.restore();
+ }
+
+ // Добавляем шум
+ for (let i = 0; i < 100; i++) {
+ ctx.fillStyle = `rgba(255,127,39,${Math.random() * 0.3})`;
+ ctx.fillRect(Math.random() * canvas.width, Math.random() * canvas.height, 2, 2);
+ }
+
+ // Добавляем линии
+ for (let i = 0; i < 4; i++) {
+ ctx.beginPath();
+ ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
+ ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
+ ctx.strokeStyle = `rgba(255,127,39,${Math.random() * 0.5})`;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+ }
+
+ // Сбрасываем ввод пользователя
+ setUserCaptcha('');
+ setCaptchaValidated(false);
+ };
+
+ // Валидация капчи
+ const validateCaptcha = () => {
+ if (userCaptcha.toLowerCase() === captchaText.toLowerCase()) {
+ setCaptchaValidated(true);
+ return true;
+ } else {
+ setCaptchaValidated(false);
+ setError('Неверный код с картинки. Попробуйте еще раз.');
+ generateCaptcha();
+ return false;
+ }
+ };
+
+ // Проверка валидности формы
+ const isFormValid = () => {
+ return (
+ login &&
+ password &&
+ confirmPassword === password &&
+ email &&
+ lastName &&
+ firstName &&
+ offerAccepted &&
+ captchaValidated
+ );
+ };
+
+ // Обработчик публичной оферты
+ const handleOfferScroll = (e: React.UIEvent) => {
+ const { scrollHeight, scrollTop, clientHeight } = e.currentTarget;
+ // Если прочитали до конца (или почти до конца)
+ if (scrollHeight - scrollTop - clientHeight < 50) {
+ setOfferRead(true);
+ }
+ };
+
+ // Обработчик регистрации
+ const handleRegister = useCallback(async () => {
+ if (!isFormValid()) {
+ // Проверка индивидуальных ошибок
+ if (password !== confirmPassword) {
+ setError('Пароли не совпадают');
+ return;
+ }
+ if (!offerAccepted) {
+ setError('Необходимо принять условия публичной оферты');
+ return;
+ }
+ if (!captchaValidated && !validateCaptcha()) {
+ // validateCaptcha уже установит сообщение об ошибке
+ return;
+ }
+ return;
+ }
+
+ setLoading(true);
+ setError(null);
+
+ try {
+ const res = await fetch('http://localhost:8000/register', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ login,
+ password,
+ email,
+ last_name: lastName,
+ first_name: firstName,
+ middle_name: middleName || null
+ }),
+ credentials: 'include',
+ });
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new Error(data.detail || 'Ошибка при регистрации');
+ }
+
+ // После успешной регистрации, делаем автоматический вход
+ try {
+ const loginRes = await fetch('http://localhost:8000/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ login, password }),
+ credentials: 'include',
+ });
+
+ if (loginRes.ok) {
+ const loginData = await loginRes.json();
+ console.log("Ответ после авто-логина:", loginData);
+
+ // Проверка, сохранился ли JWT в куки
+ setTimeout(() => {
+ const jwtCookie = document.cookie
+ .split('; ')
+ .find(c => c.startsWith('JWT_token='))
+ ?.split('=')[1];
+
+ console.log("JWT токен в куки после регистрации:",
+ jwtCookie ? `${jwtCookie.substring(0, 10)}...` : "отсутствует");
+
+ // Если JWT нет в куках, но он есть в ответе, сохраним его вручную
+ if (!jwtCookie && loginData.token) {
+ console.log("Сохраняем JWT вручную после регистрации");
+ document.cookie = `JWT_token=${loginData.token}; path=/; max-age=${60*60*24*7}`;
+ }
+
+ const userLogin = readCookie('login') || login;
+ const userAvatar = readCookie('avatar') || '/default-avatar.png';
+
+ onSuccess({
+ login: userLogin,
+ avatar: userAvatar
+ });
+ onClose();
+ }, 300);
+ } else {
+ // Если логин не удался, просто показываем сообщение об успешной регистрации
+ alert('Регистрация успешна! Теперь вы можете войти в систему.');
+ onClose();
+ }
+ } catch (e) {
+ console.error('Ошибка автоматического входа:', e);
+ alert('Регистрация успешна! Теперь вы можете войти в систему.');
+ onClose();
+ }
+ } catch (e: unknown) {
+ if (e instanceof Error) {
+ setError(e.message);
+ } else {
+ setError('Произошла неизвестная ошибка');
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [login, password, confirmPassword, email, lastName, firstName,
+ middleName, offerAccepted, captchaValidated, onSuccess, onClose]);
+
+ // Обработчик нажатия Enter
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && isFormValid()) {
+ e.preventDefault();
+ handleRegister();
+ }
+ };
+
+ // Обработчик клика на фоне
+ const handleWrapperClick = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget) {
+ onClose();
+ }
+ };
+
+ return (
+
+
+
+
{
+ e.currentTarget.style.background = 'rgba(255,127,39,0.2)';
+ e.currentTarget.style.transform = 'scale(1.1)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(0,0,0,0.4)';
+ e.currentTarget.style.transform = 'scale(1)';
+ }}
+ >
+ ✕
+
+
+
+ Регистрация
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+
+
+
+
Отчество (необязательно)
+
setMiddleName(e.target.value)}
+ onKeyDown={handleKeyDown}
+ />
+
+ {/* Капча */}
+
+
+
+
+
+
+ setUserCaptcha(e.target.value)}
+ />
+
+ ↻
+
+
+ Проверить
+
+
+
+ {captchaValidated && (
+
+ ✓
+ Проверка пройдена
+
+ )}
+
+
+ {/* Публичная оферта */}
+
+ setOfferAccepted(e.target.checked)}
+ disabled={!offerRead}
+ style={{ marginRight: 10 }}
+ />
+
+ Я принимаю условия
+ setShowOffer(true)}
+ >
+ публичной оферты
+
+
+
+
+ {!offerRead && (
+
+ * Для принятия оферты необходимо ознакомиться с её содержанием
+
+ )}
+
+
+
+
+ Уже есть аккаунт?
+
+
+
{
+ onClose();
+ onLoginClick();
+ }}
+ />
+
+ {/* Модальное окно с публичной офертой */}
+ {showOffer && (
+ setShowOffer(false)}>
+
e.stopPropagation()}
+ >
+
+ Публичная оферта
+
+
+
+
1. ОБЩИЕ ПОЛОЖЕНИЯ
+
1.1. Настоящая публичная оферта (далее — Оферта) представляет собой официальное предложение , Билетопад далее именуемого «Исполнитель», адресованное любому дееспособному физическому лицу, далее именуемому «Пользователь».
+
1.2. В соответствии с пунктом 2 статьи 437 Гражданского Кодекса Российской Федерации данный документ является публичной офертой.
+
1.3. Акцепт Оферты осуществляется путем регистрации на сайте Исполнителя. При регистрации Пользователь обязан ознакомиться с условиями настоящей Оферты.
+
+
2. ПРЕДМЕТ ОФЕРТЫ
+
2.1. Предметом Оферты является предоставление Исполнителем Пользователю доступа к использованию сервиса Билетопад (далее – Сервис).
+
2.2. Исполнитель предоставляет доступ к Сервису на условиях, предусмотренных настоящей Офертой.
+
+
3. ПРАВА И ОБЯЗАННОСТИ СТОРОН
+
3.1. Пользователь обязуется:
+
3.1.1. Предоставить при регистрации достоверную информацию.
+
3.1.2. Не передавать свои учетные данные третьим лицам.
+
3.1.3. Не использовать Сервис для распространения информации, которая является незаконной, вредоносной, оскорбительной.
+
3.1.4. Соблюдать авторские и смежные права Исполнителя и третьих лиц.
+
+
4. ОТВЕТСТВЕННОСТЬ СТОРОН
+
4.1. За неисполнение или ненадлежащее исполнение обязательств по настоящей Оферте Стороны несут ответственность в соответствии с законодательством Российской Федерации.
+
4.2. Исполнитель не несет ответственности за сбои в работе Сервиса, вызванные техническими причинами.
+
+
5. СРОК ДЕЙСТВИЯ ОФЕРТЫ
+
5.1. Оферта вступает в силу с момента размещения на сайте Исполнителя и действует до момента ее отзыва Исполнителем.
+
5.2. Исполнитель оставляет за собой право внести изменения в условия Оферты или отозвать Оферту в любой момент по своему усмотрению.
+
+
6. ДОПОЛНИТЕЛЬНЫЕ УСЛОВИЯ
+
6.1. Все споры и разногласия, которые могут возникнуть между Сторонами по вопросам, не нашедшим своего разрешения в тексте данной Оферты, будут разрешаться путем переговоров.
+
6.2. Во всем остальном, что не предусмотрено настоящей Офертой, Стороны руководствуются действующим законодательством Российской Федерации.
+
+
7. ЗАКЛЮЧИТЕЛЬНЫЕ ПОЛОЖЕНИЯ
+
7.1. Регистрируясь на сайте, Пользователь подтверждает, что ознакомлен с условиями настоящей Оферты и полностью с ними согласен.
+
+
Дата последнего обновления: 18 мая 2025 г.
+
+ {/* Дополнительный контент для обеспечения возможности прокрутки */}
+
+
+
+
+ {!offerRead ? (
+
+ * Пожалуйста, прочитайте публичную оферту до конца
+
+ ) : (
+
+ ✓
+ Вы ознакомились с офертой
+
+ )}
+
+
+ {offerRead && (
+ {
+ setOfferAccepted(true);
+ setShowOffer(false);
+ }}
+ onMouseOver={(e) => {
+ e.currentTarget.style.boxShadow = '0 4px 15px rgba(255,127,39,0.5)';
+ e.currentTarget.style.transform = 'translateY(-1px)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.boxShadow = '0 2px 10px rgba(255,127,39,0.3)';
+ e.currentTarget.style.transform = 'translateY(0)';
+ }}
+ >
+ Принять
+
+ )}
+
+ setShowOffer(false)}
+ onMouseOver={(e) => {
+ if (offerRead) {
+ e.currentTarget.style.background = 'rgba(0,0,0,0.5)';
+ }
+ }}
+ onMouseOut={(e) => {
+ if (offerRead) {
+ e.currentTarget.style.background = 'rgba(0,0,0,0.4)';
+ }
+ }}
+ >
+ {offerRead ? 'Отмена' : 'Закрыть'}
+
+
+
+
+
+ )}
+
+
+ );
+}
+
+type BtnProps = { label: string; filled?: boolean; onClick?: () => void; disabled?: boolean };
+
+function HoverButton({ label, filled = false, onClick, disabled = false }: BtnProps) {
+ const [state, set] = useState<'idle' | 'hover' | 'active'>('idle');
+
+ const base: CSSProperties = {
+ width: '100%',
+ padding: 14,
+ borderRadius: 12,
+ fontWeight: 'bold',
+ fontSize: 16,
+ cursor: disabled ? 'default' : 'pointer',
+ transition: 'all .3s',
+ marginTop: 8,
+ };
+
+ const filledBg = state === 'active'
+ ? '#cc5e00'
+ : state === 'hover'
+ ? 'linear-gradient(145deg, #ff7f27, #e96c00)'
+ : 'linear-gradient(145deg, #ff7f27, #ff5500)';
+
+ const style: CSSProperties = filled
+ ? {
+ ...base,
+ background: filledBg,
+ color: '#000',
+ border: 0,
+ boxShadow: state === 'hover' ? '0 4px 15px rgba(255,127,39,0.4)' : '0 2px 10px rgba(255,127,39,0.3)',
+ transform: state === 'hover' && !disabled ? 'translateY(-2px)' : 'translateY(0)',
+ }
+ : {
+ ...base,
+ background:
+ state === 'active'
+ ? 'rgba(255,127,39,.2)'
+ : state === 'hover'
+ ? 'rgba(255,127,39,.1)'
+ : 'transparent',
+ border: '1px solid #ff7f27',
+ color: '#fff',
+ transform: state === 'hover' && !disabled ? 'translateY(-1px)' : 'translateY(0)',
+ };
+
+ return (
+ !disabled && set('hover')}
+ onMouseLeave={() => !disabled && set('idle')}
+ onMouseDown={() => !disabled && set('active')}
+ onMouseUp={() => !disabled && set('hover')}
+ onClick={disabled ? undefined : onClick}
+ >
+ {label}
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/modal/Settings.tsx b/frontend/src/components/modal/Settings.tsx
new file mode 100644
index 0000000..06776b0
--- /dev/null
+++ b/frontend/src/components/modal/Settings.tsx
@@ -0,0 +1,781 @@
+'use client';
+import { useState, useEffect, CSSProperties, ChangeEvent } from 'react';
+
+/* Стили с анимациями и эффектами */
+const wrapper: CSSProperties = {
+ position: 'fixed',
+ inset: 0,
+ background: 'rgba(0,0,0,.7)',
+ backdropFilter: 'blur(12px)',
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding: 20,
+ zIndex: 1000,
+ transition: 'all 0.3s ease',
+};
+
+const modal: CSSProperties = {
+ position: 'relative',
+ background: 'linear-gradient(145deg, #0a0a0a, #1a1a1a)',
+ border: '2px solid #ff7f27',
+ borderRadius: 24,
+ padding: 36,
+ width: '100%',
+ maxWidth: 760,
+ color: '#ff7f27',
+ boxShadow: '0 8px 40px rgba(255,127,39,.25), 0 0 0 1px rgba(255,127,39,0.1)',
+ transformOrigin: 'center',
+ transition: 'transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
+ animation: 'settingsCardAppear 0.4s forwards',
+};
+
+const closeBtn: CSSProperties = {
+ position: 'absolute',
+ top: 16,
+ right: 16,
+ background: 'rgba(0,0,0,0.4)',
+ border: '1px solid rgba(255,127,39,0.3)',
+ fontSize: 20,
+ color: '#ff7f27',
+ cursor: 'pointer',
+ width: 36,
+ height: 36,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: '50%',
+ transition: 'all 0.2s',
+};
+
+const lblStyle: CSSProperties = {
+ fontSize: 14,
+ marginTop: 10,
+ marginBottom: 6,
+ fontWeight: 'bold',
+ color: 'rgba(255,255,255,0.9)',
+ letterSpacing: '0.5px',
+};
+
+const input: CSSProperties = {
+ width: '100%',
+ padding: '12px 16px',
+ border: '1px solid rgba(255,127,39,0.6)',
+ borderRadius: 12,
+ background: 'rgba(0,0,0,0.2)',
+ color: '#fff',
+ fontSize: 14,
+ outline: 'none',
+ transition: 'all 0.2s',
+ boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)',
+};
+
+const sectionTitle: CSSProperties = {
+ fontSize: 17,
+ marginTop: 24,
+ marginBottom: 12,
+ fontWeight: 'bold',
+ borderBottom: '1px solid rgba(255,127,39,.3)',
+ paddingBottom: 6,
+ background: 'linear-gradient(to right, #ff7f27, #ff5500)',
+ WebkitBackgroundClip: 'text',
+ WebkitTextFillColor: 'transparent',
+ textShadow: '0 2px 10px rgba(255,127,39,0.2)',
+};
+
+// Сообщение об успехе
+const successMsg: CSSProperties = {
+ color: '#4caf50',
+ fontSize: 12,
+ marginTop: 6,
+ opacity: 1,
+ transition: 'opacity 0.3s, transform 0.3s',
+ animation: 'fadeIn 0.3s forwards',
+ background: 'rgba(76,175,80,0.1)',
+ padding: '4px 8px',
+ borderRadius: 4,
+ boxShadow: '0 0 10px rgba(76,175,80,0.2)',
+};
+
+// Разделитель
+const divider: CSSProperties = {
+ height: 2,
+ background: 'linear-gradient(to right, transparent, rgba(255,127,39,0.5), transparent)',
+ margin: '24px 0',
+ borderRadius: 2,
+};
+
+// Исправленный стиль контейнера аватарки
+const avatarContainer: CSSProperties = {
+ position: 'relative',
+ width: 120,
+ height: 120,
+ margin: '0 auto',
+ borderRadius: '50%',
+ background: 'linear-gradient(145deg, #ff7f27, #f9560b)',
+ boxShadow: '0 5px 15px rgba(255, 127, 39, 0.3)',
+ animation: 'pulseGlow 3s infinite',
+ cursor: 'pointer',
+ overflow: 'hidden', // Важно: предотвращает выход изображения за пределы круга
+};
+
+const styles = `
+@keyframes settingsCardAppear {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes pulseGlow {
+ 0% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+ 50% {
+ box-shadow: 0 0 20px rgba(255,127,39,0.5);
+ }
+ 100% {
+ box-shadow: 0 0 5px rgba(255,127,39,0.3);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(5px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+input:focus {
+ border-color: #ff7f27;
+ box-shadow: 0 0 0 2px rgba(255,127,39,0.2), inset 0 1px 3px rgba(0,0,0,0.2);
+}
+`;
+
+
+type User = { login: string; full_name: string; avatar: string; email: string };
+
+export default function SettingsModal({
+ user,
+ onClose,
+ onUpdate,
+}: {
+ user: User;
+ onClose: () => void;
+ onUpdate: (p: Partial) => void;
+}) {
+ // Состояние для полей формы
+ const [avatar, setAvatar] = useState(user.avatar);
+ const [login, setLogin] = useState(user.login);
+ const [email, setEmail] = useState(user.email || '');
+ const [surname, setSurname] = useState('');
+ const [name, setName] = useState('');
+ const [patronymic, setPatr] = useState('');
+
+ // Пароль
+ const [oldPass, setOldPass] = useState('');
+ const [newPass, setNewPass] = useState('');
+
+ // Состояние для API данных
+ const [loadingData, setLoadingData] = useState(true);
+
+ // Состояния для UI
+ const [busy, setBusy] = useState>({});
+ const [success, setSuccess] = useState>({});
+
+ // Авторизация
+ const token = document.cookie.match(/JWT_token=([^;]+)/)?.[1] || '';
+ const auth = { Authorization: `Bearer ${token}` };
+
+ // Получение ID пользователя из cookie
+ const userId = document.cookie
+ .split('; ')
+ .find(row => row.startsWith('id='))
+ ?.split('=')[1];
+
+ // Загрузка данных пользователя при инициализации формы
+ useEffect(() => {
+ if (userId) {
+ fetchUserData(userId);
+ } else {
+ setLoadingData(false);
+ }
+ }, [userId]);
+
+ // Функция загрузки данных пользователя
+ const fetchUserData = async (id: string) => {
+ try {
+ const response = await fetch(`http://localhost:8000/user/${id}`, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'include',
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ console.log("Получены данные пользователя:", data);
+ // setUserData(data); // Removed as setUserData is not defined
+
+ // Заполняем форму данными из API
+ setLogin(data.login);
+ setEmail(data.email);
+ setSurname(data.last_name || '');
+ setName(data.first_name || '');
+ setPatr(data.middle_name || '');
+ if (data.avatar) setAvatar(data.avatar);
+ } else {
+ console.error("Ошибка при получении данных пользователя");
+ }
+ } catch (error) {
+ console.error("Ошибка запроса:", error);
+ } finally {
+ setLoadingData(false);
+ }
+ };
+
+ // Эффект для отображения сообщения об успехе
+ useEffect(() => {
+ const timers: NodeJS.Timeout[] = [];
+
+ Object.keys(success).forEach(key => {
+ if (success[key]) {
+ const timer = setTimeout(() => {
+ setSuccess(prev => ({ ...prev, [key]: false }));
+ }, 3000);
+ timers.push(timer);
+ }
+ });
+
+ return () => timers.forEach(timer => clearTimeout(timer));
+ }, [success]);
+
+ // Функция для отправки запросов на сервер
+ const sendRequest = async (
+ url: string,
+ body: Record,
+ key: string,
+ updateData?: Partial
+ ) => {
+ setBusy(prev => ({ ...prev, [key]: true }));
+
+ try {
+ const response = await fetch(`http://localhost:8000/${url}`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ ...auth
+ },
+ body: JSON.stringify(body),
+ credentials: 'include',
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ console.log("Успешный ответ от сервера:", data);
+
+ if (updateData) {
+ onUpdate(updateData);
+ }
+
+ setSuccess(prev => ({ ...prev, [key]: true }));
+
+ window.dispatchEvent(
+ new CustomEvent('user-update', {
+ detail: updateData || {}
+ })
+ );
+
+ if (key === 'firstName' || key === 'lastName' || key === 'patronymic') {
+ const fullName = `${surname} ${name} ${patronymic}`.trim();
+ document.cookie = `full_name=${encodeURIComponent(fullName)}; path=/`;
+ }
+
+ setTimeout(() => {
+ console.log("Текущие куки:", document.cookie);
+ }, 100);
+
+ return true;
+ } else {
+ const errorText = await response.text();
+ console.error(`Ошибка запроса ${url}:`, errorText);
+ return false;
+ }
+ } catch (error) {
+ console.error(`Ошибка запроса ${url}:`, error);
+ return false;
+ } finally {
+ setBusy(prev => ({ ...prev, [key]: false }));
+ }
+ };
+
+ // Функции для обновления отдельных полей
+ const updateLogin = () => sendRequest(
+ 'change_username',
+ { new_login: login },
+ 'login',
+ { login }
+ );
+
+ const updateEmail = () => sendRequest(
+ 'change_email',
+ { new_email: email },
+ 'email',
+ { email }
+ );
+
+ const updateLastName = () => sendRequest(
+ 'change_last_name',
+ { new_last_name: surname },
+ 'lastName',
+ { full_name: `${surname} ${name} ${patronymic}`.trim() }
+ );
+
+ const updateFirstName = () => sendRequest(
+ 'change_name',
+ { new_first_name: name },
+ 'firstName',
+ { full_name: `${surname} ${name} ${patronymic}`.trim() }
+ );
+
+ const updateMiddleName = () => sendRequest(
+ 'change_middle_name',
+ { new_middle_name: patronymic },
+ 'patronymic',
+ { full_name: `${surname} ${name} ${patronymic}`.trim() }
+ );
+
+ const updatePassword = () => {
+ if (!oldPass || !newPass) return;
+
+ sendRequest(
+ 'change_password',
+ { old_password: oldPass, new_password: newPass },
+ 'password'
+ ).then(success => {
+ if (success) {
+ setOldPass('');
+ setNewPass('');
+ }
+ });
+ };
+
+ // Обновление аватарки
+ const handleAvatarChange = async (e: ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (!file) return;
+
+ setBusy(prev => ({ ...prev, avatar: true }));
+ const formData = new FormData();
+ formData.append('file', file);
+
+ try {
+ const response = await fetch('http://localhost:8000/upload_avatar', {
+ method: 'POST',
+ headers: auth,
+ body: formData,
+ credentials: 'include',
+ });
+
+ if (response.ok) {
+ const { file_url } = await response.json();
+ setAvatar(file_url);
+ onUpdate({ avatar: file_url });
+ setSuccess(prev => ({ ...prev, avatar: true }));
+
+ window.dispatchEvent(
+ new CustomEvent('user-update', {
+ detail: { avatar: file_url }
+ })
+ );
+ }
+ } catch (error) {
+ console.error('Ошибка при обновлении аватара:', error);
+ } finally {
+ setBusy(prev => ({ ...prev, avatar: false }));
+ }
+ };
+
+ // Обработчик закрытия модального окна
+ const handleClose = (e?: React.MouseEvent) => {
+ if (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ onClose();
+ };
+
+ // Обработка клика вне модального окна
+ const handleWrapperClick = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget) {
+ handleClose();
+ }
+ };
+
+ return (
+
+
+
+ {/* Заголовок и кнопка закрытия */}
+
{
+ e.currentTarget.style.background = 'rgba(255,127,39,0.2)';
+ e.currentTarget.style.transform = 'scale(1.1)';
+ }}
+ onMouseOut={(e) => {
+ e.currentTarget.style.background = 'rgba(0,0,0,0.4)';
+ e.currentTarget.style.transform = 'scale(1)';
+ }}
+ >
+ ✕
+
+
+
+ Настройки профиля
+
+
+ {/* Разделяем на две колонки для оптимизации пространства */}
+
+ {/* Левая колонка - Аватарка и логин/email */}
+
+ {/* Аватарка */}
+
+
document.getElementById('avatar-upload')?.click()}
+ >
+
{ e.currentTarget.style.transform = 'scale(1.05)' }}
+ onMouseOut={e => { e.currentTarget.style.transform = 'scale(1)' }}
+ />
+ {busy.avatar && (
+
+ Загрузка...
+
+ )}
+
+
+ {success.avatar && (
+
+ Аватар успешно обновлен
+
+ )}
+
+ Нажмите на изображение, чтобы изменить аватар
+
+
+
+ {/* Учетные данные */}
+
Учетные данные
+
+
+
Логин
+
+ setLogin(e.target.value)}
+ style={{ ...input, flex: 1 }}
+ />
+
+
+ {success.login && (
+
Логин успешно изменен
+ )}
+
+
+
+
Почта
+
+ setEmail(e.target.value)}
+ style={{ ...input, flex: 1 }}
+ />
+
+
+ {success.email && (
+
Email успешно изменен
+ )}
+
+
+
+ {/* Правая колонка - Персональные данные и смена пароля */}
+
+ {/* Персональные данные */}
+
Персональные данные
+
+
+
Фамилия
+
+ setSurname(e.target.value)}
+ style={{ ...input, flex: 1 }}
+ />
+
+
+ {success.lastName && (
+
Фамилия успешно изменена
+ )}
+
+
+
+
Имя
+
+ setName(e.target.value)}
+ style={{ ...input, flex: 1 }}
+ />
+
+
+ {success.firstName && (
+
Имя успешно изменено
+ )}
+
+
+
+
Отчество
+
+ setPatr(e.target.value)}
+ style={{ ...input, flex: 1 }}
+ />
+
+
+ {success.patronymic && (
+
Отчество успешно изменено
+ )}
+
+
+
+
+ {/* Смена пароля */}
+
Смена пароля
+
+
+ Текущий пароль
+ setOldPass(e.target.value)}
+ style={input}
+ />
+
+
+
+ Новый пароль
+ setNewPass(e.target.value)}
+ style={input}
+ />
+
+
+
+
+
+ {success.password && (
+
+ Пароль успешно изменен
+
+ )}
+
+
+
+ {/* Показываем loader при загрузке данных */}
+ {loadingData && (
+
+
+ Загрузка данных...
+
+
+ )}
+
+
+ );
+}
+
+/* Вспомогательные компоненты */
+function HoverButton({
+ label,
+ filled = false,
+ onClick,
+ disabled = false,
+ small = false,
+}: {
+ label: string;
+ filled?: boolean;
+ onClick?: () => void;
+ disabled?: boolean;
+ small?: boolean;
+}) {
+ const [state, setState] = useState<'idle' | 'hover' | 'active'>('idle');
+
+ const base: CSSProperties = {
+ padding: small ? '8px 12px' : '10px 18px',
+ borderRadius: 12,
+ fontWeight: 'bold',
+ fontSize: small ? 12 : 14,
+ cursor: disabled ? 'default' : 'pointer',
+ transition: 'all .3s',
+ border: '1px solid #ff7f27',
+ whiteSpace: 'nowrap',
+ boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
+ };
+
+ const filledBg =
+ state === 'active' ? '#cc5e00' : state === 'hover' ? '#e96c00' : 'linear-gradient(145deg, #ff7f27, #ff5500)';
+
+ const style: CSSProperties = filled
+ ? {
+ ...base,
+ background: filledBg,
+ color: '#000',
+ border: 0,
+ boxShadow: state === 'hover' ? '0 4px 15px rgba(255,127,39,0.4)' : '0 2px 10px rgba(255,127,39,0.3)',
+ transform: state === 'hover' ? 'translateY(-2px)' : 'translateY(0)',
+ }
+ : {
+ ...base,
+ background:
+ state === 'active'
+ ? 'rgba(255,127,39,.2)'
+ : state === 'hover'
+ ? 'rgba(255,127,39,.1)'
+ : 'transparent',
+ color: '#fff',
+ transform: state === 'hover' ? 'translateY(-1px)' : 'translateY(0)',
+ };
+
+ return (
+ setState('hover')}
+ onMouseLeave={() => setState('idle')}
+ onMouseDown={() => setState('active')}
+ onMouseUp={() => setState('hover')}
+ onClick={disabled ? undefined : onClick}
+ >
+ {label}
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..0c8f652
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
+