diff --git a/back/main.py b/back/main.py index 332a0a6..801a0d7 100644 --- a/back/main.py +++ b/back/main.py @@ -1,219 +1,440 @@ -#подключение библиотек -from fastapi import FastAPI, Response, Path, Depends, Body, Form, Query, status, HTTPException, APIRouter, UploadFile, File -from fastapi.responses import HTMLResponse, FileResponse, JSONResponse, RedirectResponse -from fastapi.staticfiles import StaticFiles -from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer -from fastapi.templating import Jinja2Templates -from fastapi.requests import Request - -from pydantic import json - -from starlette.staticfiles import StaticFiles - -import requests -from uuid import uuid4 - -import ast -import pathlib -import shutil -import os - -from .utils import * -from .models import Announcement, Trashbox, UserDatabase, Base -from .db import engine, SessionLocal - -from . import schema - -Base.metadata.create_all(bind=engine) - -db = SessionLocal() - - -app = FastAPI() - -templates = Jinja2Templates(directory="./front/dist") - -app.mount("/static", StaticFiles(directory = "./front/dist")) -# app.mount("/uploads", StaticFiles(directory = "./uploads")) - - -@app.get("/api/announcements")#адрес объявлений -def annoncements_list(user_id: int = None, metro: str = None, category: str = None, booked_by: int = -1): - # Считываем данные из Body и отображаем их на странице. - # В последствии будем вставлять данные в html-форму - - a = db.query(Announcement) - b = db.query(Announcement) - c = db.query(Announcement) - d = db.query(Announcement) - e = db.query(Announcement) - - if user_id != None: - b = a.filter(Announcement.user_id == user_id) - - if metro != None: - c = a.filter(Announcement.metro == metro) - - if category != None: - d = a.filter(Announcement.category == category) - - if booked_by != -1: - e = a.filter(Announcement.booked_by == booked_by) - - if not any([category, user_id, metro]) and booked_by == -1: - result = a.all() - - else: - result = b.intersect(c, d, e).all() - - return {"Success" : True, "list_of_announcements": result} - - -@app.get("/api/announcement")#адрес объявлений -def single_annoncement(user_id:int): -# Считываем данные из Body и отображаем их на странице. -# В последствии будем вставлять данные в html-форму - try: - annoncement = db.get(Announcement, user_id) - return {"id": annoncement.id, "user_id": annoncement.user_id, "name": annoncement.name, - "category": annoncement.category, "best_by": annoncement.best_by, "address": annoncement.address, - "description": annoncement.description, "metro": annoncement.metro, "latitude": annoncement.latitude, - "longtitude":annoncement.longtitude, "trashId": annoncement.trashId, "src":annoncement.src, - "booked_by":annoncement.booked_by} - except: - return {"Answer" : False} #если неуданый доступ, то сообщаем об этом - - -# Занести объявление в базу -@app.put("/api/announcement")#адрес объявлений -def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form()], bestBy: Annotated[int, Form()], address: Annotated[str, Form()], longtitude: Annotated[float, Form()], latitude: Annotated[float, Form()], description: Annotated[str, Form()], src: UploadFile, metro: Annotated[str, Form()], trashId: Annotated[int, Form()] = None): - # try: - userId = 1 # temporary - - uploaded_name = "" - - f = src.file - f.seek(0, os.SEEK_END) - if f.tell() > 0: - f.seek(0) - destination = pathlib.Path("./uploads/" + str(hash(src.file)) + pathlib.Path(src.filename).suffix.lower()) - with destination.open('wb') as buffer: - shutil.copyfileobj(src.file, buffer) - - uploaded_name = "/uploads/"+destination.name - - temp_ancmt = Announcement(user_id=userId, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, src=uploaded_name, metro=metro, trashId=trashId) - db.add(temp_ancmt) # добавляем в бд - db.commit() # сохраняем изменения - db.refresh(temp_ancmt) # обновляем состояние объекта - return {"Answer" : True} - # except: - # return {"Answer" : False} - - -# Удалить объявления из базы -@app.delete("/api/announcement") #адрес объявления -def delete_from_db(data = Body()):#функция удаления объекта из БД - try: - db.delete(user_id=data.user_id)#удаление из БД - db.commit() # сохраняем изменения - return {"Answer" : True} - except: - return {"Answer" : False} - - -# Забронировать объявление -@app.post("/api/book") -def change_book_status(data: schema.Book): - try: - # Получаем id пользователя, который бронирует объявление - temp_user_id = 1 - # Находим объявление по данному id - announcement_to_change = db.query(Announcement).filter(id == data.id).first() - # Изменяем поле booked_status на полученный id - announcement_to_change.booked_status = temp_user_id - return {"Success": True} - except: - return {"Success": False} - - -@app.post("/api/signup") -def create_user(data = Body()): - if db.query(UserDatabase).filter(UserDatabase.email == data["email"]).first() == None: - new_user = UserDatabase(id=data["id"], email=data["email"], password=data["password"], name=data["name"], surname=data["surname"]) - db.add(new_user) - db.commit() - db.refresh(new_user) # обновляем состояние объекта - return {"Success": True} - return {"Success": False, "Message": "Пользователь с таким email уже зарегестрирован."} - - -@app.post("/api/token", response_model=Token) -async def login_for_access_token( - form_data: Annotated[OAuth2PasswordRequestForm, Depends()] -): - # разобраться с первым параметром - user = authenticate_user(db.query(UserDatabase).all(), form_data.username, form_data.password) - if not user: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Incorrect username or password", - headers={"WWW-Authenticate": "Bearer"}, - ) - access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token( - data={"user_id": user.id}, expires_delta=access_token_expires - ) - return {"access_token": access_token} - - -@app.get("/api/users/me/", response_model=User) -async def read_users_me( - current_user: Annotated[User, Depends(get_current_active_user)] -): - return current_user - - -@app.get("/api/users/me/items/") -async def read_own_items( - current_user: Annotated[User, Depends(get_current_active_user)] -): - return [{"Current user name": current_user.name, "Current user surname": current_user.surname}] - - - -@app.get("/api/trashbox") -def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api - BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин - my_token='eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3Nzg2NTk4MjEsImlhdCI6MTY4Mzk2NTQyMSwianRpIjoiOTI2ZGMyNmEtMGYyZi00OTZiLWI0NTUtMWQyYWM5YmRlMTZkIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjE2MGU1ZGVkLWFmMjMtNDkyNS05OTc1LTRhMzM0ZjVmNTkyOSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIxNjBlNWRlZC1hZjIzLTQ5MjUtOTk3NS00YTMzNGY1ZjU5MjkiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.BRyUIyY-KKnZ9xqTNa9vIsfKF0UN2VoA9h4NN4y7IgBVLiiS-j43QbeE6qgjIQo0pV3J8jtCAIPvJbO-Ex-GNkw_flgMiGHhKEpsHPW3WK-YZ-XsZJzVQ_pOmLte-Kql4z97WJvolqiXT0nMo2dlX2BGvNs6JNbupvcuGwL4YYpekYAaFNYMQrxi8bSN-R7FIqxP-gzZDAuQSWRRSUqVBLvmgRhphTM-FAx1sX833oXL9tR7ze3eDR_obSV0y6cKVIr4eIlKxFd82qiMrN6A6CTUFDeFjeAGERqeBPnJVXU36MHu7Ut7eOVav9OUARARWRkrZRkqzTfZ1iqEBq5Tsg' - head = {'Authorization': 'Bearer {}'.format(my_token)} - - my_data={ - 'x' : f"{lng}", - 'y' : f"{lat}", - 'limit' : '1' - } - - response = requests.post(f"{BASE_URL}/nearest_recycling/get", headers=head, data=my_data) - infos = response.json() - - trashboxes = [] - for trashbox in infos["results"]: - temp_dict = {} - for obj in trashbox["Objects"]: - coord_list = obj["geometry"] - temp_dict["Lat"] = coord_list["coordinates"][1] - temp_dict["Lng"] = coord_list["coordinates"][0] - - properties = obj["properties"] - temp_dict["Name"] = properties["title"] - temp_dict["Address"] = properties["address"] - temp_dict["Categories"] = properties["content_text"].split(',') - trashboxes.append(temp_dict) - - uniq_trashboxes = [ast.literal_eval(el1) for el1 in set([str(el2) for el2 in trashboxes])] - return JSONResponse(uniq_trashboxes) - -@app.get("/{rest_of_path:path}") -async def react_app(req: Request, rest_of_path: str): - return templates.TemplateResponse('index.html', { 'request': req }) +<<<<<<< HEAD +#подключение библиотек +from fastapi import FastAPI, Response, Path, Depends, Body, Form, Query, status, HTTPException, APIRouter, UploadFile, File +from fastapi.responses import HTMLResponse, FileResponse, JSONResponse, RedirectResponse +from fastapi.staticfiles import StaticFiles +from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer +from fastapi.templating import Jinja2Templates +from fastapi.requests import Request + +from pydantic import json + +from starlette.staticfiles import StaticFiles + +import requests +from uuid import uuid4 + +import ast +import pathlib +import shutil +import os + +from .utils import * +from .models import Announcement, Trashbox, UserDatabase, Base +from .db import engine, SessionLocal + +from . import schema + +Base.metadata.create_all(bind=engine) + +db = SessionLocal() + + +app = FastAPI() + +templates = Jinja2Templates(directory="./front/dist") + +app.mount("/static", StaticFiles(directory = "./front/dist")) +app.mount("/uploads", StaticFiles(directory = "./uploads")) + + +@app.get("/api/announcements")#адрес объявлений +def annoncements_list(user_id: int = None, metro: str = None, category: str = None, booked_by: int = -1): + # Считываем данные из Body и отображаем их на странице. + # В последствии будем вставлять данные в html-форму + + a = db.query(Announcement) + b = db.query(Announcement) + c = db.query(Announcement) + d = db.query(Announcement) + e = db.query(Announcement) + + if user_id != None: + b = a.filter(Announcement.user_id == user_id) + + if metro != None: + c = a.filter(Announcement.metro == metro) + + if category != None: + d = a.filter(Announcement.category == category) + + if booked_by != -1: + e = a.filter(Announcement.booked_by == booked_by) + + if not any([category, user_id, metro]) and booked_by == -1: + result = a.all() + + else: + result = b.intersect(c, d, e).all() + + return {"Success" : True, "list_of_announcements": result} + + +@app.get("/api/announcement")#адрес объявлений +def single_annoncement(user_id:int): +# Считываем данные из Body и отображаем их на странице. +# В последствии будем вставлять данные в html-форму + try: + annoncement = db.get(Announcement, user_id) + return {"id": annoncement.id, "user_id": annoncement.user_id, "name": annoncement.name, + "category": annoncement.category, "best_by": annoncement.best_by, "address": annoncement.address, + "description": annoncement.description, "metro": annoncement.metro, "latitude": annoncement.latitude, + "longtitude":annoncement.longtitude, "trashId": annoncement.trashId, "src":annoncement.src, + "booked_by":annoncement.booked_by} + except: + return {"Answer" : False} #если неуданый доступ, то сообщаем об этом + + +# Занести объявление в базу +@app.put("/api/announcement")#адрес объявлений +def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form()], bestBy: Annotated[int, Form()], address: Annotated[str, Form()], longtitude: Annotated[float, Form()], latitude: Annotated[float, Form()], description: Annotated[str, Form()], src: UploadFile, metro: Annotated[str, Form()], trashId: Annotated[int, Form()] = None): + # try: + userId = 1 # temporary + + uploaded_name = "" + + f = src.file + f.seek(0, os.SEEK_END) + if f.tell() > 0: + f.seek(0) + destination = pathlib.Path("./uploads/" + str(hash(src.file)) + pathlib.Path(src.filename).suffix.lower()) + with destination.open('wb') as buffer: + shutil.copyfileobj(src.file, buffer) + + uploaded_name = "/uploads/"+destination.name + + temp_ancmt = Announcement(user_id=userId, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, src=uploaded_name, metro=metro, trashId=trashId) + db.add(temp_ancmt) # добавляем в бд + db.commit() # сохраняем изменения + db.refresh(temp_ancmt) # обновляем состояние объекта + return {"Answer" : True} + # except: + # return {"Answer" : False} + + +# Удалить объявления из базы +@app.delete("/api/announcement") #адрес объявления +def delete_from_db(data = Body()):#функция удаления объекта из БД + try: + db.delete(user_id=data.user_id)#удаление из БД + db.commit() # сохраняем изменения + return {"Answer" : True} + except: + return {"Answer" : False} + + +# Забронировать объявление +@app.post("/api/book") +def change_book_status(data: schema.Book): + try: + # Получаем id пользователя, который бронирует объявление + temp_user_id = 1 + # Находим объявление по данному id + announcement_to_change = db.query(Announcement).filter(id == data.id).first() + # Изменяем поле booked_status на полученный id + announcement_to_change.booked_status = temp_user_id + return {"Success": True} + except: + return {"Success": False} + + +@app.post("/api/signup") +def create_user(data = Body()): + if db.query(UserDatabase).filter(UserDatabase.email == data["email"]).first() == None: + new_user = UserDatabase(id=data["id"], email=data["email"], password=data["password"], name=data["name"], surname=data["surname"]) + db.add(new_user) + db.commit() + db.refresh(new_user) # обновляем состояние объекта + return {"Success": True} + return {"Success": False, "Message": "Пользователь с таким email уже зарегестрирован."} + + +@app.post("/api/token", response_model=Token) +async def login_for_access_token( + form_data: Annotated[OAuth2PasswordRequestForm, Depends()] +): + # разобраться с первым параметром + user = authenticate_user(db.query(UserDatabase).all(), form_data.username, form_data.password) + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = create_access_token( + data={"user_id": user.id}, expires_delta=access_token_expires + ) + return {"access_token": access_token} + + +@app.get("/api/users/me/", response_model=User) +async def read_users_me( + current_user: Annotated[User, Depends(get_current_active_user)] +): + return current_user + + +@app.get("/api/users/me/items/") +async def read_own_items( + current_user: Annotated[User, Depends(get_current_active_user)] +): + return [{"Current user name": current_user.name, "Current user surname": current_user.surname}] + + + +@app.get("/api/trashbox") +def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api + BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин + my_token='eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3Nzg2NTk4MjEsImlhdCI6MTY4Mzk2NTQyMSwianRpIjoiOTI2ZGMyNmEtMGYyZi00OTZiLWI0NTUtMWQyYWM5YmRlMTZkIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjE2MGU1ZGVkLWFmMjMtNDkyNS05OTc1LTRhMzM0ZjVmNTkyOSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIxNjBlNWRlZC1hZjIzLTQ5MjUtOTk3NS00YTMzNGY1ZjU5MjkiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.BRyUIyY-KKnZ9xqTNa9vIsfKF0UN2VoA9h4NN4y7IgBVLiiS-j43QbeE6qgjIQo0pV3J8jtCAIPvJbO-Ex-GNkw_flgMiGHhKEpsHPW3WK-YZ-XsZJzVQ_pOmLte-Kql4z97WJvolqiXT0nMo2dlX2BGvNs6JNbupvcuGwL4YYpekYAaFNYMQrxi8bSN-R7FIqxP-gzZDAuQSWRRSUqVBLvmgRhphTM-FAx1sX833oXL9tR7ze3eDR_obSV0y6cKVIr4eIlKxFd82qiMrN6A6CTUFDeFjeAGERqeBPnJVXU36MHu7Ut7eOVav9OUARARWRkrZRkqzTfZ1iqEBq5Tsg' + head = {'Authorization': 'Bearer {}'.format(my_token)} + + my_data={ + 'x' : f"{lng}", + 'y' : f"{lat}", + 'limit' : '1' + } + + response = requests.post(f"{BASE_URL}/nearest_recycling/get", headers=head, data=my_data) + infos = response.json() + + trashboxes = [] + for trashbox in infos["results"]: + temp_dict = {} + for obj in trashbox["Objects"]: + coord_list = obj["geometry"] + temp_dict["Lat"] = coord_list["coordinates"][1] + temp_dict["Lng"] = coord_list["coordinates"][0] + + properties = obj["properties"] + temp_dict["Name"] = properties["title"] + temp_dict["Address"] = properties["address"] + temp_dict["Categories"] = properties["content_text"].split(',') + trashboxes.append(temp_dict) + + uniq_trashboxes = [ast.literal_eval(el1) for el1 in set([str(el2) for el2 in trashboxes])] + return JSONResponse(uniq_trashboxes) + +@app.get("/{rest_of_path:path}") +async def react_app(req: Request, rest_of_path: str): + return templates.TemplateResponse('index.html', { 'request': req }) +======= +#подключение библиотек +from fastapi import FastAPI, Response, Path, Depends, Body, Form, Query, status, HTTPException, APIRouter, UploadFile, File +from fastapi.responses import HTMLResponse, FileResponse, JSONResponse, RedirectResponse +from fastapi.staticfiles import StaticFiles +from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer +from fastapi.templating import Jinja2Templates +from fastapi.requests import Request + +from pydantic import json + +from starlette.staticfiles import StaticFiles + +import requests +from uuid import uuid4 + +import ast +import pathlib +import shutil +import os + +from .utils import * +from .models import Announcement, Trashbox, UserDatabase, Base +from .db import engine, SessionLocal + +from . import schema + + +Base.metadata.create_all(bind=engine) + +db = SessionLocal() + + +app = FastAPI() + +templates = Jinja2Templates(directory="./front/dist") + +app.mount("/static", StaticFiles(directory = "./front/dist")) +app.mount("/uploads", StaticFiles(directory = "./uploads")) + +@app.get("/api/announcements")#адрес объявлений +def annoncements_list(user_id: int = None, metro: str = None, category: str = None, booked_by: int = -1): + # Считываем данные из Body и отображаем их на странице. + # В последствии будем вставлять данные в html-форму + + a = db.query(Announcement) + b = db.query(Announcement) + c = db.query(Announcement) + d = db.query(Announcement) + e = db.query(Announcement) + + if user_id != None: + b = a.filter(Announcement.user_id == user_id) + + if metro != None: + c = a.filter(Announcement.metro == metro) + + if category != None: + d = a.filter(Announcement.category == category) + + if booked_by != -1: + e = a.filter(Announcement.booked_by == booked_by) + + if not any([category, user_id, metro]) and booked_by == -1: + result = a.all() + + else: + result = b.intersect(c, d, e).all() + + return {"Success" : True, "list_of_announcements": result} + + +@app.get("/api/announcement")#адрес объявлений +def single_annoncement(user_id:int): +# Считываем данные из Body и отображаем их на странице. +# В последствии будем вставлять данные в html-форму + try: + annoncement = db.get(Announcement, user_id) + return {"id": annoncement.id, "user_id": annoncement.user_id, "name": annoncement.name, + "category": annoncement.category, "best_by": annoncement.best_by, "address": annoncement.address, + "description": annoncement.description, "metro": annoncement.metro, "latitude": annoncement.latitude, + "longtitude":annoncement.longtitude, "trashId": annoncement.trashId, "src":annoncement.src, + "booked_by":annoncement.booked_by} + except: + return {"Answer" : False} #если неуданый доступ, то сообщаем об этом + + +# Занести объявление в базу +@app.put("/api/announcement")#адрес объявлений +def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form()], bestBy: Annotated[int, Form()], address: Annotated[str, Form()], longtitude: Annotated[float, Form()], latitude: Annotated[float, Form()], description: Annotated[str, Form()], src: Annotated[UploadFile | None, File()], metro: Annotated[str, Form()], trashId: Annotated[int | None, Form()] = -1): + # try: + userId = 1 # temporary + + uploaded_name = "" + + f = src.file + f.seek(0, os.SEEK_END) + if f.tell() > 0: + f.seek(0) + destination = pathlib.Path("./uploads/" + str(hash(src.file)) + pathlib.Path(src.filename).suffix.lower()) + with destination.open('wb') as buffer: + shutil.copyfileobj(src.file, buffer) + + uploaded_name = "/uploads/"+destination.name + + temp_ancmt = Announcement(user_id=userId, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, src=uploaded_name, metro=metro, trashId=trashId, booked_by=-1) + db.add(temp_ancmt) # добавляем в бд + db.commit() # сохраняем изменения + db.refresh(temp_ancmt) # обновляем состояние объекта + return {"Answer" : True} + # except: + # return {"Answer" : False} + + +# Удалить объявления из базы +@app.delete("/api/announcement") #адрес объявления +def delete_from_db(data = Body()):#функция удаления объекта из БД + try: + db.delete(user_id=data.user_id)#удаление из БД + db.commit() # сохраняем изменения + return {"Answer" : True} + except: + return {"Answer" : False} + + +# Забронировать объявление +@app.post("/api/book") +def change_book_status(data: schema.Book): + try: + # Получаем id пользователя, который бронирует объявление + temp_user_id = 1 + # Находим объявление по данному id + announcement_to_change = db.query(Announcement).filter(id == data.id).first() + # Изменяем поле booked_status на полученный id + announcement_to_change.booked_status = temp_user_id + return {"Success": True} + except: + return {"Success": False} + + +@app.post("/api/signup") +def create_user(data = Body()): + if db.query(UserDatabase).filter(User.email == data["email"]).first() == None: + new_user = UserDatabase(id=data["id"], email=data["email"], password=data["password"], name=data["name"], surname=data["surname"]) + db.add(new_user) + db.commit() + db.refresh(new_user) # обновляем состояние объекта + return {"Success": True} + return {"Success": False, "Message": "Пользователь с таким email уже зарегестрирован."} + + +@app.post("/api/token", response_model=Token) +async def login_for_access_token( + form_data: Annotated[OAuth2PasswordRequestForm, Depends()] +): + user = authenticate_user(db.query(UserDatabase).all(), form_data.username, form_data.password) + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = create_access_token( + data={"user_id": user.id}, expires_delta=access_token_expires + ) + return {"access_token": access_token, "token_type": "bearer"} + + +@app.get("/api/users/me/", response_model=User) +async def read_users_me( + current_user: Annotated[User, Depends(get_current_active_user)] +): + return current_user + + +@app.get("/api/users/me/items/") +async def read_own_items( + current_user: Annotated[User, Depends(get_current_active_user)] +): + return [{"Current user name": current_user.name, "Current user surname": current_user.surname}] + + + +@app.get("/api/trashbox") +def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api + BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин + my_token='eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3Nzg2NTk4MjEsImlhdCI6MTY4Mzk2NTQyMSwianRpIjoiOTI2ZGMyNmEtMGYyZi00OTZiLWI0NTUtMWQyYWM5YmRlMTZkIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjE2MGU1ZGVkLWFmMjMtNDkyNS05OTc1LTRhMzM0ZjVmNTkyOSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIxNjBlNWRlZC1hZjIzLTQ5MjUtOTk3NS00YTMzNGY1ZjU5MjkiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.BRyUIyY-KKnZ9xqTNa9vIsfKF0UN2VoA9h4NN4y7IgBVLiiS-j43QbeE6qgjIQo0pV3J8jtCAIPvJbO-Ex-GNkw_flgMiGHhKEpsHPW3WK-YZ-XsZJzVQ_pOmLte-Kql4z97WJvolqiXT0nMo2dlX2BGvNs6JNbupvcuGwL4YYpekYAaFNYMQrxi8bSN-R7FIqxP-gzZDAuQSWRRSUqVBLvmgRhphTM-FAx1sX833oXL9tR7ze3eDR_obSV0y6cKVIr4eIlKxFd82qiMrN6A6CTUFDeFjeAGERqeBPnJVXU36MHu7Ut7eOVav9OUARARWRkrZRkqzTfZ1iqEBq5Tsg' + head = {'Authorization': 'Bearer {}'.format(my_token)} + + my_data={ + 'x' : f"{lng}", + 'y' : f"{lat}", + 'limit' : '1' + } + + response = requests.post(f"{BASE_URL}/nearest_recycling/get", headers=head, data=my_data) + infos = response.json() + + trashboxes = [] + for trashbox in infos["results"]: + temp_dict = {} + for obj in trashbox["Objects"]: + coord_list = obj["geometry"] + temp_dict["Lat"] = coord_list["coordinates"][1] + temp_dict["Lng"] = coord_list["coordinates"][0] + + properties = obj["properties"] + temp_dict["Name"] = properties["title"] + temp_dict["Address"] = properties["address"] + temp_dict["Categories"] = properties["content_text"].split(',') + trashboxes.append(temp_dict) + + uniq_trashboxes = [ast.literal_eval(el1) for el1 in set([str(el2) for el2 in trashboxes])] + return JSONResponse(uniq_trashboxes) + +@app.get("/{rest_of_path:path}") +async def react_app(req: Request, rest_of_path: str): + return templates.TemplateResponse('index.html', { 'request': req }) +>>>>>>> 3668e8c33f71b7a79a0c83d41a106d9b55e2df71 diff --git a/front/.eslintrc.cjs b/front/.eslintrc.cjs index d46b749..76125b7 100644 --- a/front/.eslintrc.cjs +++ b/front/.eslintrc.cjs @@ -1,16 +1,36 @@ +/* eslint-env node */ + module.exports = { + root: true, env: { browser: true, es2020: true }, extends: [ 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', 'plugin:react-hooks/recommended', ], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: true, + tsconfigRootDir: __dirname, + }, plugins: ['react-refresh'], rules: { - 'react-refresh/only-export-components': 'warn', - 'react/prop-types': 'off' + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/quotes': [ + 'error', + 'single', + { + 'avoidEscape': true, + 'allowTemplateLiterals': true + } + ], + 'jsx-quotes': [2, 'prefer-single'], }, } diff --git a/front/index.html b/front/index.html index 998d6be..1b9c6ff 100644 --- a/front/index.html +++ b/front/index.html @@ -5,11 +5,9 @@ Porridger - -
- + diff --git a/front/package-lock.json b/front/package-lock.json index c2349cf..096b2cd 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -1,32 +1,44 @@ { - "name": "v2", + "name": "front", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "v2", + "name": "front", "version": "0.0.0", "dependencies": { - "bootstrap": "^5.2.3", - "leaflet": "^1.9.3", + "@types/leaflet": "^1.9.3", + "bootstrap": "^5.3.0", + "leaflet": "^1.9.4", "react": "^18.2.0", - "react-bootstrap": "^2.7.4", - "react-bootstrap-typeahead": "^6.1.2", + "react-bootstrap": "^2.8.0", + "react-bootstrap-typeahead": "^6.2.3", "react-dom": "^18.2.0", - "react-insta-stories": "^2.5.9", + "react-insta-stories": "^2.6.1", "react-leaflet": "^4.2.1", - "react-router-dom": "^6.11.1" + "react-router-dom": "^6.14.1" }, "devDependencies": { - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.0.0", - "eslint": "^8.38.0", - "eslint-plugin-react": "^7.32.2", + "@types/react": "^18.2.14", + "@types/react-dom": "^18.2.6", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", + "@vitejs/plugin-react": "^4.0.1", + "eslint": "^8.44.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.3.4", - "vite": "^4.3.2" + "eslint-plugin-react-refresh": "^0.4.1", + "typescript": "^5.0.2", + "vite": "^4.4.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/@ampproject/remapping": { @@ -43,47 +55,47 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz", - "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.6.tgz", + "integrity": "sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", - "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.8.tgz", + "integrity": "sha512-75+KxFB4CZqYRXjx4NlR4J7yGvKumBuZTmV4NV6v09dVXXkuYVYLT68N6HCzLvfJ+fWCxQsntNzKwwIXL4bHnw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.2" }, "engines": { "node": ">=6.9.0" @@ -94,12 +106,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", - "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.7.tgz", + "integrity": "sha512-p+jPjMG+SI8yvIaxGgeW24u7q9+5+TGpZh8/CuB7RhBKd7RCy8FayNEFNNKrNK/eUcY/4ExQqLmyrvBXKsIcwQ==", "dev": true, "dependencies": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -109,16 +121,16 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", - "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz", + "integrity": "sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "@babel/compat-data": "^7.22.6", + "@babel/helper-validator-option": "^7.22.5", + "@nicolo-ribaudo/semver-v6": "^6.3.3", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1" }, "engines": { "node": ">=6.9.0" @@ -128,151 +140,151 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", - "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", - "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", - "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", - "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", - "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -281,9 +293,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", - "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -293,12 +305,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.21.0.tgz", - "integrity": "sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -308,12 +320,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz", - "integrity": "sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -323,9 +335,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -334,33 +346,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", - "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.5", - "@babel/types": "^7.21.5", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -369,13 +381,13 @@ } }, "node_modules/@babel/types": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", - "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -383,9 +395,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", - "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.11.tgz", + "integrity": "sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==", "cpu": [ "arm" ], @@ -399,9 +411,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", - "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz", + "integrity": "sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==", "cpu": [ "arm64" ], @@ -415,9 +427,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", - "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.11.tgz", + "integrity": "sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==", "cpu": [ "x64" ], @@ -431,9 +443,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", - "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz", + "integrity": "sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==", "cpu": [ "arm64" ], @@ -447,9 +459,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", - "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz", + "integrity": "sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==", "cpu": [ "x64" ], @@ -463,9 +475,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", - "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz", + "integrity": "sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==", "cpu": [ "arm64" ], @@ -479,9 +491,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", - "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz", + "integrity": "sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==", "cpu": [ "x64" ], @@ -495,9 +507,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", - "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz", + "integrity": "sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==", "cpu": [ "arm" ], @@ -511,9 +523,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", - "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz", + "integrity": "sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==", "cpu": [ "arm64" ], @@ -527,9 +539,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", - "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz", + "integrity": "sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==", "cpu": [ "ia32" ], @@ -543,9 +555,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", - "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz", + "integrity": "sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==", "cpu": [ "loong64" ], @@ -559,9 +571,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", - "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz", + "integrity": "sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==", "cpu": [ "mips64el" ], @@ -575,9 +587,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", - "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz", + "integrity": "sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==", "cpu": [ "ppc64" ], @@ -591,9 +603,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", - "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz", + "integrity": "sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==", "cpu": [ "riscv64" ], @@ -607,9 +619,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", - "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz", + "integrity": "sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==", "cpu": [ "s390x" ], @@ -623,9 +635,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", - "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz", + "integrity": "sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==", "cpu": [ "x64" ], @@ -639,9 +651,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", - "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz", + "integrity": "sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==", "cpu": [ "x64" ], @@ -655,9 +667,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", - "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz", + "integrity": "sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==", "cpu": [ "x64" ], @@ -671,9 +683,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", - "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz", + "integrity": "sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==", "cpu": [ "x64" ], @@ -687,9 +699,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", - "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz", + "integrity": "sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==", "cpu": [ "arm64" ], @@ -703,9 +715,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", - "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz", + "integrity": "sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==", "cpu": [ "ia32" ], @@ -719,9 +731,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", - "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz", + "integrity": "sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==", "cpu": [ "x64" ], @@ -759,14 +771,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -797,18 +809,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", - "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -892,6 +904,15 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "node_modules/@nicolo-ribaudo/semver-v6": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", + "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -928,20 +949,20 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", - "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, "node_modules/@react-aria/ssr": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.6.0.tgz", - "integrity": "sha512-OFiYQdv+Yk7AO7IsQu/fAEPijbeTwrrEYvdNoJ3sblBBedD5j5fBTNWrUPNVlwC4XWWnWTCMaRIVsJujsFiWXg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.0.tgz", + "integrity": "sha512-bfufjg4ESE5giN+Fxj1XIzS5f/YIhqcGc+Ve+vUUKU8xZ8t/Xtjlv8F3kjqDBQdk//n3mluFY7xG1wQVB9rMLQ==", "dependencies": { - "@swc/helpers": "^0.4.14" + "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" @@ -958,9 +979,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.1.tgz", - "integrity": "sha512-YUkWj+xs0oOzBe74OgErsuR3wVn+efrFhXBWrit50kOiED+pvQe2r6MWY0iJMQU/mSVKxvNzL4ZaYvjdX+G7ZA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.1.tgz", + "integrity": "sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ==", "engines": { "node": ">=14" } @@ -977,9 +998,9 @@ } }, "node_modules/@restart/ui": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.3.tgz", - "integrity": "sha512-7HM5aiSWvJBWr+FghZj/n3PSuH2kUrOPiu/D92aIv1zTL8IBwFoQ3oz/f76svoN5v2PKaP6pQbg6vTcIiSffzg==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", "dependencies": { "@babel/runtime": "^7.21.0", "@popperjs/core": "^2.11.6", @@ -988,7 +1009,7 @@ "@types/warning": "^3.0.0", "dequal": "^2.0.3", "dom-helpers": "^5.2.0", - "uncontrollable": "^7.2.1", + "uncontrollable": "^8.0.1", "warning": "^4.0.3" }, "peerDependencies": { @@ -996,23 +1017,55 @@ "react-dom": ">=16.14.0" } }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.2.tgz", + "integrity": "sha512-/GDx+K1STGtpgTsj5Dj3J51YaKxZDblbCQHTH1zHLuoBEWodj6MjtRVv3TUijj1JYLRLSFsFzN8NV4M3QV4d9w==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@swc/helpers": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", "dependencies": { "tslib": "^2.4.0" } }, + "node_modules/@swc/helpers/node_modules/tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + }, + "node_modules/@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/leaflet": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.3.tgz", + "integrity": "sha512-Caa1lYOgKVqDkDZVWkto2Z5JtVo09spEaUt2S69LiugbBpoqQu92HYFMGUbYezZbnBkyOxMNPXHSgRrRY5UyIA==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { - "version": "18.2.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.6.tgz", - "integrity": "sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==", + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", + "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1020,9 +1073,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", - "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz", + "integrity": "sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==", "dev": true, "dependencies": { "@types/react": "*" @@ -1041,20 +1094,214 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, "node_modules/@types/warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==" }, - "node_modules/@vitejs/plugin-react": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.0.tgz", - "integrity": "sha512-HX0XzMjL3hhOYm+0s95pb0Z7F8O81G7joUHgfDd/9J/ZZf5k4xX6QAMFkKsHFxaHlf6X7GD7+XuaZ66ULiJuhQ==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { - "@babel/core": "^7.21.4", - "@babel/plugin-transform-react-jsx-self": "^7.21.0", - "@babel/plugin-transform-react-jsx-source": "^7.19.6", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.3.tgz", + "integrity": "sha512-pwXDog5nwwvSIzwrvYYmA2Ljcd/ZNlcsSG2Q9CNDBwnsd55UGAyr2doXtB5j+2uymRCnCfExlznzzSFbBRcoCg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.22.5", + "@babel/plugin-transform-react-jsx-self": "^7.22.5", + "@babel/plugin-transform-react-jsx-source": "^7.22.5", "react-refresh": "^0.14.0" }, "engines": { @@ -1065,9 +1312,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1128,79 +1375,13 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/balanced-match": { @@ -1210,9 +1391,9 @@ "dev": true }, "node_modules/bootstrap": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", - "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.0.tgz", + "integrity": "sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==", "funding": [ { "type": "github", @@ -1224,7 +1405,7 @@ } ], "peerDependencies": { - "@popperjs/core": "^2.11.6" + "@popperjs/core": "^2.11.7" } }, "node_modules/brace-expansion": { @@ -1237,10 +1418,22 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -1250,13 +1443,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -1265,19 +1462,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "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", @@ -1288,9 +1472,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001486", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz", - "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==", + "version": "1.0.30001515", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz", + "integrity": "sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==", "dev": true, "funding": [ { @@ -1400,22 +1584,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1424,6 +1592,18 @@ "node": ">=6" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1446,103 +1626,15 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.392", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.392.tgz", - "integrity": "sha512-TXQOMW9tnhIms3jGy/lJctLjICOgyueZFJ1KUtm6DTQ+QpxX3p7ZBwB6syuZ9KBuT5S4XX7bgY1ECPgfxKUdOg==", + "version": "1.4.455", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.455.tgz", + "integrity": "sha512-8tgdX0Odl24LtmLwxotpJCVjIndN559AvaOtd67u+2mo+IDsgsTF580NB+uuDCqsHw8yFg53l5+imFV9Fw3cbA==", "dev": true }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/esbuild": { - "version": "0.17.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", - "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.11.tgz", + "integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==", "dev": true, "hasInstallScript": true, "bin": { @@ -1552,28 +1644,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.18", - "@esbuild/android-arm64": "0.17.18", - "@esbuild/android-x64": "0.17.18", - "@esbuild/darwin-arm64": "0.17.18", - "@esbuild/darwin-x64": "0.17.18", - "@esbuild/freebsd-arm64": "0.17.18", - "@esbuild/freebsd-x64": "0.17.18", - "@esbuild/linux-arm": "0.17.18", - "@esbuild/linux-arm64": "0.17.18", - "@esbuild/linux-ia32": "0.17.18", - "@esbuild/linux-loong64": "0.17.18", - "@esbuild/linux-mips64el": "0.17.18", - "@esbuild/linux-ppc64": "0.17.18", - "@esbuild/linux-riscv64": "0.17.18", - "@esbuild/linux-s390x": "0.17.18", - "@esbuild/linux-x64": "0.17.18", - "@esbuild/netbsd-x64": "0.17.18", - "@esbuild/openbsd-x64": "0.17.18", - "@esbuild/sunos-x64": "0.17.18", - "@esbuild/win32-arm64": "0.17.18", - "@esbuild/win32-ia32": "0.17.18", - "@esbuild/win32-x64": "0.17.18" + "@esbuild/android-arm": "0.18.11", + "@esbuild/android-arm64": "0.18.11", + "@esbuild/android-x64": "0.18.11", + "@esbuild/darwin-arm64": "0.18.11", + "@esbuild/darwin-x64": "0.18.11", + "@esbuild/freebsd-arm64": "0.18.11", + "@esbuild/freebsd-x64": "0.18.11", + "@esbuild/linux-arm": "0.18.11", + "@esbuild/linux-arm64": "0.18.11", + "@esbuild/linux-ia32": "0.18.11", + "@esbuild/linux-loong64": "0.18.11", + "@esbuild/linux-mips64el": "0.18.11", + "@esbuild/linux-ppc64": "0.18.11", + "@esbuild/linux-riscv64": "0.18.11", + "@esbuild/linux-s390x": "0.18.11", + "@esbuild/linux-x64": "0.18.11", + "@esbuild/netbsd-x64": "0.18.11", + "@esbuild/openbsd-x64": "0.18.11", + "@esbuild/sunos-x64": "0.18.11", + "@esbuild/win32-arm64": "0.18.11", + "@esbuild/win32-ia32": "0.18.11", + "@esbuild/win32-x64": "0.18.11" } }, "node_modules/escalade": { @@ -1595,16 +1687,16 @@ } }, "node_modules/eslint": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", - "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.40.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -1615,7 +1707,7 @@ "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.0", "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1623,20 +1715,19 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -1651,35 +1742,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, "node_modules/eslint-plugin-react-hooks": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", @@ -1693,40 +1755,25 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.3.5.tgz", - "integrity": "sha512-61qNIsc7fo9Pp/mju0J83kzvLm0Bsayu7OQSLEoJxLDCBjIIyb87bkzufoOvdDxLkSlMfkF7UxomC4+eztUBSA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.3.tgz", + "integrity": "sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==", "dev": true, "peerDependencies": { "eslint": ">=7" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "estraverse": "^4.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=8.0.0" } }, "node_modules/eslint-visitor-keys": { @@ -1802,6 +1849,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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, + "engines": { + "node": ">=4.0" + } + }, "node_modules/eslint/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -1839,12 +1911,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", + "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -1867,6 +1939,15 @@ "node": ">=0.10" } }, + "node_modules/esquery/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, + "engines": { + "node": ">=4.0" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -1879,7 +1960,7 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { + "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -1888,6 +1969,15 @@ "node": ">=4.0" } }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1902,6 +1992,34 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-glob": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "dev": true, + "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, + "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", @@ -1935,6 +2053,18 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1970,15 +2100,6 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1999,39 +2120,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "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, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2041,36 +2129,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2112,60 +2170,32 @@ "node": ">=4" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2175,57 +2205,6 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2276,20 +2255,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -2298,87 +2263,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "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", @@ -2400,31 +2284,13 @@ "node": ">=0.10.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "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, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12.0" } }, "node_modules/is-path-inside": { @@ -2436,111 +2302,12 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2594,23 +2361,10 @@ "node": ">=6" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/leaflet": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz", - "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==" + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, "node_modules/levn": { "version": "0.4.1", @@ -2671,6 +2425,28 @@ "yallist": "^3.0.2" } }, + "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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2713,10 +2489,16 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/object-assign": { @@ -2727,103 +2509,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "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, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2834,17 +2519,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "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.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -2919,11 +2604,14 @@ "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 + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/picocolors": { "version": "1.0.0", @@ -2931,10 +2619,22 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "version": "8.4.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz", + "integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==", "dev": true, "funding": [ { @@ -3031,9 +2731,9 @@ } }, "node_modules/react-bootstrap": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.7.4.tgz", - "integrity": "sha512-EPKPwhfbxsKsNBhJBitJwqul9fvmlYWSft6jWE2EpqhEyjhqIqNihvQo2onE5XtS+QHOavUSNmA+8Lnv5YeAyg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz", + "integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==", "dependencies": { "@babel/runtime": "^7.21.0", "@restart/hooks": "^0.4.9", @@ -3060,9 +2760,9 @@ } }, "node_modules/react-bootstrap-typeahead": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/react-bootstrap-typeahead/-/react-bootstrap-typeahead-6.1.2.tgz", - "integrity": "sha512-waIWRQ4CUZld69iL+EFiuL/2B+N4LecaAKcRTMQey0NDOM7Sxmtl+iELFzGltt2/DK6yvrxEUCbZI8pTztPFXA==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/react-bootstrap-typeahead/-/react-bootstrap-typeahead-6.2.3.tgz", + "integrity": "sha512-Ge2au2WxR8CWsAH3GbKsaJpIEV2OMKum2Ov7/kuVMBlHNKwsJc2ULJIjk3yZMoTvvfOzOnYDScaWrQwzPc5c/A==", "dependencies": { "@babel/runtime": "^7.14.6", "@popperjs/core": "^2.10.2", @@ -3095,14 +2795,14 @@ } }, "node_modules/react-fast-compare": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.1.tgz", - "integrity": "sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg==" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, "node_modules/react-insta-stories": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/react-insta-stories/-/react-insta-stories-2.5.9.tgz", - "integrity": "sha512-fTcCmz9rAATPR8EVLfRlxXSeC12splCxTRG4zFMltFvb89HsyxKIqL3UNP7DvguFczVqHasdufIKwKwWKpQLfg==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/react-insta-stories/-/react-insta-stories-2.6.1.tgz", + "integrity": "sha512-JsUozczN5FO97ski6LFxxU03NNy5PspD5e1yrRZ2oXJzoAKw2RP3pg3N+LPb9lU1w2LhX/66YAPC3/TpRa3Udg==", "peerDependencies": { "react": ">=16.8.2" } @@ -3173,11 +2873,11 @@ } }, "node_modules/react-router": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.11.1.tgz", - "integrity": "sha512-OZINSdjJ2WgvAi7hgNLazrEV8SGn6xrKA+MkJe9wVDMZ3zQ6fdJocUjpCUCI0cNrelWjcvon0S/QK/j0NzL3KA==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.1.tgz", + "integrity": "sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g==", "dependencies": { - "@remix-run/router": "1.6.1" + "@remix-run/router": "1.7.1" }, "engines": { "node": ">=14" @@ -3187,12 +2887,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.11.1.tgz", - "integrity": "sha512-dPC2MhoPeTQ1YUOt5uIK376SMNWbwUxYRWk2ZmTT4fZfwlOvabF8uduRKKJIyfkCZvMgiF0GSCQckmkGGijIrg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.1.tgz", + "integrity": "sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw==", "dependencies": { - "@remix-run/router": "1.6.1", - "react-router": "6.11.1" + "@remix-run/router": "1.7.1", + "react-router": "6.14.1" }, "engines": { "node": ">=14" @@ -3222,40 +2922,6 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.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/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3291,9 +2957,9 @@ } }, "node_modules/rollup": { - "version": "3.21.6", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.6.tgz", - "integrity": "sha512-SXIICxvxQxR3D4dp/3LDHZIJPC8a4anKMHd4E3Jiz2/JnY+2bEjqrOokAauc5ShGVNFHlEFjBXAXlaxkJqIqSg==", + "version": "3.26.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz", + "integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -3329,20 +2995,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -3360,14 +3012,38 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3389,18 +3065,13 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, "node_modules/source-map-js": { @@ -3412,70 +3083,6 @@ "node": ">=0.10.0" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3512,18 +3119,6 @@ "node": ">=4" } }, - "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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3539,10 +3134,38 @@ "node": ">=4" } }, + "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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -3568,33 +3191,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=14.17" } }, "node_modules/uncontrollable": { @@ -3651,14 +3258,14 @@ } }, "node_modules/vite": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.5.tgz", - "integrity": "sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.2.tgz", + "integrity": "sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.24", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" @@ -3666,12 +3273,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -3684,6 +3295,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -3721,51 +3335,6 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/front/package.json b/front/package.json index 329bff8..5420348 100644 --- a/front/package.json +++ b/front/package.json @@ -1,33 +1,38 @@ { - "name": "v2", + "name": "front", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite --host 0.0.0.0", - "build": "vite build", - "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "build": "tsc && vite build", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "typecheck": "tsc", + "addFetchApiRoute": "bash utils/addFetchApiRoute.sh" }, "dependencies": { - "bootstrap": "^5.2.3", - "leaflet": "^1.9.3", + "@types/leaflet": "^1.9.3", + "bootstrap": "^5.3.0", + "leaflet": "^1.9.4", "react": "^18.2.0", - "react-bootstrap": "^2.7.4", - "react-bootstrap-typeahead": "^6.1.2", + "react-bootstrap": "^2.8.0", + "react-bootstrap-typeahead": "^6.2.3", "react-dom": "^18.2.0", - "react-insta-stories": "^2.5.9", + "react-insta-stories": "^2.6.1", "react-leaflet": "^4.2.1", - "react-router-dom": "^6.11.1" + "react-router-dom": "^6.14.1" }, "devDependencies": { - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", - "@vitejs/plugin-react": "^4.0.0", - "eslint": "^8.38.0", - "eslint-plugin-react": "^7.32.2", + "@types/react": "^18.2.14", + "@types/react-dom": "^18.2.6", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", + "@vitejs/plugin-react": "^4.0.1", + "eslint": "^8.44.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.3.4", - "vite": "^4.3.2" + "eslint-plugin-react-refresh": "^0.4.1", + "typescript": "^5.0.2", + "vite": "^4.4.0" } } diff --git a/front/public/PORRIDGE.jpg b/front/public/PORRIDGE.jpg index 3dba0d9..8d35fa5 100644 Binary files a/front/public/PORRIDGE.jpg and b/front/public/PORRIDGE.jpg differ diff --git a/front/public/bred.jpg b/front/public/bred.jpg new file mode 100644 index 0000000..41afc81 Binary files /dev/null and b/front/public/bred.jpg differ diff --git a/front/public/cloth.jpg b/front/public/cloth.jpg index 9c40984..7d00ff0 100644 Binary files a/front/public/cloth.jpg and b/front/public/cloth.jpg differ diff --git a/front/public/conserves.jpg b/front/public/conserves.jpg index 87d70ae..21509e4 100644 Binary files a/front/public/conserves.jpg and b/front/public/conserves.jpg differ diff --git a/front/public/conspects.jpg b/front/public/conspects.jpg new file mode 100644 index 0000000..4b44b5a Binary files /dev/null and b/front/public/conspects.jpg differ diff --git a/front/public/dinner.jpg b/front/public/dinner.jpg index 4d9d74a..b57b692 100644 Binary files a/front/public/dinner.jpg and b/front/public/dinner.jpg differ diff --git a/front/public/fruits_vegatables.jpg b/front/public/fruits_vegatables.jpg index 38edbca..d0cd577 100644 Binary files a/front/public/fruits_vegatables.jpg and b/front/public/fruits_vegatables.jpg differ diff --git a/front/public/milk.jpg b/front/public/milk.jpg new file mode 100644 index 0000000..06a0c8c Binary files /dev/null and b/front/public/milk.jpg differ diff --git a/front/public/other_things.jpg b/front/public/other_things.jpg index 5fc9081..8e0dc1c 100644 Binary files a/front/public/other_things.jpg and b/front/public/other_things.jpg differ diff --git a/front/public/pens.jpg b/front/public/pens.jpg index 560acfe..49d5272 100644 Binary files a/front/public/pens.jpg and b/front/public/pens.jpg differ diff --git a/front/public/soup.jpg b/front/public/soup.jpg index 6f9ba11..302b421 100644 Binary files a/front/public/soup.jpg and b/front/public/soup.jpg differ diff --git a/front/public/wathing.jpg b/front/public/wathing.jpg new file mode 100644 index 0000000..fac59b0 Binary files /dev/null and b/front/public/wathing.jpg differ diff --git a/front/src/App.css b/front/src/App.css index 2765713..9866f71 100644 --- a/front/src/App.css +++ b/front/src/App.css @@ -1,16 +1,16 @@ body { - height: 100vh; - overflow: hidden; - color: white; - font-family: sans-serif; + height: 100vh; + overflow: hidden; + color: white; + font-family: sans-serif; } .modal-content, .modal-content .form-select { - background-color: rgb(17, 17, 17) !important; + background-color: rgb(17, 17, 17) !important; } /* В связи со сложившейся политической обстановкой */ .leaflet-attribution-flag { - position: absolute; - right: -100px; + position: absolute; + right: -100px; } diff --git a/front/src/App.jsx b/front/src/App.tsx similarity index 60% rename from front/src/App.jsx rename to front/src/App.tsx index 6329f68..30a8afa 100644 --- a/front/src/App.jsx +++ b/front/src/App.tsx @@ -1,11 +1,9 @@ -import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom' +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom' import { HomePage, AddPage, LoginPage, UserPage } from './pages' - import { WithToken } from './components' import 'leaflet/dist/leaflet.css' - import './App.css' function App() { @@ -13,18 +11,17 @@ function App() { } /> - } /> - - {/* */} -

For Yet Go Home

+ } /> - } /> + } />
) diff --git a/front/src/api/announcement/index.ts b/front/src/api/announcement/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/front/src/api/announcement/types.ts b/front/src/api/announcement/types.ts new file mode 100644 index 0000000..ea04b23 --- /dev/null +++ b/front/src/api/announcement/types.ts @@ -0,0 +1,61 @@ +import { isObject } from '../../utils/types' +import { Category, isCategory } from '../../assets/category' + +type AnnouncementResponse = { + id: number, + user_id: number, + name: string, + category: Category, + best_by: number, + address: string, + longtitude: number, + latitude: number, + description: string, + src: string | null, + metro: string, + trashId: number | null, + booked_by: number +} + +const isAnnouncementResponse = (obj: unknown): obj is AnnouncementResponse => ( + isObject(obj, { + 'id': 'number', + 'user_id': 'number', + 'name': 'string', + 'category': isCategory, + 'best_by': 'number', + 'address': 'string', + 'longtitude': 'number', + 'latitude': 'number', + 'description': 'string', + 'src': 'string?', + 'metro': 'string', + 'trashId': 'number?', + 'booked_by': 'number' + }) +) + +type Announcement = { + id: number, + userId: number, + name: string, + category: Category, + bestBy: number, + address: string, + lng: number, + lat: number, + description: string | null, + src: string | null, + metro: string, + trashId: number | null, + bookedBy: number +} + +export type { + Announcement, + AnnouncementResponse, +} + +export { + isAnnouncementResponse, +} diff --git a/front/src/api/announcements/index.ts b/front/src/api/announcements/index.ts new file mode 100644 index 0000000..2e7c3d7 --- /dev/null +++ b/front/src/api/announcements/index.ts @@ -0,0 +1,25 @@ +import { API_URL } from '../../config' +import { FiltersType, URLEncodeFilters } from '../../utils/filters' +import { Announcement } from '../announcement/types' +import { AnnouncementsResponse } from './types' + +const initialAnnouncements: Announcement[] = [] + +const composeAnnouncementsURL = (filters: FiltersType) => ( + API_URL + '/announcements?' + new URLSearchParams(URLEncodeFilters(filters)).toString() +) + +const processAnnouncements = (data: AnnouncementsResponse): Announcement[] => { + const annList = data.list_of_announcements + + return annList.map(ann => ({ + ...ann, + lat: ann.latitude, + lng: ann.longtitude, + bestBy: ann.best_by, + bookedBy: ann.booked_by, + userId: ann.user_id + })) +} + +export { initialAnnouncements, composeAnnouncementsURL, processAnnouncements } diff --git a/front/src/api/announcements/types.ts b/front/src/api/announcements/types.ts new file mode 100644 index 0000000..fefd5f2 --- /dev/null +++ b/front/src/api/announcements/types.ts @@ -0,0 +1,22 @@ +import { isArrayOf, isObject } from '../../utils/types' +import { AnnouncementResponse, isAnnouncementResponse } from '../announcement/types' + +type AnnouncementsResponse = { + list_of_announcements: AnnouncementResponse[], + Success: boolean +} + +const isAnnouncementsResponse = (obj: unknown): obj is AnnouncementsResponse => ( + isObject(obj, { + 'list_of_announcements': obj => isArrayOf(obj, isAnnouncementResponse), + 'Success': 'boolean' + }) +) + +export type { + AnnouncementsResponse, +} + +export { + isAnnouncementsResponse, +} diff --git a/front/src/api/osmAddress/index.ts b/front/src/api/osmAddress/index.ts new file mode 100644 index 0000000..ec7ef9e --- /dev/null +++ b/front/src/api/osmAddress/index.ts @@ -0,0 +1,14 @@ +import { LatLng } from 'leaflet' +import { OsmAddressResponse } from './types' + +const initialOsmAddress = '' + +const composeOsmAddressURL = (addressPosition: LatLng) => ( + `${location.protocol}//nominatim.openstreetmap.org/reverse?format=json&accept-language=ru&lat=${addressPosition.lat}&lon=${addressPosition.lng}` +) + +const processOsmAddress = (data: OsmAddressResponse): string => ( + data.display_name +) + +export { initialOsmAddress, composeOsmAddressURL, processOsmAddress } diff --git a/front/src/api/osmAddress/types.ts b/front/src/api/osmAddress/types.ts new file mode 100644 index 0000000..88ba31b --- /dev/null +++ b/front/src/api/osmAddress/types.ts @@ -0,0 +1,19 @@ +import { isObject } from '../../utils/types' + +type OsmAddressResponse = { + display_name: string +} + +const isOsmAddressResponse = (obj: unknown): obj is OsmAddressResponse => ( + isObject(obj, { + 'display_name': 'string', + }) +) + +export type { + OsmAddressResponse, +} + +export { + isOsmAddressResponse, +} diff --git a/front/src/api/trashbox/index.ts b/front/src/api/trashbox/index.ts new file mode 100644 index 0000000..acf4b1d --- /dev/null +++ b/front/src/api/trashbox/index.ts @@ -0,0 +1,12 @@ +import { LatLng } from 'leaflet' + +import { API_URL } from '../../config' + +const composeTrashboxURL = (position: LatLng) => ( + API_URL + '/trashbox?' + new URLSearchParams({ + lat: position.lat.toString(), + lng: position.lng.toString() + }).toString() +) + +export { composeTrashboxURL } diff --git a/front/src/api/trashbox/types.ts b/front/src/api/trashbox/types.ts new file mode 100644 index 0000000..b2a17c0 --- /dev/null +++ b/front/src/api/trashbox/types.ts @@ -0,0 +1,26 @@ +import { isArrayOf, isObject, isString } from '../../utils/types' + +type Trashbox = { + Lat: number, + Lng: number, + Address: string, + Categories: string[] +} + +const isTrashbox = (obj: unknown): obj is Trashbox => ( + isObject(obj, { + 'Lat': 'number', + 'Lng': 'number', + 'Address': 'string', + 'Categories': obj => isArrayOf(obj, isString) + }) +) + +type TrashboxResponse = Trashbox[] + +const isTrashboxResponse = (obj: unknown): obj is Trashbox[] => ( + isArrayOf(obj, isTrashbox) +) + +export type { Trashbox, TrashboxResponse } +export { isTrashbox, isTrashboxResponse } diff --git a/front/src/assets/category.js b/front/src/assets/category.js deleted file mode 100644 index 89252f1..0000000 --- a/front/src/assets/category.js +++ /dev/null @@ -1,32 +0,0 @@ -const categoryGraphics = new Map([ - ["PORRIDGE", "static/PORRIDGE.jpg"], - ["conspects", "static/conspects.jpg"], - ["milk", "static/milk.jpg"], - ["bred", "static/bred.jpg"], - ["wathing", "static/wathing.jpg"], - ["cloth", "static/cloth.jpg"], - ["fruits_vegatables", "static/fruits_vegatables.jpg"], - ["soup", "static/soup.jpg"], - ["dinner", "static/dinner.jpg"], - ["conserves", "static/conserves.jpg"], - ["pens", "static/pens.jpg"], - ["other_things", "static/other_things.jpg"] - -]) - -const categoryNames = new Map([ - ["PORRIDGE", "PORRIDGE"], - ["conspects", "Конспекты"], - ["milk", "Молочные продукты"], - ["bred", "Хлебобулочные изделия"], - ["wathing", "Моющие средства"], - ["cloth", "Одежда"], - ["fruits_vegatables", "Фрукты и овощи"], - ["soup", "Супы"], - ["dinner", "Ужин"], - ["conserves", "Консервы"], - ["pens", "Канцелярия"], - ["other_things", "Всякая всячина"] -]) - -export { categoryNames, categoryGraphics } diff --git a/front/src/assets/category.ts b/front/src/assets/category.ts new file mode 100644 index 0000000..5ceda7f --- /dev/null +++ b/front/src/assets/category.ts @@ -0,0 +1,42 @@ +import { isLiteralUnion } from '../utils/types' + +const categories = ['PORRIDGE', 'conspects', 'milk', 'bred', 'wathing', 'cloth', + 'fruits_vegatables', 'soup', 'dinner', 'conserves', 'pens', 'other_things'] as const +type Category = typeof categories[number] + +const isCategory = (obj: unknown): obj is Category => ( + isLiteralUnion(obj, categories) +) + +const categoryGraphics: Record = { + 'PORRIDGE': 'static/PORRIDGE.jpg', + 'conspects': 'static/conspects.jpg', + 'milk': 'static/milk.jpg', + 'bred': 'static/bred.jpg', + 'wathing': 'static/wathing.jpg', + 'cloth': 'static/cloth.jpg', + 'fruits_vegatables': 'static/fruits_vegatables.jpg', + 'soup': 'static/soup.jpg', + 'dinner': 'static/dinner.jpg', + 'conserves': 'static/conserves.jpg', + 'pens': 'static/pens.jpg', + 'other_things': 'static/other_things.jpg', +} + +const categoryNames: Record = { + 'PORRIDGE': 'PORRIDGE', + 'conspects': 'Конспекты', + 'milk': 'Молочные продукты', + 'bred': 'Хлебобулочные изделия', + 'wathing': 'Моющие средства', + 'cloth': 'Одежда', + 'fruits_vegatables': 'Фрукты и овощи', + 'soup': 'Супы', + 'dinner': 'Ужин', + 'conserves': 'Консервы', + 'pens': 'Канцелярия', + 'other_things': 'Всякая всячина', +} + +export type { Category } +export { categories, categoryNames, categoryGraphics, isCategory } diff --git a/front/src/assets/itemMarker.png b/front/src/assets/itemMarker.png new file mode 100644 index 0000000..f68bbb3 Binary files /dev/null and b/front/src/assets/itemMarker.png differ diff --git a/front/src/assets/metro.js b/front/src/assets/metro.js deleted file mode 100644 index f577ae3..0000000 --- a/front/src/assets/metro.js +++ /dev/null @@ -1,101 +0,0 @@ -const stations = { - red: new Set([ - "Девяткино", - "Гражданский проспект", - "Академическая", - "Политехническая", - "Площадь Мужества", - "Лесная", - "Выборгская", - "Площадь Ленина", - "Чернышевская", - "Площадь Восстания", - "Владимирская", - "Пушкинская", - "Технологический институт", - "Балтийская", - "Нарвская", - "Кировский завод", - "Автово", - "Ленинский проспект", - "Проспект Ветеранов" - ]), - blue: new Set([ - "Парнас", - "Проспект Просвещения", - "Озерки", - "Удельная", - "Пионерская", - "Чёрная речка", - "Петроградская", - "Горьковская", - "Невский проспект", - "Сенная площадь", - "Технологический институт", - "Фрунзенская", - "Московские ворота", - "Электросила", - "Парк Победы", - "Московская", - "Звёздная", - "Купчино" - ]), - green: new Set([ - "Приморская", - "Беговая", - "Василеостровская", - "Гостиный двор", - "Маяковская", - "Площадь Александра Невского", - "Елизаровская", - "Ломоносовская", - "Пролетарская", - "Обухово", - "Рыбацкое" - ]), - orange: new Set([ - "Спасская", - "Достоевская", - "Лиговский проспект", - "Площадь Александра Невского", - "Новочеркасская", - "Ладожская", - "Проспект Большевиков", - "Улица Дыбенко" - ]), - violet: new Set([ - "Комендантский проспект", - "Старая Деревня", - "Крестовский остров", - "Чкаловская", - "Спортивная", - "Адмиралтейская", - "Садовая", - "Звенигородская", - "Обводный канал", - "Волковская", - "Бухарестская", - "Международная", - "Проспект славы", - "Дунайскай", - "Шушары" - ]), -} - -const colors = { - red: "#D6083B", - blue: "#0078C9", - green: "#009A49", - orange: "#EA7125", - violet: "#702785", -} - -const lines = { - red: "Красная", - blue: "Синяя", - green: "Зелёная", - orange: "Оранжевая", - violet: "Фиолетовая", -} - -export { stations, colors, lines } diff --git a/front/src/assets/metro.ts b/front/src/assets/metro.ts new file mode 100644 index 0000000..76168a5 --- /dev/null +++ b/front/src/assets/metro.ts @@ -0,0 +1,109 @@ +const lines = ['red', 'blue', 'green', 'orange', 'violet'] as const +type Lines = typeof lines[number] + +const stations: Record> = { + red: new Set([ + 'Девяткино', + 'Гражданский проспект', + 'Академическая', + 'Политехническая', + 'Площадь Мужества', + 'Лесная', + 'Выборгская', + 'Площадь Ленина', + 'Чернышевская', + 'Площадь Восстания', + 'Владимирская', + 'Пушкинская', + 'Технологический институт', + 'Балтийская', + 'Нарвская', + 'Кировский завод', + 'Автово', + 'Ленинский проспект', + 'Проспект Ветеранов' + ]), + blue: new Set([ + 'Парнас', + 'Проспект Просвещения', + 'Озерки', + 'Удельная', + 'Пионерская', + 'Чёрная речка', + 'Петроградская', + 'Горьковская', + 'Невский проспект', + 'Сенная площадь', + 'Технологический институт', + 'Фрунзенская', + 'Московские ворота', + 'Электросила', + 'Парк Победы', + 'Московская', + 'Звёздная', + 'Купчино' + ]), + green: new Set([ + 'Приморская', + 'Беговая', + 'Василеостровская', + 'Гостиный двор', + 'Маяковская', + 'Площадь Александра Невского', + 'Елизаровская', + 'Ломоносовская', + 'Пролетарская', + 'Обухово', + 'Рыбацкое' + ]), + orange: new Set([ + 'Спасская', + 'Достоевская', + 'Лиговский проспект', + 'Площадь Александра Невского', + 'Новочеркасская', + 'Ладожская', + 'Проспект Большевиков', + 'Улица Дыбенко' + ]), + violet: new Set([ + 'Комендантский проспект', + 'Старая Деревня', + 'Крестовский остров', + 'Чкаловская', + 'Спортивная', + 'Адмиралтейская', + 'Садовая', + 'Звенигородская', + 'Обводный канал', + 'Волковская', + 'Бухарестская', + 'Международная', + 'Проспект славы', + 'Дунайскай', + 'Шушары' + ]), +} + +const colors: Record = { + red: '#D6083B', + blue: '#0078C9', + green: '#009A49', + orange: '#EA7125', + violet: '#702785', +} + +const lineNames: Record = { + red: 'Красная', + blue: 'Синяя', + green: 'Зелёная', + orange: 'Оранжевая', + violet: 'Фиолетовая', +} + +const lineByName = (name: string) => ( + lines.find(line => stations[line].has(name)) +) + +export type { Lines } +export { lines, stations, colors, lineNames, lineByName } diff --git a/front/src/assets/trashMarker.png b/front/src/assets/trashMarker.png new file mode 100644 index 0000000..ce5a53b Binary files /dev/null and b/front/src/assets/trashMarker.png differ diff --git a/front/src/components/AnnouncementDetails.jsx b/front/src/components/AnnouncementDetails.tsx similarity index 60% rename from front/src/components/AnnouncementDetails.jsx rename to front/src/components/AnnouncementDetails.tsx index 0439fa0..352b138 100644 --- a/front/src/components/AnnouncementDetails.jsx +++ b/front/src/components/AnnouncementDetails.tsx @@ -4,16 +4,32 @@ import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet' import LineDot from './LineDot' import { categoryNames } from '../assets/category' import { useBook } from '../hooks/api' +import { Announcement } from '../api/announcement/types' +import { iconItem } from '../utils/markerIcons' +import { CSSProperties } from 'react' -function AnnouncementDetails({ close, announcement: { id, name, category, bestBy, description, lat, lng, address, metro } }) { +type AnnouncementDetailsProps = { + close: () => void, + announcement: Announcement +} + +const styles = { + container: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + } as CSSProperties, +} + +function AnnouncementDetails({ close, announcement: { id, name, category, bestBy, description, lat, lng, address, metro } }: AnnouncementDetailsProps) { const { handleBook, status: bookStatus } = useBook(id) return (
- + Подробнее @@ -23,19 +39,19 @@ function AnnouncementDetails({ close, announcement: { id, name, category, bestBy

{name}

- {categoryNames.get(category)} + {categoryNames[category]} {/* dot */} Годен до {new Date(bestBy).toLocaleString('ru-RU')}

{description}

- + - + {address}
@@ -46,8 +62,8 @@ function AnnouncementDetails({ close, announcement: { id, name, category, bestBy
-
@@ -55,4 +71,4 @@ function AnnouncementDetails({ close, announcement: { id, name, category, bestBy ) } -export default AnnouncementDetails \ No newline at end of file +export default AnnouncementDetails diff --git a/front/src/components/AuthForm.tsx b/front/src/components/AuthForm.tsx new file mode 100644 index 0000000..bd5f129 --- /dev/null +++ b/front/src/components/AuthForm.tsx @@ -0,0 +1,56 @@ +import { FormEventHandler } from 'react' +import { Button, Form } from 'react-bootstrap' + +type AuthFormProps = { + register: boolean + handleAuth: FormEventHandler, + loading: boolean, + error: string +} + +function AuthForm ({ handleAuth, register, loading, error }: AuthFormProps) { + const buttonText = loading ? 'Загрузка...' : (error || (register ? 'Зарегистрироваться' : 'Войти')) + + return ( +
+ + Почта + + + + {register && <> + + Имя + + + + + Фамилия + + + } + + + Пароль + + + + {register && + + + + + Я согласен с условиями обработки персональных данных + + + + } + + +
+ ) +} + +export default AuthForm diff --git a/front/src/components/BottomNavBar.jsx b/front/src/components/BottomNavBar.jsx deleted file mode 100644 index c36f372..0000000 --- a/front/src/components/BottomNavBar.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Link } from 'react-router-dom' - -import addIcon from '../assets/addIcon.svg' -import filterIcon from '../assets/filterIcon.svg' -import userIcon from '../assets/userIcon.svg' - -const navBarStyles = { - backgroundColor: 'var(--bs-success)', - height: 56, - width: "100%", -} - -const navBarGroupStyles = { - display: "flex", - flexDirection: "row", - height: "100%", - margin: "auto" -} - -const navBarElementStyles = { - width: "100%", - height: "100%", - display: "flex", - alignItems: "center", - justifyContent: "center" -} - -function BottomNavBar({ width, toggleFilters }) { - return ( -
-
- - toggleFilters(true)}> - Фильтровать объявления - - - - Опубликовать объявление - - - - Личный кабинет - - -
-
- ) -} - -export default BottomNavBar \ No newline at end of file diff --git a/front/src/components/BottomNavBar.tsx b/front/src/components/BottomNavBar.tsx new file mode 100644 index 0000000..03c8d7f --- /dev/null +++ b/front/src/components/BottomNavBar.tsx @@ -0,0 +1,58 @@ +import { Link } from 'react-router-dom' + +import addIcon from '../assets/addIcon.svg' +import filterIcon from '../assets/filterIcon.svg' +import userIcon from '../assets/userIcon.svg' +import { CSSProperties } from 'react' + +const styles = { + navBar: { + backgroundColor: 'var(--bs-success)', + height: 56, + width: '100%', + } as CSSProperties, + navBarGroup: { + display: 'flex', + flexDirection: 'row', + height: '100%', + margin: 'auto' + } as CSSProperties, + navBarElement: { + width: '100%', + height: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + } as CSSProperties, +} + + + +type BottomNavBarProps = { + width: number, + toggleFilters: (p: boolean) => void +} + +function BottomNavBar({ width, toggleFilters }: BottomNavBarProps) { + return ( +
+
+ + toggleFilters(true)}> + Фильтровать объявления + + + + Опубликовать объявление + + + + Личный кабинет + + +
+
+ ) +} + +export default BottomNavBar diff --git a/front/src/components/ClickHandler.jsx b/front/src/components/ClickHandler.jsx deleted file mode 100644 index 1404c20..0000000 --- a/front/src/components/ClickHandler.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useMapEvent } from "react-leaflet" - -function ClickHandler({ setPosition }) { - const map = useMapEvent('click', (e) => { - setPosition(e.latlng) - map.setView(e.latlng) - }) - - return null -} - -export default ClickHandler \ No newline at end of file diff --git a/front/src/components/ClickHandler.tsx b/front/src/components/ClickHandler.tsx new file mode 100644 index 0000000..1391c30 --- /dev/null +++ b/front/src/components/ClickHandler.tsx @@ -0,0 +1,15 @@ +import { useMapEvent } from 'react-leaflet' +import { LatLng } from 'leaflet' + +import { SetState } from '../utils/types' + +function ClickHandler({ setPosition }: { setPosition: SetState }) { + const map = useMapEvent('click', (e) => { + setPosition(e.latlng) + map.setView(e.latlng) + }) + + return null +} + +export default ClickHandler diff --git a/front/src/components/Filters.jsx b/front/src/components/Filters.tsx similarity index 54% rename from front/src/components/Filters.jsx rename to front/src/components/Filters.tsx index a8e156c..e0e3619 100644 --- a/front/src/components/Filters.jsx +++ b/front/src/components/Filters.tsx @@ -1,11 +1,21 @@ -import { Button, Form, Modal } from "react-bootstrap" +import { Button, Form, Modal } from 'react-bootstrap' +import { FormEventHandler } from 'react' -import { categoryNames } from "../assets/category" -import { stations, lines } from '../assets/metro' +import { categories, categoryNames } from '../assets/category' +import { stations, lines, lineNames } from '../assets/metro' +import { FiltersType } from '../utils/filters' +import { SetState } from '../utils/types' -function Filters({ filter, setFilter, filterShown, setFilterShown }) { +type FiltersProps = { + filter: FiltersType, + setFilter: SetState, + filterShown: boolean, + setFilterShown: SetState +} - const handleSubmit = (event) => { +function Filters({ filter, setFilter, filterShown, setFilterShown }: FiltersProps) { + + const handleSubmit: FormEventHandler = (event) => { event.preventDefault(); event.stopPropagation(); @@ -13,8 +23,8 @@ function Filters({ filter, setFilter, filterShown, setFilterShown }) { setFilter(prev => ({ ...prev, - category: formData.get("category") || null, - metro: formData.get("metro") || null + category: (formData.get('category') as (FiltersType['category'] | null)) || undefined, + metro: (formData.get('metro') as (FiltersType['metro'] | null)) || undefined })) setFilterShown(false) @@ -30,35 +40,35 @@ function Filters({ filter, setFilter, filterShown, setFilterShown }) {
- + Категория - - - + Станция метро - - - @@ -75,4 +85,4 @@ function Filters({ filter, setFilter, filterShown, setFilterShown }) { ) } -export default Filters \ No newline at end of file +export default Filters diff --git a/front/src/components/LineDot.jsx b/front/src/components/LineDot.jsx deleted file mode 100644 index c9ee77c..0000000 --- a/front/src/components/LineDot.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { colors, lines } from '../assets/metro' -import { lineByName } from '../utils/metro' - -function LineDot({ station }) { - const line = lineByName(station) - const lineTitle = lines[line] - const color = colors[line] - - return -} - -export default LineDot diff --git a/front/src/components/LineDot.tsx b/front/src/components/LineDot.tsx new file mode 100644 index 0000000..a3f16de --- /dev/null +++ b/front/src/components/LineDot.tsx @@ -0,0 +1,15 @@ +import { colors, lineNames, lineByName } from '../assets/metro' + +function LineDot({ station }: { station: string }) { + const line = lineByName(station) + + if (line == undefined) + return <> + + const lineTitle = lineNames[line] + const color = colors[line] + + return +} + +export default LineDot diff --git a/front/src/components/LocationMarker.jsx b/front/src/components/LocationMarker.jsx deleted file mode 100644 index d60f39c..0000000 --- a/front/src/components/LocationMarker.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Marker, Popup, useMapEvents } from "react-leaflet" - -const LocationMarker = ({ address, position, setPosition }) => { - - const map = useMapEvents({ - dragend: () => { - setPosition(map.getCenter()) - }, - zoomend: () => { - setPosition(map.getCenter()) - }, - resize: () => { - setPosition(map.getCenter()) - } - }) - - return ( - - - {address} - {position.lat.toFixed(4)}, {position.lng.toFixed(4)} - - - ) -} - -export default LocationMarker \ No newline at end of file diff --git a/front/src/components/LocationMarker.tsx b/front/src/components/LocationMarker.tsx new file mode 100644 index 0000000..b4a1db5 --- /dev/null +++ b/front/src/components/LocationMarker.tsx @@ -0,0 +1,37 @@ +import { Marker, Popup, useMapEvents } from 'react-leaflet' +import { LatLng } from 'leaflet' + +import { SetState } from '../utils/types' +import { iconItem } from '../utils/markerIcons' + +type LocationMarkerProps = { + address: string, + position: LatLng, + setPosition: SetState +} + +function LocationMarker({ address, position, setPosition }: LocationMarkerProps) { + + const map = useMapEvents({ + dragend: () => { + setPosition(map.getCenter()) + }, + zoomend: () => { + setPosition(map.getCenter()) + }, + resize: () => { + setPosition(map.getCenter()) + } + }) + + return ( + + + {address} + {position.lat.toFixed(4)}, {position.lng.toFixed(4)} + + + ) +} + +export default LocationMarker diff --git a/front/src/components/TrashboxMarkers.jsx b/front/src/components/TrashboxMarkers.tsx similarity index 56% rename from front/src/components/TrashboxMarkers.jsx rename to front/src/components/TrashboxMarkers.tsx index 5838d8f..a1a625a 100644 --- a/front/src/components/TrashboxMarkers.jsx +++ b/front/src/components/TrashboxMarkers.tsx @@ -1,15 +1,23 @@ -import { Marker, Popup } from "react-leaflet" +import { Marker, Popup } from 'react-leaflet' -const TrashboxMarkers = ({ trashboxes, selectTrashbox }) => { +import { Trashbox } from '../api/trashbox/types' +import { iconTrash } from '../utils/markerIcons' + +type TrashboxMarkersProps = { + trashboxes: Trashbox[], + selectTrashbox: ({ index, category }: { index: number, category: string }) => void +} + +function TrashboxMarkers({ trashboxes, selectTrashbox }: TrashboxMarkersProps) { return ( <>{trashboxes.map((trashbox, index) => ( - +

{trashbox.Address}

Тип мусора: <> {trashbox.Categories.map((category, j) => - selectTrashbox({ index, category })}> + selectTrashbox({ index, category })}> {category} {(j < trashbox.Categories.length - 1) ? ', ' : ''} @@ -23,4 +31,4 @@ const TrashboxMarkers = ({ trashboxes, selectTrashbox }) => { ) } -export default TrashboxMarkers \ No newline at end of file +export default TrashboxMarkers diff --git a/front/src/components/WithToken.jsx b/front/src/components/WithToken.jsx deleted file mode 100644 index 258f072..0000000 --- a/front/src/components/WithToken.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useEffect } from "react" -import { getToken } from "../utils/auth" -import { useNavigate } from "react-router-dom" - -function WithToken({ children }) { - const navigate = useNavigate() - - useEffect(() => { - if (!getToken()) { - return navigate("/login") - } - }, [navigate]) - - return ( - <>{children} - ) -} - -export default WithToken \ No newline at end of file diff --git a/front/src/components/WithToken.tsx b/front/src/components/WithToken.tsx new file mode 100644 index 0000000..61aae58 --- /dev/null +++ b/front/src/components/WithToken.tsx @@ -0,0 +1,20 @@ +import { PropsWithChildren, useEffect } from 'react' +import { useNavigate } from 'react-router-dom' + +import { getToken } from '../utils/auth' + +function WithToken({ children }: PropsWithChildren) { + const navigate = useNavigate() + + useEffect(() => { + if (!getToken()) { + return navigate('/login') + } + }, [navigate]) + + return ( + <>{children} + ) +} + +export default WithToken diff --git a/front/src/components/index.js b/front/src/components/index.js deleted file mode 100644 index eeffa7d..0000000 --- a/front/src/components/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import AnnouncementDetails from "./AnnouncementDetails" -import BottomNavBar from "./BottomNavBar" -import Filters from "./Filters" -import LineDot from "./LineDot" -import LocationMarker from './LocationMarker' -import TrashboxMarkers from './TrashboxMarkers' -import WithToken from './WithToken' -import ClickHandler from './ClickHandler' - -export { - AnnouncementDetails, - BottomNavBar, - Filters, - LineDot, - LocationMarker, - TrashboxMarkers, - WithToken, - ClickHandler, -} diff --git a/front/src/components/index.ts b/front/src/components/index.ts new file mode 100644 index 0000000..aa92c4b --- /dev/null +++ b/front/src/components/index.ts @@ -0,0 +1,9 @@ +export { default as AnnouncementDetails } from './AnnouncementDetails' +export { default as BottomNavBar } from './BottomNavBar' +export { default as Filters } from './Filters' +export { default as LineDot } from './LineDot' +export { default as LocationMarker } from './LocationMarker' +export { default as TrashboxMarkers } from './TrashboxMarkers' +export { default as WithToken } from './WithToken' +export { default as ClickHandler } from './ClickHandler' +export { default as AuthForm } from './AuthForm' diff --git a/front/src/config.js b/front/src/config.js deleted file mode 100644 index 4555d0a..0000000 --- a/front/src/config.js +++ /dev/null @@ -1,3 +0,0 @@ -const API_URL = "/api" - -export { API_URL } \ No newline at end of file diff --git a/front/src/config.ts b/front/src/config.ts new file mode 100644 index 0000000..2802649 --- /dev/null +++ b/front/src/config.ts @@ -0,0 +1,3 @@ +const API_URL = '/api' + +export { API_URL } diff --git a/front/src/hooks/api/index.js b/front/src/hooks/api/index.ts similarity index 63% rename from front/src/hooks/api/index.js rename to front/src/hooks/api/index.ts index 69169bc..9023aee 100644 --- a/front/src/hooks/api/index.js +++ b/front/src/hooks/api/index.ts @@ -1,5 +1,6 @@ -export { default as useHomeAnnouncementList } from './useHomeAnnouncementList' +export { default as useAnnouncements } from './useAnnouncements' export { default as useBook } from './useBook' export { default as useAuth } from './useAuth' export { default as useTrashboxes } from './useTrashboxes' export { default as useAddAnnouncement } from './useAddAnnouncement' +export { default as useOsmAddresses } from './useOsmAddress' diff --git a/front/src/hooks/api/useAddAnnouncement.js b/front/src/hooks/api/useAddAnnouncement.js deleted file mode 100644 index 0b7f9d4..0000000 --- a/front/src/hooks/api/useAddAnnouncement.js +++ /dev/null @@ -1,54 +0,0 @@ -import { useEffect, useRef, useState } from "react" -import { API_URL } from "../../config" - -const useAddAnnouncement = () => { - const [status, setStatus] = useState("Опубликовать") - - const timerIdRef = useRef() - const abortControllerRef = useRef() - - const doAdd = async (formData) => { - if (status === "Загрузка") { - abortControllerRef.current?.abort() - setStatus("Отменено") - timerIdRef.current = setTimeout(() => setStatus("Опубликовать"), 3000) - return - } - - setStatus("Загрузка") - - const abortController = new AbortController() - abortControllerRef.current = abortController - - try { - const res = await fetch(API_URL + "/announcement", { - method: 'PUT', - body: formData, - signal: abortController.signal - }) - - const data = await res.json() - - if (!data.Answer) { - throw new Error("Не удалось опубликовать объявление") - } - setStatus("Опубликовано") - - } catch (err) { - setStatus(err.message ?? err) - timerIdRef.current = setTimeout(() => setStatus("Опубликовать"), 10000) - } - } - - useEffect(() => { - const abortController = abortControllerRef.current - return () => { - clearTimeout(timerIdRef.current) - abortController?.abort() - } - }) - - return {doAdd, status} -} - -export default useAddAnnouncement \ No newline at end of file diff --git a/front/src/hooks/api/useAddAnnouncement.ts b/front/src/hooks/api/useAddAnnouncement.ts new file mode 100644 index 0000000..fb610aa --- /dev/null +++ b/front/src/hooks/api/useAddAnnouncement.ts @@ -0,0 +1,79 @@ +import { useEffect, useRef, useState } from 'react' + +import { API_URL } from '../../config' +import { isLiteralUnion } from '../../utils/types' +import { handleHTTPErrors } from '../../utils' + +const addErrors = ['Не удалось опубликовать объявление', 'Неверный ответ от сервера', 'Неизвестная ошибка'] as const +type AddError = typeof addErrors[number] + +const isAddError = (obj: unknown): obj is AddError => ( + isLiteralUnion(obj, addErrors) +) + +const buttonStates = ['Опубликовать', 'Загрузка...', 'Опубликовано', 'Отменено'] as const +type ButtonState = typeof buttonStates[number] | AddError + +type AddResponse = { + Answer: boolean +} + +const isAddResponse = (obj: unknown): obj is AddResponse => ( + typeof obj === 'object' && obj !== null && typeof Reflect.get(obj, 'Answer') === 'boolean' +) + +const useAddAnnouncement = () => { + const [status, setStatus] = useState('Опубликовать') + + const timerIdRef = useRef() + const abortControllerRef = useRef() + + const doAdd = async (formData: FormData) => { + if (status === 'Загрузка...') { + abortControllerRef.current?.abort() + setStatus('Отменено') + timerIdRef.current = setTimeout(() => setStatus('Опубликовать'), 3000) + return + } + + setStatus('Загрузка...') + + const abortController = new AbortController() + abortControllerRef.current = abortController + + try { + const res = await fetch(API_URL + '/announcement', { + method: 'PUT', + body: formData, + signal: abortController.signal + }) + + handleHTTPErrors(res) + + const data: unknown = await res.json() + + if (!isAddResponse(data)) throw new Error('Неверный ответ от сервера') + + if (!data.Answer) { + throw new Error('Не удалось опубликовать объявление') + } + setStatus('Опубликовано') + + } catch (err) { + setStatus(isAddError(err) ? err : 'Неизвестная ошибка') + timerIdRef.current = setTimeout(() => setStatus('Опубликовать'), 10000) + } + } + + useEffect(() => { + const abortController = abortControllerRef.current + return () => { + clearTimeout(timerIdRef.current) + abortController?.abort() + } + }) + + return { doAdd, status } +} + +export default useAddAnnouncement diff --git a/front/src/hooks/api/useAnnouncements.ts b/front/src/hooks/api/useAnnouncements.ts new file mode 100644 index 0000000..1e8dbc2 --- /dev/null +++ b/front/src/hooks/api/useAnnouncements.ts @@ -0,0 +1,18 @@ +import { useFetch } from '../' +import { FiltersType } from '../../utils/filters' +import { composeAnnouncementsURL, initialAnnouncements, processAnnouncements } from '../../api/announcements' + +import { isAnnouncementsResponse } from '../../api/announcements/types' + +const useAnnouncements = (filters: FiltersType) => ( + useFetch( + composeAnnouncementsURL(filters), + 'GET', + false, + isAnnouncementsResponse, + processAnnouncements, + initialAnnouncements + ) +) + +export default useAnnouncements diff --git a/front/src/hooks/api/useAuth.js b/front/src/hooks/api/useAuth.js deleted file mode 100644 index 673fbe8..0000000 --- a/front/src/hooks/api/useAuth.js +++ /dev/null @@ -1,61 +0,0 @@ -import { useState } from "react" -import { API_URL } from "../../config" - -function useAuth() { - const [loading, setLoading] = useState(false) - const [error, setError] = useState('') - - const doAuth = async (data, newAccount) => { - setLoading(true) - - if (newAccount) { - try { - const res = await fetch(API_URL + "/signup", { - method: "POST", - body: data, - headers: { - 'Content-Type': 'application/json' - } - }) - const signupData = await res.json() - - if (signupData.Success === false) { - throw new Error(signupData.Message) - } - } catch (err) { - setError(err.message) - setLoading(false) - return null - } - } - - try { - const res = fetch(API_URL + '/auth/token' + new URLSearchParams({ - username: data.email, - password: data.password - }), { - method: "POST", - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) - - const loginData = await res.json() - - const token = loginData.access_token - - setError('') - setLoading(false) - - return token - } catch (err) { - setError(err.message) - setLoading(false) - return null - } - } - - return { doAuth, loading, error } -} - -export default useAuth \ No newline at end of file diff --git a/front/src/hooks/api/useAuth.ts b/front/src/hooks/api/useAuth.ts new file mode 100644 index 0000000..afcebd7 --- /dev/null +++ b/front/src/hooks/api/useAuth.ts @@ -0,0 +1,117 @@ +import { useState } from 'react' + +import { API_URL } from '../../config' +import { isConst, isObject } from '../../utils/types' +import { handleHTTPErrors } from '../../utils' + +interface AuthData { + email: string, + password: string, +} + +// interface LoginData extends AuthData { } + +// interface SignUpData extends AuthData { +// name: string, +// surname: string +// } + +type SignUpResponse = { + Success: true +} | { + Success: false, + Message: string +} + +const isSignUpResponse = (obj: unknown): obj is SignUpResponse => ( + isObject(obj, { + 'Success': isConst(true) + }) || + isObject(obj, { + 'Success': isConst(false), + 'Message': 'string' + }) +) + +interface LogInResponse { + access_token: string, + token_type: 'bearer' +} + +const isLogInResponse = (obj: unknown): obj is LogInResponse => ( + isObject(obj, { + 'access_token': 'string', + 'token_type': isConst('bearer') + }) +) + +function useAuth() { + const [loading, setLoading] = useState(false) + const [error, setError] = useState('') + + async function doAuth(data: AuthData, newAccount: boolean) { + setLoading(true) + + if (newAccount) { + try { + const res = await fetch(API_URL + '/signup', { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json' + } + }) + + handleHTTPErrors(res) + + const signupData: unknown = await res.json() + + if (!isSignUpResponse(signupData)) { + throw new Error('Malformed server response') + } + + if (signupData.Success === false) { + throw new Error(signupData.Message) + } + + } catch (err) { + setError(err instanceof Error ? err.message : err as string) + setLoading(false) + return null + } + } + + try { + const res = await fetch(API_URL + '/auth/token' + new URLSearchParams({ + username: data.email, + password: data.password + }).toString(), { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }) + + const logInData: unknown = await res.json() + + if (!isLogInResponse(logInData)) { + throw new Error('Malformed server response') + } + + const token = logInData.access_token + + setError('') + setLoading(false) + + return token + } catch (err) { + setError(err instanceof Error ? err.message : err as string) + setLoading(false) + return null + } + } + + return { doAuth, loading, error } +} + +export default useAuth diff --git a/front/src/hooks/api/useBook.js b/front/src/hooks/api/useBook.js deleted file mode 100644 index 37c38df..0000000 --- a/front/src/hooks/api/useBook.js +++ /dev/null @@ -1,42 +0,0 @@ -import { useState } from "react" -import { useNavigate } from "react-router-dom" - -import { getToken } from "../../utils/auth" -import { API_URL } from "../../config" - -function useBook(id) { - const navigate = useNavigate() - - const [status, setStatus] = useState('') - - const handleBook = () => { - const token = getToken() - - if (token) { - setStatus("Загрузка") - - fetch(API_URL + '/book', { - method: 'POST', - body: JSON.stringify({ - id: id - }), - headers: { - 'Authorization': 'Bearer ' + token, - 'Content-Type': 'application/json' - } - }).then(res => res.json()).then(data => { - if (data.Success === true) { - setStatus('Забронировано') - } else { - setStatus("Ошибка бронирования") - } - }) - } else { - return navigate("/login") - } - } - - return { handleBook, status } -} - -export default useBook diff --git a/front/src/hooks/api/useBook.ts b/front/src/hooks/api/useBook.ts new file mode 100644 index 0000000..71fe6ed --- /dev/null +++ b/front/src/hooks/api/useBook.ts @@ -0,0 +1,74 @@ +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' + +import { getToken } from '../../utils/auth' +import { API_URL } from '../../config' +import { isObject } from '../../utils/types' +import { handleHTTPErrors } from '../../utils' + +type BookResponse = { + Success: boolean +} + +const isBookResponse = (obj: unknown): obj is BookResponse => ( + isObject(obj, { + 'Success': 'boolean' + }) +) + +type BookStatus = '' | 'Загрузка...' | 'Забронировано' | 'Ошибка бронирования' + +function useBook(id: number) { + const navigate = useNavigate() + + const [status, setStatus] = useState('') + + const handleBook = async () => { + const token = getToken() + + if (token) { + setStatus('Загрузка...') + + try { + + const res = await fetch(API_URL + '/book', { + method: 'POST', + body: JSON.stringify({ + id: id + }), + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json' + } + }) + + handleHTTPErrors(res) + + const data: unknown = await res.json() + + if (!isBookResponse(data)) { + throw new Error('Malformed server response') + } + + if (data.Success === true) { + setStatus('Забронировано') + } else { + throw new Error('Server refused to book') + } + } + catch (err) { + setStatus('Ошибка бронирования') + + if (import.meta.env.DEV) { + console.log(err) + } + } + } else { + return navigate('/login') + } + } + + return { handleBook, status } +} + +export default useBook diff --git a/front/src/hooks/api/useFetch.js b/front/src/hooks/api/useFetch.js deleted file mode 100644 index ab1997e..0000000 --- a/front/src/hooks/api/useFetch.js +++ /dev/null @@ -1,57 +0,0 @@ -import { useEffect, useRef, useState } from "react" -import { isAborted } from '../../utils' - -const useFetch = (url, params, initialData) => { - const [data, setData] = useState(initialData) - const [loading, setLoading] = useState(true) - const [error, setError] = useState("") - - const abortControllerRef = useRef(null) - - useEffect(() => { - if (abortControllerRef.current) { - abortControllerRef.current.abort() - } - - const abortController = new AbortController() - abortControllerRef.current = abortController - - fetch(url, { ...params, signal: abortControllerRef.current.signal }) - .then(res => { - if (!res.ok) { - switch (res.status) { - case 401: - throw new Error("Ошибка авторизации") - case 404: - throw new Error("Объект не найден") - default: { - throw new Error("Ошибка ответа от сервера") - } - } - } - - return res.json() - }) - .then(data => { - setData(data) - setLoading(false) - }) - .catch(err => { - if (!isAborted(err)) { - setError("Ошибка сети") - } - - setLoading(false) - - if (import.meta.env.DEV) { - console.log(url, params, err) - } - }) - - return () => abortControllerRef.current.abort() - }, [url, params]) - - return { data, loading, error, abort: abortControllerRef.current?.abort } -} - -export default useFetch diff --git a/front/src/hooks/api/useHomeAnnouncementList.js b/front/src/hooks/api/useHomeAnnouncementList.js deleted file mode 100644 index db1fad0..0000000 --- a/front/src/hooks/api/useHomeAnnouncementList.js +++ /dev/null @@ -1,26 +0,0 @@ -import useFetch from './useFetch' -import { API_URL } from '../../config' -import { removeNull } from '../../utils' - -const initialAnnouncements = { list_of_announcements: [], Success: true } - -const useHomeAnnouncementList = (filters) => { - const { data, loading, error } = useFetch( - API_URL + '/announcements?' + new URLSearchParams(removeNull(filters)), - null, - initialAnnouncements - ) - - const annList = data.list_of_announcements - - const res = annList.map(ann => ({ - ...ann, - lat: ann.latitude, - lng: ann.longtitude, - bestBy: ann.best_by - })) - - return { data: error ? [] : res, loading, error } -} - -export default useHomeAnnouncementList diff --git a/front/src/hooks/api/useOsmAddress.ts b/front/src/hooks/api/useOsmAddress.ts new file mode 100644 index 0000000..2c91a37 --- /dev/null +++ b/front/src/hooks/api/useOsmAddress.ts @@ -0,0 +1,18 @@ +import { LatLng } from 'leaflet' + +import { useFetch } from '../' +import { composeOsmAddressURL, processOsmAddress } from '../../api/osmAddress' +import { isOsmAddressResponse } from '../../api/osmAddress/types' + +const useOsmAddresses = (addressPosition: LatLng) => ( + useFetch( + composeOsmAddressURL(addressPosition), + 'GET', + false, + isOsmAddressResponse, + processOsmAddress, + '' + ) +) + +export default useOsmAddresses diff --git a/front/src/hooks/api/useTrashboxes.js b/front/src/hooks/api/useTrashboxes.js deleted file mode 100644 index 3451a22..0000000 --- a/front/src/hooks/api/useTrashboxes.js +++ /dev/null @@ -1,12 +0,0 @@ -import { API_URL } from "../../config" -import useFetch from "./useFetch" - -const useTrashboxes = (position) => { - return useFetch( - API_URL + "/trashbox?" + new URLSearchParams({ lat: position.lat, lng: position.lng }), - undefined, - [] - ) -} - -export default useTrashboxes \ No newline at end of file diff --git a/front/src/hooks/api/useTrashboxes.ts b/front/src/hooks/api/useTrashboxes.ts new file mode 100644 index 0000000..1804112 --- /dev/null +++ b/front/src/hooks/api/useTrashboxes.ts @@ -0,0 +1,18 @@ +import { LatLng } from 'leaflet' + +import { useFetch } from '../' +import { composeTrashboxURL } from '../../api/trashbox' +import { isTrashboxResponse } from '../../api/trashbox/types' + +const useTrashboxes = (position: LatLng) => ( + useFetch( + composeTrashboxURL(position), + 'GET', + true, + isTrashboxResponse, + (data) => data, + [] + ) +) + +export default useTrashboxes diff --git a/front/src/hooks/index.js b/front/src/hooks/index.js deleted file mode 100644 index 7f558fd..0000000 --- a/front/src/hooks/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import useStoryDimensions from "./useStoryDimensions" - -export { - useStoryDimensions, -} diff --git a/front/src/hooks/index.ts b/front/src/hooks/index.ts new file mode 100644 index 0000000..e1c5fa4 --- /dev/null +++ b/front/src/hooks/index.ts @@ -0,0 +1,3 @@ +export { default as useStoryDimensions } from './useStoryDimensions' +export { default as useSend } from './useSend' +export { default as useFetch } from './useFetch' diff --git a/front/src/hooks/useFetch.ts b/front/src/hooks/useFetch.ts new file mode 100644 index 0000000..41e9ab6 --- /dev/null +++ b/front/src/hooks/useFetch.ts @@ -0,0 +1,75 @@ +import { useEffect, useState } from 'react' + +import { SetState } from '../utils/types' +import useSend from './useSend' + +type UseFetchShared = { + loading: boolean, + abort?: () => void, +} + +type UseFetchSucced = { + error: null, + data: T, +} & UseFetchShared + +type UseFetchErrored = { + error: string, + data: undefined +} & UseFetchShared + +const gotError = (res: UseFetchErrored | UseFetchSucced): res is UseFetchErrored => ( + typeof res.error === 'string' +) + +const fallbackError = (res: UseFetchSucced | UseFetchErrored) => ( + gotError(res) ? res.error : res.data +) + +type UseFetchReturn = ({ + error: null, + data: T +} | { + error: string, + data: undefined +}) & { + loading: boolean, + setData: SetState + abort?: (() => void) +} + +function useFetch( + url: string, + method: RequestInit['method'], + needAuth: boolean, + guardResponse: (data: unknown) => data is R, + processResponse: (data: R) => T, + initialData?: T, + params?: Omit +): UseFetchReturn { + const [data, setData] = useState(initialData) + + const { doSend, loading, error } = useSend(url, method, needAuth, guardResponse, processResponse, params) + + useEffect(() => { + doSend().then( + data => { if (data !== undefined) setData(data) } + ).catch( // must never occur + err => import.meta.env.DEV && console.error('Failed to do fetch request', err) + ) + }, [doSend]) + + return { + ...( + error === null ? ({ + data: data!, error: null + }) : ({ data: undefined, error }) + ), + loading, + setData + } +} + +export default useFetch + +export { gotError, fallbackError } diff --git a/front/src/hooks/useSend.ts b/front/src/hooks/useSend.ts new file mode 100644 index 0000000..8ef4505 --- /dev/null +++ b/front/src/hooks/useSend.ts @@ -0,0 +1,94 @@ +import { useCallback, useEffect, useRef, useState } from 'react' +import { useNavigate } from 'react-router-dom' + +import { getToken } from '../utils/auth' +import { handleHTTPErrors, isAborted } from '../utils' + +function useSend( + url: string, + method: RequestInit['method'], + needAuth: boolean, + guardResponse: (data: unknown) => data is R, + processResponse: (data: R) => T, + defaultParams?: Omit +) { + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + + const navigate = useNavigate() + + const abortControllerRef = useRef() + + useEffect(() => () => abortControllerRef.current?.abort(), []) + + /** Don't use in useEffect. If you need request result, go with useFetch instead */ + const doSend = useCallback(async (urlProps?: Record, params?: Omit) => { + setLoading(true) + + if (abortControllerRef.current) { + abortControllerRef.current.abort() + } + + const abortController = new AbortController() + abortControllerRef.current = abortController + + const headers = new Headers({ + ...defaultParams?.headers, + ...params?.headers + }) + + if (needAuth) { + const token = getToken() + + if (token === null) { + navigate('/login') + + return undefined + } + + headers.append('Auth', `Bearer ${token}`) + } + + try { + const res = await fetch(url + new URLSearchParams(urlProps).toString(), { + method, + ...defaultParams, + ...params, + headers, + signal: abortControllerRef.current.signal, + }) + + handleHTTPErrors(res) + + const data: unknown = await res.json() + + if (!guardResponse(data)) { + throw new Error('Malformed server response') + } + + setLoading(false) + + return processResponse(data) + + } catch (err) { + if (err instanceof Error && !isAborted(err)) { + setError('Ошибка сети') + + if (import.meta.env.DEV) { + console.log(url, params, err) + } + } + + setLoading(false) + + return undefined + } + }, [defaultParams, needAuth, navigate, url, method, guardResponse, processResponse]) + + return { + doSend, loading, error, + abort: abortControllerRef.current?.abort.bind(abortControllerRef.current) + } +} + +export default useSend diff --git a/front/src/hooks/useStoryDimensions.js b/front/src/hooks/useStoryDimensions.ts similarity index 58% rename from front/src/hooks/useStoryDimensions.js rename to front/src/hooks/useStoryDimensions.ts index ef5638e..7d69655 100644 --- a/front/src/hooks/useStoryDimensions.js +++ b/front/src/hooks/useStoryDimensions.ts @@ -1,15 +1,13 @@ import { useState, useEffect } from 'react'; -function getWindowDimensions() { - const { innerWidth: width, innerHeight: height } = window; +const getWindowDimensions = () => ( + { + width: window.innerWidth, + height: window.innerHeight + } +) - return { - width, - height - }; -} - -function useStoryDimensions(maxRatio = 16/9) { +function useStoryDimensions(maxRatio = 16 / 9) { const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions()) useEffect(() => { @@ -17,8 +15,8 @@ function useStoryDimensions(maxRatio = 16/9) { setWindowDimensions(getWindowDimensions()); } - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); }, []); const height = windowDimensions.height - 56 @@ -31,4 +29,4 @@ function useStoryDimensions(maxRatio = 16/9) { } } -export default useStoryDimensions \ No newline at end of file +export default useStoryDimensions diff --git a/front/src/index.css b/front/src/index.css index 3c1a6d7..d85d960 100644 --- a/front/src/index.css +++ b/front/src/index.css @@ -2,4 +2,4 @@ padding: 0; margin: 0; list-style: none; -} \ No newline at end of file +} diff --git a/front/src/main.jsx b/front/src/main.tsx similarity index 51% rename from front/src/main.jsx rename to front/src/main.tsx index 9cdeb69..20632c1 100644 --- a/front/src/main.jsx +++ b/front/src/main.tsx @@ -1,11 +1,11 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import App from './App.jsx' - +import App from './App.tsx' import './index.css' +import 'bootstrap/dist/css/bootstrap.min.css' -ReactDOM.createRoot(document.getElementById('root')).render( +ReactDOM.createRoot(document.getElementById('root')!).render( , diff --git a/front/src/pages/AddPage.jsx b/front/src/pages/AddPage.jsx deleted file mode 100644 index dd14b99..0000000 --- a/front/src/pages/AddPage.jsx +++ /dev/null @@ -1,204 +0,0 @@ -import { useEffect, useState } from "react" -import { Form, Button, Card } from "react-bootstrap" -import { MapContainer, TileLayer } from 'react-leaflet' -import { latLng } from "leaflet" - -import { ClickHandler, LocationMarker, TrashboxMarkers } from "../components" -import { useAddAnnouncement, useTrashboxes } from "../hooks/api" - -import { categoryNames } from "../assets/category" -import { stations, lines } from "../assets/metro" - -function AddPage() { - const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227)) - const [address, setAddress] = useState('') - - const { data: trashboxes, trashboxes_loading, trashboxes_error } = useTrashboxes(addressPosition) - const [selectedTrashbox, setSelectedTrashbox] = useState({ index: -1, category: '' }) - - useEffect(() => { - (async () => { - try { - const res = await fetch(location.protocol + "//nominatim.openstreetmap.org/search?format=json&q=" + address) - - const fetchData = await res.json() - - console.log("f", fetchData) - - } catch (err) { - console.error(err) - } - })() - }, [address]) - - useEffect(() => { - (async () => { - try { - const res = await fetch(location.protocol + "//nominatim.openstreetmap.org/reverse?format=json&accept-language=ru&lat=" + addressPosition.lat + "&lon=" + addressPosition.lng) - - const fetchData = await res.json() - - setAddress(fetchData.display_name) - - } catch (err) { - console.error(err) - } - })() - }, [addressPosition]) - - const { doAdd, status } = useAddAnnouncement() - - const handleSubmit = (event) => { - event.preventDefault() - event.stopPropagation() - - const formData = new FormData(event.target) - - formData.append("latitude", addressPosition.lat) - formData.append("longtitude", addressPosition.lng) - formData.append("address", address) - formData.set("bestBy", new Date(formData.get("bestBy")).getTime()) - - doAdd(formData) - } - - return ( - - -

- - Заголовок объявления - - - - - Категория - - - {Array.from(categoryNames).map( - ([category, categoryName]) => - - )} - - - - - Срок годности - - - - - Адрес выдачи -
- - - - - -
-

Адрес: {address}

-
- - - Описание - - - - - Иллюстрация (фото или видео) - - - - - Станция метро - - - - {Object.entries(stations).map( - ([line, stations]) => - - {Array.from(stations).map(metro => - - )} - - )} - - - - - Пункт сбора мусора -
- {trashboxes_loading - ? ( -
-

Загрузка

-
- ) : ( - trashboxes_error ? ( -

{trashboxes_error}

- ) : ( - - - - - ) - ) - } -
- {selectedTrashbox.index > -1 ? ( -

Выбран пункт сбора мусора на { - trashboxes[selectedTrashbox.index].Address - } с категорией {selectedTrashbox.category}

- ) : ( -

Выберите пунк сбора мусора и категорию

- )} -
- - -
- - - ) -} - -export default AddPage diff --git a/front/src/pages/AddPage.tsx b/front/src/pages/AddPage.tsx new file mode 100644 index 0000000..a58de78 --- /dev/null +++ b/front/src/pages/AddPage.tsx @@ -0,0 +1,207 @@ +import { CSSProperties, FormEventHandler, useEffect, useState } from 'react' +import { Form, Button, Card } from 'react-bootstrap' +import { MapContainer, TileLayer } from 'react-leaflet' +import { latLng } from 'leaflet' + +import { ClickHandler, LocationMarker, TrashboxMarkers } from '../components' +import { useAddAnnouncement, useTrashboxes } from '../hooks/api' +import { handleHTTPErrors } from '../utils' +import { categories, categoryNames } from '../assets/category' +import { stations, lines, lineNames } from '../assets/metro' +import { fallbackError, gotError } from '../hooks/useFetch' +import { useOsmAddresses } from '../hooks/api' + +const styles = { + modal: { + height: 'calc(100vh - 3rem)', + } as CSSProperties, + body: { + overflowY: 'auto', + } as CSSProperties, + map: { + width: '100%', + height: 400, + } as CSSProperties, +} + +function AddPage() { + const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227)) + + const trashboxes = useTrashboxes(addressPosition) + const [selectedTrashbox, setSelectedTrashbox] = useState({ index: -1, category: '' }) + + const address = useOsmAddresses(addressPosition) + + useEffect(() => { + if (!gotError(address)) + void (async () => { + try { + const res = await fetch(location.protocol + '//nominatim.openstreetmap.org/search?format=json&q=' + encodeURIComponent(address.data)) + + handleHTTPErrors(res) + + const fetchData: unknown = await res.json() + + console.log('f', fetchData) + + } catch (err) { + console.error(err) + } + })() + }, [address]) + + const { doAdd, status } = useAddAnnouncement() + + const handleSubmit: FormEventHandler = (event) => { + event.preventDefault() + event.stopPropagation() + + const formData = new FormData(event.currentTarget) + + formData.append('latitude', addressPosition.lat.toString()) + formData.append('longtitude', addressPosition.lng.toString()) + formData.append('address', address.data || '') // if address.error + formData.set('bestBy', new Date((formData.get('bestBy') as number | null) || 0).getTime().toString()) + + void doAdd(formData) + } + + return ( + + +
+ + Заголовок объявления + + + + + Категория + + + {categories.map(category => + + )} + + + + + Срок годности + + + + + Адрес выдачи +
+ + + + + +
+

Адрес: {fallbackError(address)}

+
+ + + Описание + + + + + Иллюстрация (фото или видео) + + + + + Станция метро + + + + {lines.map( + line => + + {Array.from(stations[line]).map(metro => + + )} + + )} + + + + + Пункт сбора мусора +
+ {trashboxes.loading + ? ( +
+

Загрузка...

+
+ ) : ( + gotError(trashboxes) ? ( +

{trashboxes.error}

+ ) : ( + + + + + ) + ) + } +
+ {!gotError(trashboxes) && selectedTrashbox.index > -1 ? ( +

Выбран пункт сбора мусора на { + trashboxes.data[selectedTrashbox.index].Address + } с категорией {selectedTrashbox.category}

+ ) : ( +

Выберите пунк сбора мусора и категорию

+ )} +
+ + +
+
+
+ ) +} + +export default AddPage diff --git a/front/src/pages/HomePage.jsx b/front/src/pages/HomePage.tsx similarity index 53% rename from front/src/pages/HomePage.jsx rename to front/src/pages/HomePage.tsx index 0602629..9ac9435 100644 --- a/front/src/pages/HomePage.jsx +++ b/front/src/pages/HomePage.tsx @@ -1,68 +1,80 @@ -import { useEffect, useState } from 'react' +import { CSSProperties, useEffect, useState } from 'react' import Stories from 'react-insta-stories' +import { Story } from 'react-insta-stories/dist/interfaces' import { BottomNavBar, AnnouncementDetails, Filters } from '../components' import { useStoryDimensions } from '../hooks' -import { useHomeAnnouncementList } from '../hooks/api' - -import puffSpinner from '../assets/puff.svg' +import { useAnnouncements } from '../hooks/api' +import { defaultFilters } from '../utils/filters' +import { Announcement } from '../api/announcement/types' import { categoryGraphics } from '../assets/category' -function generateStories(announcements) { +import puffSpinner from '../assets/puff.svg' +import { gotError } from '../hooks/useFetch' + +function generateStories(announcements: Announcement[]): Story[] { return announcements.map(announcement => { return ({ id: announcement.id, - url: announcement.src || categoryGraphics.get(announcement.category), - type: announcement.src?.endsWith("mp4") ? "video" : undefined, - seeMore: ({ close }) => + url: announcement.src || categoryGraphics[announcement.category], + type: announcement.src?.endsWith('mp4') ? 'video' : undefined, + seeMore: ({ close }: { close: () => void }) => }) }) } -function fallbackGenerateStories(announcementsFetch) { - const stories = generateStories(announcementsFetch.data) - +function fallbackGenerateStories(announcementsFetch: ReturnType) { if (announcementsFetch.loading) return fallbackStory() - if (announcementsFetch.error) + if (gotError(announcementsFetch)) return fallbackStory(announcementsFetch.error, true) + const stories = generateStories(announcementsFetch.data) + if (stories.length === 0) - return fallbackStory("Здесь пока пусто") + return fallbackStory('Здесь пока пусто') return stories } -const fallbackStory = (text, isError = false) => [{ +const fallbackStory = (text = '', isError = false): Story[] => [{ content: ({ action }) => { // eslint-disable-next-line react-hooks/rules-of-hooks useEffect(() => { action('pause') }, [action]) return ( -
+
{text || }
) }, - header: { heading: text } }] -const defaultFilters = { userId: null, category: null, metro: null, bookedBy: null } +const styles = { + container: { + display: 'flex', + justifyContent: 'center', + backgroundColor: 'rgb(17, 17, 17)', + } as CSSProperties, + center: { + margin: 'auto' + } as CSSProperties, +} function HomePage() { - const { height, width } = useStoryDimensions(16 / 10) + const { height, width } = useStoryDimensions(16 / 9) const [filterShown, setFilterShown] = useState(false) const [filter, setFilter] = useState(defaultFilters) - const announcementsFetch = useHomeAnnouncementList(filter) + const announcementsFetch = useAnnouncements(filter) const stories = fallbackGenerateStories(announcementsFetch) return (<> -
+
(event) => { - event.preventDefault(); - event.stopPropagation(); - - const formData = new FormData(event.currentTarget) - - const data = { - email: formData.get('email'), - name: newAccount ? formData.get('name') : undefined, - password: formData.get('password') - } - - const token = "a" // doAuth(data, newAccount) - - setToken(token) - - navigate("/") - } - - return ( - - - - -
- - Почта - - - - - Имя - - - - - Фамилия - - - - - Пароль - - - - - - - - -
-
- -
- - Почта - - - - - Пароль - - - - -
-
-
-
-
- ) -} - -export default LoginPage \ No newline at end of file diff --git a/front/src/pages/LoginPage.tsx b/front/src/pages/LoginPage.tsx new file mode 100644 index 0000000..fd1a8cb --- /dev/null +++ b/front/src/pages/LoginPage.tsx @@ -0,0 +1,51 @@ +import { FormEventHandler } from 'react' +import { Card, Tabs, Tab } from 'react-bootstrap' +import { useNavigate } from 'react-router-dom'; + +import { useAuth } from '../hooks/api'; +import { setToken } from '../utils/auth'; +import { AuthForm } from '../components'; + +function LoginPage() { + const navigate = useNavigate() + + const { doAuth, loading, error } = useAuth() + + const handleAuth = (newAccount: boolean): FormEventHandler => async (event) => { + event.preventDefault(); + event.stopPropagation(); + + const formData = new FormData(event.currentTarget) + + const data = { + email: formData.get('email') as string, + name: newAccount ? formData.get('name') as string : undefined, + surname: newAccount ? formData.get('surname') as string : undefined, + password: formData.get('password') as string + } + + const token = import.meta.env.PROD ? await doAuth(data, newAccount) : 'a' + + if (token) { + setToken(token) + navigate(-1 - Number(import.meta.env.DEV)) + } + } + + return ( + + + + + + + + + + + + + ) +} + +export default LoginPage diff --git a/front/src/pages/UserPage.jsx b/front/src/pages/UserPage.jsx deleted file mode 100644 index 9252205..0000000 --- a/front/src/pages/UserPage.jsx +++ /dev/null @@ -1,7 +0,0 @@ -function UserPage() { - /* TODO */ - - return <> -} - -export default UserPage diff --git a/front/src/pages/UserPage.tsx b/front/src/pages/UserPage.tsx new file mode 100644 index 0000000..6c90c41 --- /dev/null +++ b/front/src/pages/UserPage.tsx @@ -0,0 +1,9 @@ +import { Link } from 'react-router-dom' + +function UserPage() { + /* TODO */ + + return

For Yet Go Home

+} + +export default UserPage diff --git a/front/src/pages/index.js b/front/src/pages/index.ts similarity index 100% rename from front/src/pages/index.js rename to front/src/pages/index.ts diff --git a/front/src/utils/auth.js b/front/src/utils/auth.js deleted file mode 100644 index 88d8dcf..0000000 --- a/front/src/utils/auth.js +++ /dev/null @@ -1,13 +0,0 @@ -const getToken = () => { - const token = localStorage.getItem("Token") - - /* check expirity */ - - return token -} - -const setToken = (token) => { - localStorage.setItem("Token", token) -} - -export { getToken, setToken } \ No newline at end of file diff --git a/front/src/utils/auth.ts b/front/src/utils/auth.ts new file mode 100644 index 0000000..a380ee4 --- /dev/null +++ b/front/src/utils/auth.ts @@ -0,0 +1,13 @@ +const getToken = () => { + const token = localStorage.getItem('Token') + + /* check expirity */ + + return token +} + +function setToken(token: string) { + localStorage.setItem('Token', token) +} + +export { getToken, setToken } diff --git a/front/src/utils/filters.ts b/front/src/utils/filters.ts new file mode 100644 index 0000000..30a821f --- /dev/null +++ b/front/src/utils/filters.ts @@ -0,0 +1,19 @@ +import { Announcement } from '../api/announcement/types' + +const filterNames = ['userId', 'category', 'metro', 'bookedBy'] as const +type FilterNames = typeof filterNames[number] + +type FiltersType = Partial> + +const defaultFilters: FiltersType = { userId: undefined, category: undefined, metro: undefined, bookedBy: undefined } + +const URLEncodeFilters = (filters: FiltersType) => ( + Object.fromEntries( + filterNames.map( + fName => [fName, filters[fName]?.toString()] + ).filter((p): p is [string, string] => typeof p[1] !== 'undefined') + ) +) + +export type { FilterNames, FiltersType } +export { defaultFilters, filterNames, URLEncodeFilters } diff --git a/front/src/utils/index.js b/front/src/utils/index.js deleted file mode 100644 index f24284b..0000000 --- a/front/src/utils/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const removeNull = (obj) => Object.fromEntries( - Object.entries(obj) - .filter(([_, value]) => value != null) - .map(([key, value]) => [ - key, - value === Object(value) ? removeNull(value) : value, - ]), -) - -const isAborted = (err) => err.name == 'AbortError' - -export { removeNull, isAborted } \ No newline at end of file diff --git a/front/src/utils/index.ts b/front/src/utils/index.ts new file mode 100644 index 0000000..e173552 --- /dev/null +++ b/front/src/utils/index.ts @@ -0,0 +1,19 @@ +const isAborted = (err: Error) => ( + err.name === 'AbortError' +) + +function handleHTTPErrors(res: Response) { + if (!res.ok) { + switch (res.status) { + case 401: + throw new Error('Ошибка авторизации') + case 404: + throw new Error('Объект не найден') + default: { + throw new Error('Ошибка ответа от сервера') + } + } + } +} + +export { isAborted, handleHTTPErrors } diff --git a/front/src/utils/markerIcons.ts b/front/src/utils/markerIcons.ts new file mode 100644 index 0000000..eb3a4ce --- /dev/null +++ b/front/src/utils/markerIcons.ts @@ -0,0 +1,20 @@ +import L from 'leaflet' + +import itemMarker from '../assets/itemMarker.png' +import trashMarker from '../assets/trashMarker.png' + +const iconItem = new L.Icon({ + iconUrl: itemMarker, + iconRetinaUrl: itemMarker, + popupAnchor: [0, 0], + iconSize: [41, 41], +}) + +const iconTrash = new L.Icon({ + iconUrl: trashMarker, + iconRetinaUrl: trashMarker, + popupAnchor: [0, 0], + iconSize: [34, 41], +}) + +export { iconItem, iconTrash } diff --git a/front/src/utils/metro.js b/front/src/utils/metro.js deleted file mode 100644 index dce5a72..0000000 --- a/front/src/utils/metro.js +++ /dev/null @@ -1,7 +0,0 @@ -import { stations } from "../assets/metro" - -function lineByName(name) { - return Object.keys(stations).find(line => stations[line].has(name)) -} - -export { lineByName } diff --git a/front/src/utils/types.ts b/front/src/utils/types.ts new file mode 100644 index 0000000..4ac3255 --- /dev/null +++ b/front/src/utils/types.ts @@ -0,0 +1,67 @@ +const isRecord = (obj: unknown): obj is Record => ( + typeof obj === 'object' && + !Array.isArray(obj) && + Object.getOwnPropertySymbols(obj).length === 0 && // We don't like symbols as keys here + obj !== null +) + +type Primitive = 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined' + +type PropertyGuard = Primitive | `${Primitive}?` | ((obj: unknown) => boolean) + +type PropertiesGuards = Record< + string | number | symbol, + PropertyGuard> + +const isObject = (obj: unknown, properties: PropertiesGuards): obj is T => ( + typeof obj === 'object' && + obj !== null && + // Does not actually iterate over symbols. Hope, will not need to use them + Object.entries(properties).every(([name, guard]) => { + const param: unknown = Reflect.get(obj, name) + + if (import.meta.env.DEV) { + console.debug('isObject', name, param, guard) + } + + if (typeof guard === 'function') { + return guard(param) + } + + if (guard[guard.length - 1] === '?') + return ( + (param !== undefined && param !== null && typeof param === guard.slice(0, -1)) || + param === undefined || param === null + ) + + return typeof param === guard + }) +) + +const isConst = (val: T) => (obj: unknown): obj is T => ( + obj === val || + ( + typeof obj === 'number' && isNaN(obj) && + typeof val === 'number' && isNaN(val) + ) +) + +const isLiteralUnion = (obj: unknown, list: T): obj is T[number] => ( + typeof obj === 'string' && + list.includes(obj as T[number]) +) + +const isArrayOf = (obj: unknown, itemGuard: ((obj: unknown) => obj is T)): obj is T[] => ( + Array.isArray(obj) && + obj.every(itemGuard) +) + +const isString = (obj: unknown): obj is string => ( + typeof obj === 'string' +) + +type SetState = React.Dispatch> + +export type { SetState } + +export { isRecord, isObject, isConst, isLiteralUnion, isArrayOf, isString } diff --git a/front/src/vite-env.d.ts b/front/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/front/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/front/tsconfig.json b/front/tsconfig.json new file mode 100644 index 0000000..a7fc6fb --- /dev/null +++ b/front/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/front/tsconfig.node.json b/front/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/front/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/front/utils/addFetchApiRoute.sh b/front/utils/addFetchApiRoute.sh new file mode 100644 index 0000000..7576e04 --- /dev/null +++ b/front/utils/addFetchApiRoute.sh @@ -0,0 +1,49 @@ +NAME=${1:-Route} + +NAME=${NAME^} + +mkdir -p src/api/${NAME,} +cat > src/api/${NAME,}/index.ts << EOF +import { API_URL } from '../../config' +import { ${NAME}Response, ${NAME} } from './types' + +const initial${NAME}: ${NAME} = {} + +const compose${NAME}URL = () => ( + API_URL + '/${NAME,}?' +) + +const process${NAME} = (data: ${NAME}Response): ${NAME} => { + return data +} + +export { initial${NAME}, compose${NAME}URL, process${NAME} } +EOF + +cat > src/api/${NAME,}/types.ts << EOF +import { isObject } from '../../utils/types' + +type ${NAME}Response = { + +} + +const is${NAME}Response = (obj: unknown): obj is ${NAME}Response => ( + isObject(obj, { + + }) +) + +type ${NAME} = { + +} + +const is${NAME} = (obj: unknown): obj is ${NAME} => ( + isObject(obj, { + + }) +) + +export type { ${NAME}Response, ${NAME} } + +export { is${NAME}Response, is${NAME} } +EOF diff --git a/front/vite.config.js b/front/vite.config.ts similarity index 100% rename from front/vite.config.js rename to front/vite.config.ts