Major addition. User register, login and update is now functional on frontend, removed UUID5 for UUID4 for users and many more changes
This commit is contained in:
parent
d87c280109
commit
d0690f2f06
2
.gitignore
vendored
2
.gitignore
vendored
@ -293,3 +293,5 @@ cython_debug/
|
|||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
!frontend/src/lib
|
!frontend/src/lib
|
||||||
|
|
||||||
|
frontend/openapi.json
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -8,6 +8,7 @@
|
|||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Frontend settings
|
// #region Frontend settings
|
||||||
"prettier.configPath": "./frontend/.prettierrc" // Prettier config override
|
"prettier.configPath": "./frontend/.prettierrc", // Prettier config override
|
||||||
|
"biome.lspBin": "${workspaceFolder}/frontend"
|
||||||
// #endregion
|
// #endregion
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ from app.api.routes import cart_routes, login_routes, shop, user_routes, utils_r
|
|||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
|
|
||||||
api_router.include_router(cart_routes.router)
|
|
||||||
api_router.include_router(user_routes.router)
|
api_router.include_router(user_routes.router)
|
||||||
api_router.include_router(utils_routes.router)
|
api_router.include_router(utils_routes.router)
|
||||||
api_router.include_router(login_routes.router)
|
api_router.include_router(login_routes.router)
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
from fastapi import APIRouter, HTTPException, status
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.dependencies import CurrentOwnerUser, SessionDep
|
from app.api.dependencies import CurrentOwnerUser, SessionDep
|
||||||
|
from app.crud.shop_crud import create_shop
|
||||||
from app.schemas.shop_schemas import ShopCreate
|
from app.schemas.shop_schemas import ShopCreate
|
||||||
from app.schemas.user_schemas import Token
|
from app.schemas.user_schemas import Token
|
||||||
from app.crud.shop_crud import create_shop
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/shop", tags=["Dashboard"])
|
router = APIRouter(prefix="/shop", tags=["Dashboard"])
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=bool, status_code=201)
|
@router.post("", status_code=201)
|
||||||
def register_new_shop(
|
def register_new_shop(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
current_user: CurrentOwnerUser,
|
current_user: CurrentOwnerUser,
|
||||||
shop_data: ShopCreate
|
shop_data: ShopCreate
|
||||||
) -> Token:
|
) -> bool:
|
||||||
create_shop(session, shop_data, current_user)
|
create_shop(session, shop_data, current_user)
|
||||||
return True
|
return True
|
||||||
|
@ -7,7 +7,7 @@ from sqlalchemy.exc import IntegrityError
|
|||||||
from app.api.dependencies import CurrentOwnerUser, SessionDep
|
from app.api.dependencies import CurrentOwnerUser, SessionDep
|
||||||
from app.crud import user_crud
|
from app.crud import user_crud
|
||||||
from app.database.models.user_model import UserRole
|
from app.database.models.user_model import UserRole
|
||||||
from app.schemas.user_schemas import UserRegister, UserUpdate
|
from app.schemas.user_schemas import UserPublic, UserRegister, UserUpdate
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -21,8 +21,8 @@ router = APIRouter(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("", summary="Get information about currently logged in user")
|
@router.get("", summary="Get information about currently logged in user")
|
||||||
async def get_user(session: SessionDep, current_user: CurrentOwnerUser):
|
async def get_user(current_user: CurrentOwnerUser) -> UserPublic:
|
||||||
pass
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="Register new user", status_code=status.HTTP_201_CREATED, response_model=bool)
|
@router.post("", summary="Register new user", status_code=status.HTTP_201_CREATED, response_model=bool)
|
||||||
@ -40,11 +40,8 @@ async def register(session: SessionDep, user_data: UserRegister):
|
|||||||
column_name = "email"
|
column_name = "email"
|
||||||
|
|
||||||
detail = f"{field_mapping.get(column_name, column_name or 'Entry').capitalize()} already in use"
|
detail = f"{field_mapping.get(column_name, column_name or 'Entry').capitalize()} already in use"
|
||||||
|
logger.warning("%s already exists in the database", column_name.capitalize)
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=detail)
|
||||||
status_code=status.HTTP_409_CONFLICT,
|
|
||||||
detail=detail
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, HTTPException):
|
if isinstance(e, HTTPException):
|
||||||
raise
|
raise
|
||||||
@ -53,10 +50,11 @@ async def register(session: SessionDep, user_data: UserRegister):
|
|||||||
|
|
||||||
|
|
||||||
@router.delete("", summary="Delete user")
|
@router.delete("", summary="Delete user")
|
||||||
async def delete_user():
|
async def delete_user() -> bool:
|
||||||
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="delete_user() not implemented")
|
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="delete_user() not implemented")
|
||||||
|
|
||||||
|
|
||||||
@router.put("", summary="Update user details")
|
@router.put("", summary="Update user details")
|
||||||
async def update_user(data: UserUpdate):
|
async def update_user(session: SessionDep, data: UserUpdate, current_user: CurrentOwnerUser) -> bool:
|
||||||
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="update_user() not implemented")
|
user_crud.update_user(session, data, current_user)
|
||||||
|
return True
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import UUID, uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
@ -7,12 +8,19 @@ from app.database.models.shop_model import Shop, ShopStatus
|
|||||||
from app.database.models.user_model import User
|
from app.database.models.user_model import User
|
||||||
from app.schemas.shop_schemas import ShopCreate
|
from app.schemas.shop_schemas import ShopCreate
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def get_shop_id_from_uuid(session: Session, shop_id: int) -> Optional[UUID]:
|
def get_shop_by_id(session: Session, shop_id: int) -> Optional[Shop]:
|
||||||
|
logger.debug("Getting shop by shop_id - %d", id)
|
||||||
stmt = select(Shop).where(Shop.id == shop_id)
|
stmt = select(Shop).where(Shop.id == shop_id)
|
||||||
db_shop = session.exec(stmt).one_or_none()
|
db_shop = session.exec(stmt).one_or_none()
|
||||||
return db_shop
|
return db_shop
|
||||||
|
|
||||||
|
def get_shop_by_uuid(session: Session, shop_uuid: str) -> Optional[Shop]:
|
||||||
|
logger.debug("Getting shop by UUID - %s" , shop_uuid)
|
||||||
|
stmt = select(Shop).where(Shop.uuid == shop_uuid)
|
||||||
|
db_shop = session.exec(stmt).one_or_none()
|
||||||
|
return db_shop
|
||||||
|
|
||||||
def create_shop(session: Session, shop_data: ShopCreate, creator: User) -> None:
|
def create_shop(session: Session, shop_data: ShopCreate, creator: User) -> None:
|
||||||
shop_uuid = uuid4()
|
shop_uuid = uuid4()
|
||||||
|
@ -1,37 +1,32 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import UUID
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
from sqlmodel import Session, select
|
from fastapi import HTTPException, status
|
||||||
|
from sqlmodel import Session, select, and_
|
||||||
|
|
||||||
from app.core.security import get_password_hash, verify_password
|
from app.core.security import get_password_hash, verify_password
|
||||||
from app.crud.shop_crud import get_shop_id_from_uuid
|
from app.crud.shop_crud import get_shop_by_uuid
|
||||||
from app.database.models.user_model import User, UserRole
|
from app.database.models.user_model import User, UserRole
|
||||||
from app.schemas.user_schemas import UserRegister
|
from app.schemas.user_schemas import UserRegister, UserUpdate
|
||||||
from app.utils.models import generate_user_uuid5
|
from app.utils.models import generate_user_uuid5
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_user_by_generated_uuid(session: Session, email: str, shop_uuid: Optional[UUID]) -> Optional[User]:
|
|
||||||
logger.debug("Getting shop id by UUID - %s", shop_uuid)
|
|
||||||
shop_id = get_shop_id_from_uuid(session, shop_uuid)
|
|
||||||
logger.debug("Generating user UUID5")
|
|
||||||
user_uuid = generate_user_uuid5(email, shop_id)
|
|
||||||
stmt = select(User).where(User.uuid == user_uuid)
|
|
||||||
logger.debug("Executing select query")
|
|
||||||
db_user = session.exec(stmt).one_or_none()
|
|
||||||
return db_user
|
|
||||||
|
|
||||||
def create_user(session: Session, user_register: UserRegister, shop_uuid: Optional[UUID], user_role: UserRole):
|
def create_user(session: Session, user_register: UserRegister, shop_uuid: Optional[UUID], user_role: UserRole):
|
||||||
logger.debug("Getting shop id by UUID - %s", shop_uuid)
|
if shop_uuid:
|
||||||
shop_id = get_shop_id_from_uuid(session, shop_uuid)
|
logger.debug("Fetching shop by UUID")
|
||||||
logger.debug("Generating user UUID5")
|
shop_id = get_shop_by_uuid(session, shop_uuid).id
|
||||||
user_uuid = generate_user_uuid5(user_register.email, shop_id)
|
else:
|
||||||
|
logger.debug("No shop UUID provided -> Owner account is being created")
|
||||||
|
shop_id = None
|
||||||
|
|
||||||
logger.debug("Hashing password")
|
logger.debug("Hashing password")
|
||||||
hashed_password = get_password_hash(user_register.password)
|
hashed_password = get_password_hash(user_register.password)
|
||||||
|
|
||||||
new_user = User(
|
new_user = User(
|
||||||
uuid=user_uuid,
|
uuid=uuid4(),
|
||||||
shop_id=shop_id,
|
shop_id=shop_id,
|
||||||
email=user_register.email,
|
email=user_register.email,
|
||||||
username=user_register.username,
|
username=user_register.username,
|
||||||
@ -39,20 +34,50 @@ def create_user(session: Session, user_register: UserRegister, shop_uuid: Option
|
|||||||
user_role=user_role,
|
user_role=user_role,
|
||||||
password=hashed_password
|
password=hashed_password
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("Inserting new user")
|
logger.debug("Inserting new user")
|
||||||
session.add(new_user)
|
session.add(new_user)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def update_user(session: Session, user_update: UserUpdate, current_user: User):
|
||||||
|
|
||||||
|
current_user.email = user_update.email
|
||||||
|
current_user.username = user_update.username
|
||||||
|
current_user.phone_number = user_update.phone_number
|
||||||
|
current_user.first_name = user_update.first_name
|
||||||
|
current_user.last_name = user_update.last_name
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_by_uuid(session: Session, email: str, shop_uuid: Optional[UUID]) -> Optional[User]:
|
||||||
|
if shop_uuid:
|
||||||
|
shop_id = get_shop_by_uuid(session, shop_uuid).id
|
||||||
|
else:
|
||||||
|
shop_id = None
|
||||||
|
|
||||||
|
stmt = select(User).where(and_(
|
||||||
|
User.email == email,
|
||||||
|
User.shop_id == shop_id
|
||||||
|
))
|
||||||
|
logger.debug("Executing select query")
|
||||||
|
db_user = session.exec(stmt).one_or_none()
|
||||||
|
return db_user
|
||||||
|
|
||||||
|
|
||||||
def authenticate(session: Session, email: str, password: str, shop_uuid: Optional[int]) -> Optional[User]:
|
def authenticate(session: Session, email: str, password: str, shop_uuid: Optional[int]) -> Optional[User]:
|
||||||
logger.debug("Getting shop id by UUID - %s", shop_uuid)
|
if shop_uuid:
|
||||||
shop_id = get_shop_id_from_uuid(session, shop_uuid)
|
shop_id = get_shop_by_uuid(session, shop_uuid).id
|
||||||
|
else:
|
||||||
|
shop_id = None
|
||||||
|
|
||||||
logger.debug("Fetching user from db by email - %s", email)
|
logger.debug("Fetching user from db by email - %s", email)
|
||||||
db_user = get_user_by_generated_uuid(session, email, shop_id)
|
db_user = get_user_by_uuid(session, email, shop_id)
|
||||||
if db_user is None:
|
if db_user is None:
|
||||||
logger.warn("Didn't find User with email=%s for shop=%s", email, shop_uuid)
|
logger.warning("Didn't find User with email=%s for shop=%s", email, shop_uuid)
|
||||||
return None
|
return None
|
||||||
if not verify_password(plain_password=password, hashed_password=db_user.password):
|
if not verify_password(plain_password=password, hashed_password=db_user.password):
|
||||||
logger.warn("Found user with email=%s for shop=%s", email, shop_uuid)
|
logger.warning("Found user with email=%s for shop=%s", email, shop_uuid)
|
||||||
return None
|
return None
|
||||||
return db_user
|
return db_user
|
||||||
|
@ -4,7 +4,7 @@ from typing import Optional
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from sqlalchemy import Column
|
from sqlalchemy import Column
|
||||||
from sqlmodel import Enum, Field, Relationship, SQLModel
|
from sqlmodel import Enum, Field, Relationship, SQLModel, UniqueConstraint
|
||||||
|
|
||||||
|
|
||||||
class UserRole(PyEnum):
|
class UserRole(PyEnum):
|
||||||
@ -17,6 +17,9 @@ class UserRole(PyEnum):
|
|||||||
|
|
||||||
class User(SQLModel, table=True):
|
class User(SQLModel, table=True):
|
||||||
__tablename__ = "user"
|
__tablename__ = "user"
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint("email", "shop_id", name="uix_email_shop_id"),
|
||||||
|
)
|
||||||
|
|
||||||
id: int = Field(primary_key=True)
|
id: int = Field(primary_key=True)
|
||||||
uuid: UUID = Field(nullable=False, unique=True)
|
uuid: UUID = Field(nullable=False, unique=True)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
from typing import Optional
|
import uuid
|
||||||
|
|
||||||
from pydantic import BaseModel, EmailStr, Field, model_validator
|
from pydantic import BaseModel, EmailStr, Field, model_validator
|
||||||
|
|
||||||
@ -7,9 +7,13 @@ from pydantic import BaseModel, EmailStr, Field, model_validator
|
|||||||
class UserRegister(BaseModel):
|
class UserRegister(BaseModel):
|
||||||
username: str = Field(min_length=3, max_length=64)
|
username: str = Field(min_length=3, max_length=64)
|
||||||
email: EmailStr = Field()
|
email: EmailStr = Field()
|
||||||
phone_number: str = Field(min_length=2, max_length=16, pattern=r'^\+[1-9]\d{1,14}$')
|
phone_number: str = Field(min_length=2, max_length=16, pattern=r"^\+[1-9]\d{1,14}$")
|
||||||
password: str = Field(min_length=8, max_length=128, examples=["Abc123#!"],
|
password: str = Field(
|
||||||
description="Password must conform to this regex: \n```\n^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$\n```")
|
min_length=8,
|
||||||
|
max_length=128,
|
||||||
|
examples=["Abc123#!"],
|
||||||
|
description="Password must conform to this regex: \n```\n^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$\n```",
|
||||||
|
)
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_using_regex(self):
|
def validate_using_regex(self):
|
||||||
@ -17,28 +21,28 @@ class UserRegister(BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __validate_password(self):
|
def __validate_password(self):
|
||||||
password_regex = r"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$"
|
password_regex = r"^(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])(?=\D*\d)(?=[^#?!@$ %^&*-]*[#?!@$ %^&*-]).{8,128}$"
|
||||||
|
|
||||||
if not re.match(password_regex, self.password):
|
if not re.match(password_regex, self.password):
|
||||||
raise ValueError("Password is too weak")
|
raise ValueError("Password is too weak")
|
||||||
|
|
||||||
|
|
||||||
class UserUpdate(BaseModel):
|
class UserUpdate(BaseModel):
|
||||||
username: Optional[str] = Field(None, min_length=3, max_length=64)
|
email: EmailStr | None = Field()
|
||||||
first_name: Optional[str] = Field(None, max_length=64)
|
phone_number: str | None = Field(min_length=2, max_length=16, pattern=r"^\+[1-9]\d{1,14}$")
|
||||||
last_name: Optional[str] = Field(None, max_length=64)
|
username: str | None = Field(min_length=3, max_length=64)
|
||||||
email: Optional[EmailStr] = Field(None)
|
first_name: str | None = Field(None, max_length=64)
|
||||||
phone_number: Optional[str] = Field(None, min_length=2, max_length=16, pattern=r'^\+[1-9]\d{1,14}$')
|
last_name: str | None = Field(None, max_length=64)
|
||||||
password: Optional[str] = Field(None, min_length=8, max_length=128, examples=["Abc123#!"],
|
|
||||||
description="Password must conform to this regex: \n```\n^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$\n```")
|
|
||||||
|
|
||||||
@model_validator(mode="after")
|
|
||||||
def validate_using_regex(self):
|
|
||||||
self.__validate_password()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __validate_password(self):
|
class UserPublic(BaseModel):
|
||||||
password_regex = r"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$"
|
uuid: uuid.UUID
|
||||||
if not re.match(password_regex, self.password):
|
username: str
|
||||||
raise ValueError("Password is too weak")
|
email: str
|
||||||
|
first_name: str | None
|
||||||
|
last_name: str | None
|
||||||
|
phone_number: str
|
||||||
|
|
||||||
|
|
||||||
class Token(BaseModel):
|
class Token(BaseModel):
|
||||||
access_token: str
|
access_token: str
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import uuid5, UUID, NAMESPACE_DNS
|
from uuid import NAMESPACE_DNS, UUID, uuid5
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def generate_user_uuid5(email: str, shop_id: Optional[int]) -> UUID:
|
def generate_user_uuid5(email: str, shop_id: Optional[int]) -> UUID:
|
||||||
unique_string = f"{email}-{shop_id}" if shop_id else email
|
unique_string = f"{email}-{shop_id}" if shop_id else email
|
||||||
return uuid5(NAMESPACE_DNS, unique_string)
|
generated_uuid = uuid5(NAMESPACE_DNS, unique_string)
|
||||||
|
logger.debug("Generated user UUID5 - %s", generated_uuid)
|
||||||
|
return generated_uuid
|
||||||
|
@ -30,7 +30,6 @@ select = [
|
|||||||
ignore = [
|
ignore = [
|
||||||
"E501", # line too long, handled by black
|
"E501", # line too long, handled by black
|
||||||
"B008", # do not perform function calls in argument defaults
|
"B008", # do not perform function calls in argument defaults
|
||||||
"W191", # indentation contains tabs
|
|
||||||
"B904", # Allow raising exceptions without from e, for HTTPException
|
"B904", # Allow raising exceptions without from e, for HTTPException
|
||||||
"UP007",
|
"UP007",
|
||||||
]
|
]
|
||||||
|
0
frontend/.env.example
Normal file
0
frontend/.env.example
Normal file
30
frontend/biome.json
Normal file
30
frontend/biome.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||||
|
"vcs": {
|
||||||
|
"enabled": false,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": false
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": false,
|
||||||
|
"ignore": []
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "tab"
|
||||||
|
},
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
frontend/openapi-ts.config.ts
Normal file
16
frontend/openapi-ts.config.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { defineConfig } from "@hey-api/openapi-ts";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
input: "./openapi.json",
|
||||||
|
output: {
|
||||||
|
path: "./src/client"
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
"legacy/axios",
|
||||||
|
{
|
||||||
|
name: "@hey-api/sdk",
|
||||||
|
asClass: true,
|
||||||
|
operationId: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
1036
frontend/package-lock.json
generated
1036
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@
|
|||||||
"format:check": "prettier --check .",
|
"format:check": "prettier --check .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"knip": "knip",
|
"knip": "knip",
|
||||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client @hey-api/client-axios"
|
"generate-client": "openapi-ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.9.1",
|
"@hookform/resolvers": "^3.9.1",
|
||||||
@ -51,6 +51,7 @@
|
|||||||
"react-day-picker": "^9.6.0",
|
"react-day-picker": "^9.6.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.54.0",
|
"react-hook-form": "^7.54.0",
|
||||||
|
"react-phone-number-input": "^3.4.12",
|
||||||
"recharts": "^2.14.1",
|
"recharts": "^2.14.1",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
21
frontend/src/client/core/ApiError.ts
Normal file
21
frontend/src/client/core/ApiError.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
import type { ApiResult } from './ApiResult';
|
||||||
|
|
||||||
|
export class ApiError extends Error {
|
||||||
|
public readonly url: string;
|
||||||
|
public readonly status: number;
|
||||||
|
public readonly statusText: string;
|
||||||
|
public readonly body: unknown;
|
||||||
|
public readonly request: ApiRequestOptions;
|
||||||
|
|
||||||
|
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
this.name = 'ApiError';
|
||||||
|
this.url = response.url;
|
||||||
|
this.status = response.status;
|
||||||
|
this.statusText = response.statusText;
|
||||||
|
this.body = response.body;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
}
|
21
frontend/src/client/core/ApiRequestOptions.ts
Normal file
21
frontend/src/client/core/ApiRequestOptions.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export type ApiRequestOptions<T = unknown> = {
|
||||||
|
readonly body?: any;
|
||||||
|
readonly cookies?: Record<string, unknown>;
|
||||||
|
readonly errors?: Record<number | string, string>;
|
||||||
|
readonly formData?: Record<string, unknown> | any[] | Blob | File;
|
||||||
|
readonly headers?: Record<string, unknown>;
|
||||||
|
readonly mediaType?: string;
|
||||||
|
readonly method:
|
||||||
|
| 'DELETE'
|
||||||
|
| 'GET'
|
||||||
|
| 'HEAD'
|
||||||
|
| 'OPTIONS'
|
||||||
|
| 'PATCH'
|
||||||
|
| 'POST'
|
||||||
|
| 'PUT';
|
||||||
|
readonly path?: Record<string, unknown>;
|
||||||
|
readonly query?: Record<string, unknown>;
|
||||||
|
readonly responseHeader?: string;
|
||||||
|
readonly responseTransformer?: (data: unknown) => Promise<T>;
|
||||||
|
readonly url: string;
|
||||||
|
};
|
7
frontend/src/client/core/ApiResult.ts
Normal file
7
frontend/src/client/core/ApiResult.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export type ApiResult<TData = any> = {
|
||||||
|
readonly body: TData;
|
||||||
|
readonly ok: boolean;
|
||||||
|
readonly status: number;
|
||||||
|
readonly statusText: string;
|
||||||
|
readonly url: string;
|
||||||
|
};
|
126
frontend/src/client/core/CancelablePromise.ts
Normal file
126
frontend/src/client/core/CancelablePromise.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
export class CancelError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'CancelError';
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isCancelled(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OnCancel {
|
||||||
|
readonly isResolved: boolean;
|
||||||
|
readonly isRejected: boolean;
|
||||||
|
readonly isCancelled: boolean;
|
||||||
|
|
||||||
|
(cancelHandler: () => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CancelablePromise<T> implements Promise<T> {
|
||||||
|
private _isResolved: boolean;
|
||||||
|
private _isRejected: boolean;
|
||||||
|
private _isCancelled: boolean;
|
||||||
|
readonly cancelHandlers: (() => void)[];
|
||||||
|
readonly promise: Promise<T>;
|
||||||
|
private _resolve?: (value: T | PromiseLike<T>) => void;
|
||||||
|
private _reject?: (reason?: unknown) => void;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
executor: (
|
||||||
|
resolve: (value: T | PromiseLike<T>) => void,
|
||||||
|
reject: (reason?: unknown) => void,
|
||||||
|
onCancel: OnCancel
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
|
this._isResolved = false;
|
||||||
|
this._isRejected = false;
|
||||||
|
this._isCancelled = false;
|
||||||
|
this.cancelHandlers = [];
|
||||||
|
this.promise = new Promise<T>((resolve, reject) => {
|
||||||
|
this._resolve = resolve;
|
||||||
|
this._reject = reject;
|
||||||
|
|
||||||
|
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||||
|
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._isResolved = true;
|
||||||
|
if (this._resolve) this._resolve(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReject = (reason?: unknown): void => {
|
||||||
|
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._isRejected = true;
|
||||||
|
if (this._reject) this._reject(reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = (cancelHandler: () => void): void => {
|
||||||
|
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.cancelHandlers.push(cancelHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(onCancel, 'isResolved', {
|
||||||
|
get: (): boolean => this._isResolved,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(onCancel, 'isRejected', {
|
||||||
|
get: (): boolean => this._isRejected,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(onCancel, 'isCancelled', {
|
||||||
|
get: (): boolean => this._isCancelled,
|
||||||
|
});
|
||||||
|
|
||||||
|
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "Cancellable Promise";
|
||||||
|
}
|
||||||
|
|
||||||
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
|
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||||
|
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
|
||||||
|
): Promise<TResult1 | TResult2> {
|
||||||
|
return this.promise.then(onFulfilled, onRejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public catch<TResult = never>(
|
||||||
|
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
|
||||||
|
): Promise<T | TResult> {
|
||||||
|
return this.promise.catch(onRejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||||
|
return this.promise.finally(onFinally);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel(): void {
|
||||||
|
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._isCancelled = true;
|
||||||
|
if (this.cancelHandlers.length) {
|
||||||
|
try {
|
||||||
|
for (const cancelHandler of this.cancelHandlers) {
|
||||||
|
cancelHandler();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Cancellation threw an error', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cancelHandlers.length = 0;
|
||||||
|
if (this._reject) this._reject(new CancelError('Request aborted'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isCancelled(): boolean {
|
||||||
|
return this._isCancelled;
|
||||||
|
}
|
||||||
|
}
|
57
frontend/src/client/core/OpenAPI.ts
Normal file
57
frontend/src/client/core/OpenAPI.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
|
||||||
|
type Headers = Record<string, string>;
|
||||||
|
type Middleware<T> = (value: T) => T | Promise<T>;
|
||||||
|
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
||||||
|
|
||||||
|
export class Interceptors<T> {
|
||||||
|
_fns: Middleware<T>[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._fns = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
eject(fn: Middleware<T>): void {
|
||||||
|
const index = this._fns.indexOf(fn);
|
||||||
|
if (index !== -1) {
|
||||||
|
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use(fn: Middleware<T>): void {
|
||||||
|
this._fns = [...this._fns, fn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OpenAPIConfig = {
|
||||||
|
BASE: string;
|
||||||
|
CREDENTIALS: 'include' | 'omit' | 'same-origin';
|
||||||
|
ENCODE_PATH?: ((path: string) => string) | undefined;
|
||||||
|
HEADERS?: Headers | Resolver<Headers> | undefined;
|
||||||
|
PASSWORD?: string | Resolver<string> | undefined;
|
||||||
|
TOKEN?: string | Resolver<string> | undefined;
|
||||||
|
USERNAME?: string | Resolver<string> | undefined;
|
||||||
|
VERSION: string;
|
||||||
|
WITH_CREDENTIALS: boolean;
|
||||||
|
interceptors: {
|
||||||
|
request: Interceptors<AxiosRequestConfig>;
|
||||||
|
response: Interceptors<AxiosResponse>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OpenAPI: OpenAPIConfig = {
|
||||||
|
BASE: '',
|
||||||
|
CREDENTIALS: 'include',
|
||||||
|
ENCODE_PATH: undefined,
|
||||||
|
HEADERS: undefined,
|
||||||
|
PASSWORD: undefined,
|
||||||
|
TOKEN: undefined,
|
||||||
|
USERNAME: undefined,
|
||||||
|
VERSION: '0.0.1',
|
||||||
|
WITH_CREDENTIALS: false,
|
||||||
|
interceptors: {
|
||||||
|
request: new Interceptors(),
|
||||||
|
response: new Interceptors(),
|
||||||
|
},
|
||||||
|
};
|
347
frontend/src/client/core/request.ts
Normal file
347
frontend/src/client/core/request.ts
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
import { ApiError } from './ApiError';
|
||||||
|
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||||
|
import type { ApiResult } from './ApiResult';
|
||||||
|
import { CancelablePromise } from './CancelablePromise';
|
||||||
|
import type { OnCancel } from './CancelablePromise';
|
||||||
|
import type { OpenAPIConfig } from './OpenAPI';
|
||||||
|
|
||||||
|
export const isString = (value: unknown): value is string => {
|
||||||
|
return typeof value === 'string';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isStringWithValue = (value: unknown): value is string => {
|
||||||
|
return isString(value) && value !== '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isBlob = (value: any): value is Blob => {
|
||||||
|
return value instanceof Blob;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isFormData = (value: unknown): value is FormData => {
|
||||||
|
return value instanceof FormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isSuccess = (status: number): boolean => {
|
||||||
|
return status >= 200 && status < 300;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const base64 = (str: string): string => {
|
||||||
|
try {
|
||||||
|
return btoa(str);
|
||||||
|
} catch (err) {
|
||||||
|
// @ts-ignore
|
||||||
|
return Buffer.from(str).toString('base64');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getQueryString = (params: Record<string, unknown>): string => {
|
||||||
|
const qs: string[] = [];
|
||||||
|
|
||||||
|
const append = (key: string, value: unknown) => {
|
||||||
|
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const encodePair = (key: string, value: unknown) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Date) {
|
||||||
|
append(key, value.toISOString());
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
value.forEach(v => encodePair(key, v));
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v));
|
||||||
|
} else {
|
||||||
|
append(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(params).forEach(([key, value]) => encodePair(key, value));
|
||||||
|
|
||||||
|
return qs.length ? `?${qs.join('&')}` : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
|
||||||
|
const encoder = config.ENCODE_PATH || encodeURI;
|
||||||
|
|
||||||
|
const path = options.url
|
||||||
|
.replace('{api-version}', config.VERSION)
|
||||||
|
.replace(/{(.*?)}/g, (substring: string, group: string) => {
|
||||||
|
if (options.path?.hasOwnProperty(group)) {
|
||||||
|
return encoder(String(options.path[group]));
|
||||||
|
}
|
||||||
|
return substring;
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = config.BASE + path;
|
||||||
|
return options.query ? url + getQueryString(options.query) : url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
|
||||||
|
if (options.formData) {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
const process = (key: string, value: unknown) => {
|
||||||
|
if (isString(value) || isBlob(value)) {
|
||||||
|
formData.append(key, value);
|
||||||
|
} else {
|
||||||
|
formData.append(key, JSON.stringify(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(options.formData)
|
||||||
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
|
.forEach(([key, value]) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach(v => process(key, v));
|
||||||
|
} else {
|
||||||
|
process(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
|
||||||
|
|
||||||
|
export const resolve = async <T>(options: ApiRequestOptions<T>, resolver?: T | Resolver<T>): Promise<T | undefined> => {
|
||||||
|
if (typeof resolver === 'function') {
|
||||||
|
return (resolver as Resolver<T>)(options);
|
||||||
|
}
|
||||||
|
return resolver;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getHeaders = async <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>): Promise<Record<string, string>> => {
|
||||||
|
const [token, username, password, additionalHeaders] = await Promise.all([
|
||||||
|
// @ts-ignore
|
||||||
|
resolve(options, config.TOKEN),
|
||||||
|
// @ts-ignore
|
||||||
|
resolve(options, config.USERNAME),
|
||||||
|
// @ts-ignore
|
||||||
|
resolve(options, config.PASSWORD),
|
||||||
|
// @ts-ignore
|
||||||
|
resolve(options, config.HEADERS),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const headers = Object.entries({
|
||||||
|
Accept: 'application/json',
|
||||||
|
...additionalHeaders,
|
||||||
|
...options.headers,
|
||||||
|
})
|
||||||
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
|
.reduce((headers, [key, value]) => ({
|
||||||
|
...headers,
|
||||||
|
[key]: String(value),
|
||||||
|
}), {} as Record<string, string>);
|
||||||
|
|
||||||
|
if (isStringWithValue(token)) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||||
|
const credentials = base64(`${username}:${password}`);
|
||||||
|
headers['Authorization'] = `Basic ${credentials}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.body !== undefined) {
|
||||||
|
if (options.mediaType) {
|
||||||
|
headers['Content-Type'] = options.mediaType;
|
||||||
|
} else if (isBlob(options.body)) {
|
||||||
|
headers['Content-Type'] = options.body.type || 'application/octet-stream';
|
||||||
|
} else if (isString(options.body)) {
|
||||||
|
headers['Content-Type'] = 'text/plain';
|
||||||
|
} else if (!isFormData(options.body)) {
|
||||||
|
headers['Content-Type'] = 'application/json';
|
||||||
|
}
|
||||||
|
} else if (options.formData !== undefined) {
|
||||||
|
if (options.mediaType) {
|
||||||
|
headers['Content-Type'] = options.mediaType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRequestBody = (options: ApiRequestOptions): unknown => {
|
||||||
|
if (options.body) {
|
||||||
|
return options.body;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendRequest = async <T>(
|
||||||
|
config: OpenAPIConfig,
|
||||||
|
options: ApiRequestOptions<T>,
|
||||||
|
url: string,
|
||||||
|
body: unknown,
|
||||||
|
formData: FormData | undefined,
|
||||||
|
headers: Record<string, string>,
|
||||||
|
onCancel: OnCancel,
|
||||||
|
axiosClient: AxiosInstance
|
||||||
|
): Promise<AxiosResponse<T>> => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
let requestConfig: AxiosRequestConfig = {
|
||||||
|
data: body ?? formData,
|
||||||
|
headers,
|
||||||
|
method: options.method,
|
||||||
|
signal: controller.signal,
|
||||||
|
url,
|
||||||
|
withCredentials: config.WITH_CREDENTIALS,
|
||||||
|
};
|
||||||
|
|
||||||
|
onCancel(() => controller.abort());
|
||||||
|
|
||||||
|
for (const fn of config.interceptors.request._fns) {
|
||||||
|
requestConfig = await fn(requestConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await axiosClient.request(requestConfig);
|
||||||
|
} catch (error) {
|
||||||
|
const axiosError = error as AxiosError<T>;
|
||||||
|
if (axiosError.response) {
|
||||||
|
return axiosError.response;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResponseHeader = (response: AxiosResponse<unknown>, responseHeader?: string): string | undefined => {
|
||||||
|
if (responseHeader) {
|
||||||
|
const content = response.headers[responseHeader];
|
||||||
|
if (isString(content)) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResponseBody = (response: AxiosResponse<unknown>): unknown => {
|
||||||
|
if (response.status !== 204) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
|
||||||
|
const errors: Record<number, string> = {
|
||||||
|
400: 'Bad Request',
|
||||||
|
401: 'Unauthorized',
|
||||||
|
402: 'Payment Required',
|
||||||
|
403: 'Forbidden',
|
||||||
|
404: 'Not Found',
|
||||||
|
405: 'Method Not Allowed',
|
||||||
|
406: 'Not Acceptable',
|
||||||
|
407: 'Proxy Authentication Required',
|
||||||
|
408: 'Request Timeout',
|
||||||
|
409: 'Conflict',
|
||||||
|
410: 'Gone',
|
||||||
|
411: 'Length Required',
|
||||||
|
412: 'Precondition Failed',
|
||||||
|
413: 'Payload Too Large',
|
||||||
|
414: 'URI Too Long',
|
||||||
|
415: 'Unsupported Media Type',
|
||||||
|
416: 'Range Not Satisfiable',
|
||||||
|
417: 'Expectation Failed',
|
||||||
|
418: 'Im a teapot',
|
||||||
|
421: 'Misdirected Request',
|
||||||
|
422: 'Unprocessable Content',
|
||||||
|
423: 'Locked',
|
||||||
|
424: 'Failed Dependency',
|
||||||
|
425: 'Too Early',
|
||||||
|
426: 'Upgrade Required',
|
||||||
|
428: 'Precondition Required',
|
||||||
|
429: 'Too Many Requests',
|
||||||
|
431: 'Request Header Fields Too Large',
|
||||||
|
451: 'Unavailable For Legal Reasons',
|
||||||
|
500: 'Internal Server Error',
|
||||||
|
501: 'Not Implemented',
|
||||||
|
502: 'Bad Gateway',
|
||||||
|
503: 'Service Unavailable',
|
||||||
|
504: 'Gateway Timeout',
|
||||||
|
505: 'HTTP Version Not Supported',
|
||||||
|
506: 'Variant Also Negotiates',
|
||||||
|
507: 'Insufficient Storage',
|
||||||
|
508: 'Loop Detected',
|
||||||
|
510: 'Not Extended',
|
||||||
|
511: 'Network Authentication Required',
|
||||||
|
...options.errors,
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = errors[result.status];
|
||||||
|
if (error) {
|
||||||
|
throw new ApiError(options, result, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
const errorStatus = result.status ?? 'unknown';
|
||||||
|
const errorStatusText = result.statusText ?? 'unknown';
|
||||||
|
const errorBody = (() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(result.body, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
throw new ApiError(options, result,
|
||||||
|
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request method
|
||||||
|
* @param config The OpenAPI configuration object
|
||||||
|
* @param options The request options from the service
|
||||||
|
* @param axiosClient The axios client instance to use
|
||||||
|
* @returns CancelablePromise<T>
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions<T>, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
|
||||||
|
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||||
|
try {
|
||||||
|
const url = getUrl(config, options);
|
||||||
|
const formData = getFormData(options);
|
||||||
|
const body = getRequestBody(options);
|
||||||
|
const headers = await getHeaders(config, options);
|
||||||
|
|
||||||
|
if (!onCancel.isCancelled) {
|
||||||
|
let response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient);
|
||||||
|
|
||||||
|
for (const fn of config.interceptors.response._fns) {
|
||||||
|
response = await fn(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseBody = getResponseBody(response);
|
||||||
|
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||||
|
|
||||||
|
let transformedBody = responseBody;
|
||||||
|
if (options.responseTransformer && isSuccess(response.status)) {
|
||||||
|
transformedBody = await options.responseTransformer(responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: ApiResult = {
|
||||||
|
url,
|
||||||
|
ok: isSuccess(response.status),
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
body: responseHeader ?? transformedBody,
|
||||||
|
};
|
||||||
|
|
||||||
|
catchErrorCodes(options, result);
|
||||||
|
|
||||||
|
resolve(result.body);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
6
frontend/src/client/index.ts
Normal file
6
frontend/src/client/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
export { ApiError } from './core/ApiError';
|
||||||
|
export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
||||||
|
export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI';
|
||||||
|
export * from './sdk.gen';
|
||||||
|
export * from './types.gen';
|
438
frontend/src/client/sdk.gen.ts
Normal file
438
frontend/src/client/sdk.gen.ts
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
import type { CancelablePromise } from './core/CancelablePromise';
|
||||||
|
import { OpenAPI } from './core/OpenAPI';
|
||||||
|
import { request as __request } from './core/request';
|
||||||
|
import type { UserGetUserResponse, UserUpdateUserData, UserUpdateUserResponse, UserRegisterData, UserRegisterResponse, UserDeleteUserResponse, DashboardLoginAccessTokenData, DashboardLoginAccessTokenResponse, DashboardRegisterNewShopData, DashboardRegisterNewShopResponse, ShopLoginAccessTokenData, ShopLoginAccessTokenResponse, ShopDeleteUserData, ShopDeleteUserResponse, ShopLogoutResponse, ShopRegisterData, ShopRegisterResponse, ShopUpdateUserData, ShopUpdateUserResponse, UtilsHealthCheckResponse, UtilsTestDbResponse } from './types.gen';
|
||||||
|
|
||||||
|
export class DashboardService {
|
||||||
|
/**
|
||||||
|
* Get information about currently logged in user
|
||||||
|
* @returns UserPublic Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userGetUser(): CancelablePromise<UserGetUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user details
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userUpdateUser(data: UserUpdateUserData): CancelablePromise<UserUpdateUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/user',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new user
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userRegister(data: UserRegisterData): CancelablePromise<UserRegisterResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/user',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete user
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userDeleteUser(): CancelablePromise<UserDeleteUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login Access Token
|
||||||
|
* OAuth2 compatible token login for the dashboard.
|
||||||
|
*
|
||||||
|
* This endpoint generates an access token required for authenticating future
|
||||||
|
* requests to the dashboard section of the application. The token is valid for
|
||||||
|
* a predefined expiration period.
|
||||||
|
*
|
||||||
|
* - **username**: User's email
|
||||||
|
* - **password**: User's password
|
||||||
|
*
|
||||||
|
* **Note:** This login is restricted to dashboard access only and cannot be
|
||||||
|
* used for tenant accounts access to shops
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.formData
|
||||||
|
* @returns Token Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static dashboardLoginAccessToken(data: DashboardLoginAccessTokenData): CancelablePromise<DashboardLoginAccessTokenResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/login/access-token',
|
||||||
|
formData: data.formData,
|
||||||
|
mediaType: 'application/x-www-form-urlencoded',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register New Shop
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static dashboardRegisterNewShop(data: DashboardRegisterNewShopData): CancelablePromise<DashboardRegisterNewShopResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/shop',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LoginService {
|
||||||
|
/**
|
||||||
|
* Login Access Token
|
||||||
|
* OAuth2 compatible token login for the dashboard.
|
||||||
|
*
|
||||||
|
* This endpoint generates an access token required for authenticating future
|
||||||
|
* requests to the dashboard section of the application. The token is valid for
|
||||||
|
* a predefined expiration period.
|
||||||
|
*
|
||||||
|
* - **username**: User's email
|
||||||
|
* - **password**: User's password
|
||||||
|
*
|
||||||
|
* **Note:** This login is restricted to dashboard access only and cannot be
|
||||||
|
* used for tenant accounts access to shops
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.formData
|
||||||
|
* @returns Token Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static dashboardLoginAccessToken(data: DashboardLoginAccessTokenData): CancelablePromise<DashboardLoginAccessTokenResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/login/access-token',
|
||||||
|
formData: data.formData,
|
||||||
|
mediaType: 'application/x-www-form-urlencoded',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login Access Token
|
||||||
|
* OAuth2 compatible token login, get an access token for future requests
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.formData
|
||||||
|
* @returns Token Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopLoginAccessToken(data: ShopLoginAccessTokenData): CancelablePromise<ShopLoginAccessTokenResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/shop/{shop_uuid}/login/access-token',
|
||||||
|
formData: data.formData,
|
||||||
|
mediaType: 'application/x-www-form-urlencoded',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ShopService {
|
||||||
|
/**
|
||||||
|
* Login Access Token
|
||||||
|
* OAuth2 compatible token login, get an access token for future requests
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.formData
|
||||||
|
* @returns Token Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopLoginAccessToken(data: ShopLoginAccessTokenData): CancelablePromise<ShopLoginAccessTokenResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/shop/{shop_uuid}/login/access-token',
|
||||||
|
formData: data.formData,
|
||||||
|
mediaType: 'application/x-www-form-urlencoded',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete user
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.shopUuid
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopDeleteUser(data: ShopDeleteUserData): CancelablePromise<ShopDeleteUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/shop/{shop_uuid}/user/delete',
|
||||||
|
path: {
|
||||||
|
shop_uuid: data.shopUuid
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User logout
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopLogout(): CancelablePromise<ShopLogoutResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/shop/{shop_uuid}/user/logout'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new user
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.shopUuid
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopRegister(data: ShopRegisterData): CancelablePromise<ShopRegisterResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/shop/{shop_uuid}/user/register',
|
||||||
|
path: {
|
||||||
|
shop_uuid: data.shopUuid
|
||||||
|
},
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user details
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopUpdateUser(data: ShopUpdateUserData): CancelablePromise<ShopUpdateUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/shop/{shop_uuid}/user/update',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserService {
|
||||||
|
/**
|
||||||
|
* Get information about currently logged in user
|
||||||
|
* @returns UserPublic Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userGetUser(): CancelablePromise<UserGetUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user details
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userUpdateUser(data: UserUpdateUserData): CancelablePromise<UserUpdateUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/user',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new user
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userRegister(data: UserRegisterData): CancelablePromise<UserRegisterResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/user',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete user
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static userDeleteUser(): CancelablePromise<UserDeleteUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete user
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.shopUuid
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopDeleteUser(data: ShopDeleteUserData): CancelablePromise<ShopDeleteUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/shop/{shop_uuid}/user/delete',
|
||||||
|
path: {
|
||||||
|
shop_uuid: data.shopUuid
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User logout
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopLogout(): CancelablePromise<ShopLogoutResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/shop/{shop_uuid}/user/logout'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new user
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.shopUuid
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopRegister(data: ShopRegisterData): CancelablePromise<ShopRegisterResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/shop/{shop_uuid}/user/register',
|
||||||
|
path: {
|
||||||
|
shop_uuid: data.shopUuid
|
||||||
|
},
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user details
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns unknown Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static shopUpdateUser(data: ShopUpdateUserData): CancelablePromise<ShopUpdateUserResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/shop/{shop_uuid}/user/update',
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: 'Validation Error'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UtilsService {
|
||||||
|
/**
|
||||||
|
* Health Check
|
||||||
|
* Ping the API whether it's alive or not
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static utilsHealthCheck(): CancelablePromise<UtilsHealthCheckResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/utils/health-check/'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Db
|
||||||
|
* Ping database using select 1 to see if connection works
|
||||||
|
* @returns boolean Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public static utilsTestDb(): CancelablePromise<UtilsTestDbResponse> {
|
||||||
|
return __request(OpenAPI, {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/utils/test-db/'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
142
frontend/src/client/types.gen.ts
Normal file
142
frontend/src/client/types.gen.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
export type Body_Dashboard_login_access_token = {
|
||||||
|
grant_type?: (string | null);
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
scope?: string;
|
||||||
|
client_id?: (string | null);
|
||||||
|
client_secret?: (string | null);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Body_Shop_login_access_token = {
|
||||||
|
grant_type?: (string | null);
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
scope?: string;
|
||||||
|
client_id?: (string | null);
|
||||||
|
client_secret?: (string | null);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HTTPValidationError = {
|
||||||
|
detail?: Array<ValidationError>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopAddress = {
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
postal_code: string;
|
||||||
|
country: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopCreate = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
currency: string;
|
||||||
|
contact_email: string;
|
||||||
|
contact_phone_number: string;
|
||||||
|
address: ShopAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Token = {
|
||||||
|
access_token: string;
|
||||||
|
token_type?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserPublic = {
|
||||||
|
uuid: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
first_name: (string | null);
|
||||||
|
last_name: (string | null);
|
||||||
|
phone_number: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserRegister = {
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
phone_number: string;
|
||||||
|
/**
|
||||||
|
* Password must conform to this regex:
|
||||||
|
* ```
|
||||||
|
* ^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,128}$
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserUpdate = {
|
||||||
|
email: (string | null);
|
||||||
|
phone_number: (string | null);
|
||||||
|
username: (string | null);
|
||||||
|
first_name?: (string | null);
|
||||||
|
last_name?: (string | null);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ValidationError = {
|
||||||
|
loc: Array<(string | number)>;
|
||||||
|
msg: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserGetUserResponse = (UserPublic);
|
||||||
|
|
||||||
|
export type UserUpdateUserData = {
|
||||||
|
requestBody: UserUpdate;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserUpdateUserResponse = (boolean);
|
||||||
|
|
||||||
|
export type UserRegisterData = {
|
||||||
|
requestBody: UserRegister;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserRegisterResponse = (boolean);
|
||||||
|
|
||||||
|
export type UserDeleteUserResponse = (boolean);
|
||||||
|
|
||||||
|
export type DashboardLoginAccessTokenData = {
|
||||||
|
formData: Body_Dashboard_login_access_token;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DashboardLoginAccessTokenResponse = (Token);
|
||||||
|
|
||||||
|
export type DashboardRegisterNewShopData = {
|
||||||
|
requestBody: ShopCreate;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DashboardRegisterNewShopResponse = (boolean);
|
||||||
|
|
||||||
|
export type ShopLoginAccessTokenData = {
|
||||||
|
formData: Body_Shop_login_access_token;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopLoginAccessTokenResponse = (Token);
|
||||||
|
|
||||||
|
export type ShopDeleteUserData = {
|
||||||
|
shopUuid: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopDeleteUserResponse = (unknown);
|
||||||
|
|
||||||
|
export type ShopLogoutResponse = (unknown);
|
||||||
|
|
||||||
|
export type ShopRegisterData = {
|
||||||
|
requestBody: UserRegister;
|
||||||
|
shopUuid: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopRegisterResponse = (unknown);
|
||||||
|
|
||||||
|
export type ShopUpdateUserData = {
|
||||||
|
requestBody: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopUpdateUserResponse = (unknown);
|
||||||
|
|
||||||
|
export type UtilsHealthCheckResponse = (boolean);
|
||||||
|
|
||||||
|
export type UtilsTestDbResponse = (boolean);
|
@ -1,175 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
AudioWaveform,
|
|
||||||
BookOpen,
|
|
||||||
Bot,
|
|
||||||
Command,
|
|
||||||
Frame,
|
|
||||||
GalleryVerticalEnd,
|
|
||||||
Map,
|
|
||||||
PieChart,
|
|
||||||
Settings2,
|
|
||||||
SquareTerminal
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
import { NavMain } from "@/components/nav-main";
|
|
||||||
import { NavProjects } from "@/components/nav-projects";
|
|
||||||
import { NavUser } from "@/components/nav-user";
|
|
||||||
import { TeamSwitcher } from "@/components/team-switcher";
|
|
||||||
import {
|
|
||||||
Sidebar,
|
|
||||||
SidebarContent,
|
|
||||||
SidebarFooter,
|
|
||||||
SidebarHeader,
|
|
||||||
SidebarRail
|
|
||||||
} from "@/components/ui/sidebar";
|
|
||||||
|
|
||||||
// This is sample data.
|
|
||||||
const data = {
|
|
||||||
user: {
|
|
||||||
name: "shadcn",
|
|
||||||
email: "m@example.com",
|
|
||||||
avatar: "/avatars/shadcn.jpg"
|
|
||||||
},
|
|
||||||
teams: [
|
|
||||||
{
|
|
||||||
name: "Acme Inc",
|
|
||||||
logo: GalleryVerticalEnd,
|
|
||||||
plan: "Enterprise"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Acme Corp.",
|
|
||||||
logo: AudioWaveform,
|
|
||||||
plan: "Startup"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Evil Corp.",
|
|
||||||
logo: Command,
|
|
||||||
plan: "Free"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
navMain: [
|
|
||||||
{
|
|
||||||
title: "Playground",
|
|
||||||
url: "#",
|
|
||||||
icon: SquareTerminal,
|
|
||||||
isActive: true,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "History",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Starred",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Settings",
|
|
||||||
url: "#"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Models",
|
|
||||||
url: "#",
|
|
||||||
icon: Bot,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Genesis",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Explorer",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Quantum",
|
|
||||||
url: "#"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Documentation",
|
|
||||||
url: "#",
|
|
||||||
icon: BookOpen,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Introduction",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Get Started",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Tutorials",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Changelog",
|
|
||||||
url: "#"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Settings",
|
|
||||||
url: "#",
|
|
||||||
icon: Settings2,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "General",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Team",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Billing",
|
|
||||||
url: "#"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Limits",
|
|
||||||
url: "#"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: "Design Engineering",
|
|
||||||
url: "#",
|
|
||||||
icon: Frame
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Sales & Marketing",
|
|
||||||
url: "#",
|
|
||||||
icon: PieChart
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Travel",
|
|
||||||
url: "#",
|
|
||||||
icon: Map
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|
||||||
return (
|
|
||||||
<Sidebar collapsible="icon" {...props}>
|
|
||||||
<SidebarHeader>
|
|
||||||
<TeamSwitcher teams={data.teams} />
|
|
||||||
</SidebarHeader>
|
|
||||||
<SidebarContent>
|
|
||||||
<NavMain items={data.navMain} />
|
|
||||||
<NavProjects projects={data.projects} />
|
|
||||||
</SidebarContent>
|
|
||||||
<SidebarFooter>
|
|
||||||
<NavUser user={data.user} />
|
|
||||||
</SidebarFooter>
|
|
||||||
<SidebarRail />
|
|
||||||
</Sidebar>
|
|
||||||
);
|
|
||||||
}
|
|
32
frontend/src/components/dynamic-login-button.tsx
Normal file
32
frontend/src/components/dynamic-login-button.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Link } from "@tanstack/react-router";
|
||||||
|
import { Button } from "./ui/button";
|
||||||
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
|
||||||
|
const DynamicLoginButton = () => {
|
||||||
|
const { logout, user } = useAuth();
|
||||||
|
if (user) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button variant="secondary" className="hidden px-2 md:block">
|
||||||
|
<Link to="/dashboard">Dashboard</Link>
|
||||||
|
</Button>
|
||||||
|
<Button className="ml-2 mr-2 hidden md:block" onClick={logout}>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button variant="secondary" className="hidden px-2 md:block">
|
||||||
|
<Link to="/sign-in">Login</Link>
|
||||||
|
</Button>
|
||||||
|
<Button className="ml-2 mr-2 hidden md:block">
|
||||||
|
<Link to="/sign-up">Get Started</Link>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DynamicLoginButton;
|
@ -1,29 +1,33 @@
|
|||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
SidebarFooter,
|
|
||||||
SidebarHeader,
|
SidebarHeader,
|
||||||
SidebarRail
|
SidebarRail
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { NavGroup } from "@/components/layout/nav-group";
|
import { NavGroup } from "@/components/layout/nav-group";
|
||||||
import { NavUser } from "@/components/layout/nav-user";
|
|
||||||
import { TeamSwitcher } from "@/components/layout/team-switcher";
|
|
||||||
import { sidebarData } from "./data/sidebar-data";
|
import { sidebarData } from "./data/sidebar-data";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
return (
|
return (
|
||||||
<Sidebar collapsible="icon" variant="floating" {...props}>
|
<Sidebar collapsible="icon" variant="floating" {...props}>
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
<TeamSwitcher teams={sidebarData.teams} />
|
<h1
|
||||||
|
className={cn(
|
||||||
|
"header-fixed peer/header flex h-16 w-[inherit] items-center gap-3 rounded-md bg-background p-4 text-xl font-bold sm:gap-4"
|
||||||
|
)}
|
||||||
|
{...props}>
|
||||||
|
SwagShop
|
||||||
|
</h1>
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
{sidebarData.navGroups.map((props) => (
|
{sidebarData.navGroups.map((props) => (
|
||||||
<NavGroup key={props.title} {...props} />
|
<NavGroup key={props.title} {...props} />
|
||||||
))}
|
))}
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>
|
{/* <SidebarFooter>
|
||||||
<NavUser user={sidebarData.user} />
|
<NavUser user={sidebarData.user} />
|
||||||
</SidebarFooter>
|
</SidebarFooter> */}
|
||||||
<SidebarRail />
|
<SidebarRail />
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
IconBarrierBlock,
|
|
||||||
IconBrowserCheck,
|
IconBrowserCheck,
|
||||||
IconBug,
|
IconCoin,
|
||||||
IconChecklist,
|
IconForklift,
|
||||||
IconError404,
|
|
||||||
IconHelp,
|
IconHelp,
|
||||||
IconLayoutDashboard,
|
IconLayoutDashboard,
|
||||||
IconLock,
|
|
||||||
IconLockAccess,
|
|
||||||
IconMessages,
|
|
||||||
IconNotification,
|
IconNotification,
|
||||||
IconPackages,
|
IconPackage,
|
||||||
IconPalette,
|
IconPalette,
|
||||||
IconServerOff,
|
IconPercentage,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
|
IconTag,
|
||||||
IconTool,
|
IconTool,
|
||||||
IconUserCog,
|
IconUserCog,
|
||||||
IconUserOff,
|
|
||||||
IconUsers
|
IconUsers
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { AudioWaveform, Command, GalleryVerticalEnd } from "lucide-react";
|
import { AudioWaveform, Command, GalleryVerticalEnd } from "lucide-react";
|
||||||
@ -47,100 +42,43 @@ export const sidebarData: SidebarData = {
|
|||||||
],
|
],
|
||||||
navGroups: [
|
navGroups: [
|
||||||
{
|
{
|
||||||
title: "General",
|
title: "Dashboard",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
url: "/",
|
url: "/dashboard",
|
||||||
icon: IconLayoutDashboard
|
icon: IconLayoutDashboard
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Tasks",
|
title: "Products",
|
||||||
url: "/tasks",
|
url: "/dashboard/products",
|
||||||
icon: IconChecklist
|
icon: IconPackage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Apps",
|
title: "Inventory",
|
||||||
url: "/apps",
|
url: "/dashboard/tasks",
|
||||||
icon: IconPackages
|
icon: IconForklift
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Chats",
|
title: "Sales",
|
||||||
url: "/chats",
|
icon: IconCoin,
|
||||||
badge: "3",
|
items: [
|
||||||
icon: IconMessages
|
{
|
||||||
|
title: "Discounts",
|
||||||
|
url: "/dashboard/sales/discounts",
|
||||||
|
icon: IconPercentage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Users",
|
title: "Coupons",
|
||||||
url: "/users",
|
url: "/dashboard/sales/coupons",
|
||||||
|
icon: IconTag
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Customers",
|
||||||
|
url: "/dashboard/users",
|
||||||
icon: IconUsers
|
icon: IconUsers
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "NBA",
|
|
||||||
url: "/nba",
|
|
||||||
icon: IconChecklist
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Pages",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Auth",
|
|
||||||
icon: IconLockAccess,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Sign In",
|
|
||||||
url: "/sign-in"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Sign In (2 Col)",
|
|
||||||
url: "/sign-in-2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Sign Up",
|
|
||||||
url: "/sign-up"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Forgot Password",
|
|
||||||
url: "/forgot-password"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "OTP",
|
|
||||||
url: "/otp"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Errors",
|
|
||||||
icon: IconBug,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Unauthorized",
|
|
||||||
url: "/401",
|
|
||||||
icon: IconLock
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Forbidden",
|
|
||||||
url: "/403",
|
|
||||||
icon: IconUserOff
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Not Found",
|
|
||||||
url: "/404",
|
|
||||||
icon: IconError404
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Internal Server Error",
|
|
||||||
url: "/500",
|
|
||||||
icon: IconServerOff
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Maintenance Error",
|
|
||||||
url: "/503",
|
|
||||||
icon: IconBarrierBlock
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -153,27 +91,27 @@ export const sidebarData: SidebarData = {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "Profile",
|
title: "Profile",
|
||||||
url: "/settings",
|
url: "/dashboard/settings",
|
||||||
icon: IconUserCog
|
icon: IconUserCog
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Account",
|
title: "Account",
|
||||||
url: "/settings/account",
|
url: "/dashboard/settings/account",
|
||||||
icon: IconTool
|
icon: IconTool
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Appearance",
|
title: "Appearance",
|
||||||
url: "/settings/appearance",
|
url: "/dashboard/settings/appearance",
|
||||||
icon: IconPalette
|
icon: IconPalette
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Notifications",
|
title: "Notifications",
|
||||||
url: "/settings/notifications",
|
url: "/dashboard/settings/notifications",
|
||||||
icon: IconNotification
|
icon: IconNotification
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Display",
|
title: "Display",
|
||||||
url: "/settings/display",
|
url: "/dashboard/settings/display",
|
||||||
icon: IconBrowserCheck
|
icon: IconBrowserCheck
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -8,7 +8,7 @@ import { Menu } from "lucide-react";
|
|||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
import { ThemeSwitch } from "./theme-switch";
|
import { ThemeSwitch } from "./theme-switch";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Link } from "@tanstack/react-router";
|
import DynamicLoginButton from "./dynamic-login-button";
|
||||||
|
|
||||||
const MainNavbar = () => {
|
const MainNavbar = () => {
|
||||||
return (
|
return (
|
||||||
@ -27,11 +27,7 @@ const MainNavbar = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button variant="secondary" className="hidden px-2 md:block">
|
<DynamicLoginButton />
|
||||||
<Link to="/sign-in">Login</Link>
|
|
||||||
</Button>
|
|
||||||
<Button className="ml-2 mr-2 hidden md:block"><Link to="/sign-up">Get Started</Link></Button>
|
|
||||||
|
|
||||||
<div className="mr-2 flex items-center gap-2 md:hidden">
|
<div className="mr-2 flex items-center gap-2 md:hidden">
|
||||||
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
|
@ -11,51 +11,55 @@ import {
|
|||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
DropdownMenuTrigger
|
DropdownMenuTrigger
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
|
||||||
export function ProfileDropdown() {
|
export function ProfileDropdown() {
|
||||||
|
const { user, logout } = useAuth();
|
||||||
|
if (!user) return "";
|
||||||
|
|
||||||
|
let shorthandUsername = "";
|
||||||
|
if (user.first_name && user.last_name) {
|
||||||
|
shorthandUsername = (user.first_name[0] + user.last_name[0]).toUpperCase();
|
||||||
|
} else {
|
||||||
|
shorthandUsername = user.username[0].toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu modal={false}>
|
<DropdownMenu modal={false}>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
|
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
|
||||||
<Avatar className="h-8 w-8">
|
<Avatar className="h-8 w-8">
|
||||||
<AvatarImage src="/avatars/01.png" alt="@shadcn" />
|
<AvatarImage src="/avatars/01.png" alt="@shadcn" />
|
||||||
<AvatarFallback>SN</AvatarFallback>
|
<AvatarFallback>{shorthandUsername}</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-56" align="end" forceMount>
|
<DropdownMenuContent className="w-56" align="end" forceMount>
|
||||||
<DropdownMenuLabel className="font-normal">
|
<DropdownMenuLabel className="font-normal">
|
||||||
<div className="flex flex-col space-y-1">
|
<div className="flex flex-col space-y-1">
|
||||||
<p className="text-sm font-medium leading-none">satnaing</p>
|
<p className="text-sm font-medium leading-none">{user.username}</p>
|
||||||
<p className="text-xs leading-none text-muted-foreground">
|
<p className="text-xs leading-none text-muted-foreground">
|
||||||
satnaingdev@gmail.com
|
{user.email}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link to="/settings">
|
<Link to="/dashboard/settings">
|
||||||
Profile
|
Profile
|
||||||
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link to="/settings">
|
<Link to="/dashboard/settings">
|
||||||
Billing
|
|
||||||
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link to="/settings">
|
|
||||||
Settings
|
Settings
|
||||||
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem>New Team</DropdownMenuItem>
|
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem onClick={logout}>
|
||||||
Log out
|
Log out
|
||||||
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
@ -7,7 +7,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
167
frontend/src/components/ui/phone-number-input.tsx
Normal file
167
frontend/src/components/ui/phone-number-input.tsx
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { CheckIcon, ChevronsUpDown } from "lucide-react";
|
||||||
|
import * as RPNInput from "react-phone-number-input";
|
||||||
|
import flags from "react-phone-number-input/flags";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
type PhoneInputProps = Omit<
|
||||||
|
React.ComponentProps<"input">,
|
||||||
|
"onChange" | "value" | "ref"
|
||||||
|
> &
|
||||||
|
Omit<RPNInput.Props<typeof RPNInput.default>, "onChange"> & {
|
||||||
|
onChange?: (value: RPNInput.Value) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> =
|
||||||
|
React.forwardRef<React.ElementRef<typeof RPNInput.default>, PhoneInputProps>(
|
||||||
|
({ className, onChange, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<RPNInput.default
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex", className)}
|
||||||
|
flagComponent={FlagComponent}
|
||||||
|
countrySelectComponent={CountrySelect}
|
||||||
|
inputComponent={InputComponent}
|
||||||
|
smartCaret={false}
|
||||||
|
/**
|
||||||
|
* Handles the onChange event.
|
||||||
|
*
|
||||||
|
* react-phone-number-input might trigger the onChange event as undefined
|
||||||
|
* when a valid phone number is not entered. To prevent this,
|
||||||
|
* the value is coerced to an empty string.
|
||||||
|
*
|
||||||
|
* @param {E164Number | undefined} value - The entered value
|
||||||
|
*/
|
||||||
|
onChange={(value) => onChange?.(value || ("" as RPNInput.Value))}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
PhoneInput.displayName = "PhoneInput";
|
||||||
|
|
||||||
|
const InputComponent = React.forwardRef<
|
||||||
|
HTMLInputElement,
|
||||||
|
React.ComponentProps<"input">
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<Input
|
||||||
|
className={cn("rounded-e-lg rounded-s-none", className)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
InputComponent.displayName = "InputComponent";
|
||||||
|
|
||||||
|
type CountryEntry = { label: string; value: RPNInput.Country | undefined };
|
||||||
|
|
||||||
|
type CountrySelectProps = {
|
||||||
|
disabled?: boolean;
|
||||||
|
value: RPNInput.Country;
|
||||||
|
options: CountryEntry[];
|
||||||
|
onChange: (country: RPNInput.Country) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CountrySelect = ({
|
||||||
|
disabled,
|
||||||
|
value: selectedCountry,
|
||||||
|
options: countryList,
|
||||||
|
onChange
|
||||||
|
}: CountrySelectProps) => {
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
className="flex gap-1 rounded-e-none rounded-s-lg border-r-0 px-3 focus:z-10"
|
||||||
|
disabled={disabled}>
|
||||||
|
<FlagComponent
|
||||||
|
country={selectedCountry}
|
||||||
|
countryName={selectedCountry}
|
||||||
|
/>
|
||||||
|
<ChevronsUpDown
|
||||||
|
className={cn(
|
||||||
|
"-mr-2 size-4 opacity-50",
|
||||||
|
disabled ? "hidden" : "opacity-100"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[300px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search country..." />
|
||||||
|
<CommandList>
|
||||||
|
<ScrollArea className="h-72">
|
||||||
|
<CommandEmpty>No country found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{countryList.map(({ value, label }) =>
|
||||||
|
value ? (
|
||||||
|
<CountrySelectOption
|
||||||
|
key={value}
|
||||||
|
country={value}
|
||||||
|
countryName={label}
|
||||||
|
selectedCountry={selectedCountry}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</CommandGroup>
|
||||||
|
</ScrollArea>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface CountrySelectOptionProps extends RPNInput.FlagProps {
|
||||||
|
selectedCountry: RPNInput.Country;
|
||||||
|
onChange: (country: RPNInput.Country) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CountrySelectOption = ({
|
||||||
|
country,
|
||||||
|
countryName,
|
||||||
|
selectedCountry,
|
||||||
|
onChange
|
||||||
|
}: CountrySelectOptionProps) => {
|
||||||
|
return (
|
||||||
|
<CommandItem className="gap-2" onSelect={() => onChange(country)}>
|
||||||
|
<FlagComponent country={country} countryName={countryName} />
|
||||||
|
<span className="flex-1 text-sm">{countryName}</span>
|
||||||
|
<span className="text-sm text-foreground/50">{`+${RPNInput.getCountryCallingCode(country)}`}</span>
|
||||||
|
<CheckIcon
|
||||||
|
className={`ml-auto size-4 ${country === selectedCountry ? "opacity-100" : "opacity-0"}`}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
|
||||||
|
const Flag = flags[country];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="flex h-4 w-6 overflow-hidden rounded-sm bg-foreground/20 [&_svg]:size-full">
|
||||||
|
{Flag && <Flag title={countryName} />}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { PhoneInput };
|
@ -4,7 +4,7 @@ import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|||||||
import { VariantProps, cva } from "class-variance-authority";
|
import { VariantProps, cva } from "class-variance-authority";
|
||||||
import { PanelLeft } from "lucide-react";
|
import { PanelLeft } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/useMobile";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
@ -1,69 +1,105 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { useNavigate } from "@tanstack/react-router"
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { handleServerError } from "@/utils/handle-server-error"
|
import { handleServerError } from "@/utils/handle-server-error";
|
||||||
|
import { ApiError } from "@/errors/api-error";
|
||||||
|
import {
|
||||||
|
DashboardService,
|
||||||
|
LoginService,
|
||||||
|
ShopLoginAccessTokenData,
|
||||||
|
UserPublic,
|
||||||
|
UserRegister,
|
||||||
|
UserService,
|
||||||
|
UserUpdate
|
||||||
|
} from "@/client";
|
||||||
|
import { toast } from "./useToast";
|
||||||
|
|
||||||
const isLoggedIn = () => {
|
const isLoggedIn = () => {
|
||||||
return localStorage.getItem("access_token") !== null
|
return localStorage.getItem("access_token") !== null;
|
||||||
}
|
};
|
||||||
|
|
||||||
const useAuth = () => {
|
const useAuth = () => {
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null);
|
||||||
const navigate = useNavigate()
|
const [loggedIn, setLoggedIn] = useState(isLoggedIn());
|
||||||
const queryClient = useQueryClient()
|
const navigate = useNavigate();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const { data: user } = useQuery<UserPublic | null, Error>({
|
const { data: user } = useQuery<UserPublic | null, Error>({
|
||||||
queryKey: ["currentUser"],
|
queryKey: ["currentUser"],
|
||||||
queryFn: UsersService.readUserMe,
|
queryFn: DashboardService.userGetUser,
|
||||||
enabled: isLoggedIn(),
|
enabled: loggedIn
|
||||||
})
|
});
|
||||||
|
|
||||||
const signUpMutation = useMutation({
|
const signUpMutation = useMutation({
|
||||||
mutationFn: (data: UserRegister) =>
|
mutationFn: (data: UserRegister) =>
|
||||||
UsersService.registerUser({ requestBody: data }),
|
DashboardService.userRegister({ requestBody: data }),
|
||||||
|
onSuccess: () => navigate({ to: "/sign-in" }),
|
||||||
|
onError: (err: ApiError) => handleServerError(err),
|
||||||
|
onSettled: () => queryClient.invalidateQueries({ queryKey: ["users"] })
|
||||||
|
});
|
||||||
|
|
||||||
onSuccess: () => {
|
const login = async (data: ShopLoginAccessTokenData) => {
|
||||||
navigate({ to: "/sign-in" })
|
const response = await LoginService.dashboardLoginAccessToken({
|
||||||
},
|
formData: {
|
||||||
onError: (err: ApiError) => {
|
username: data.formData.username,
|
||||||
handleServerError(err)
|
password: data.formData.password
|
||||||
},
|
|
||||||
onSettled: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["users"] })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const login = async (data: AccessToken) => {
|
|
||||||
const response = await LoginService.loginAccessToken({
|
|
||||||
formData: data,
|
|
||||||
})
|
|
||||||
localStorage.setItem("access_token", response.access_token)
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
localStorage.setItem("access_token", response.access_token);
|
||||||
|
setLoggedIn(true);
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ["currentUser"] });
|
||||||
|
};
|
||||||
|
|
||||||
const loginMutation = useMutation({
|
const loginMutation = useMutation({
|
||||||
mutationFn: login,
|
mutationFn: login,
|
||||||
onSuccess: () => {
|
onSuccess: () => navigate({ to: "/" }),
|
||||||
navigate({ to: "/" })
|
onError: (err: ApiError) => handleServerError(err)
|
||||||
},
|
});
|
||||||
onError: (err: ApiError) => {
|
|
||||||
handleError(err)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
localStorage.removeItem("access_token")
|
localStorage.removeItem("access_token");
|
||||||
navigate({ to: "/sign-in" })
|
setLoggedIn(false);
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["currentUser"] });
|
||||||
|
navigate({ to: "/", replace: true, reloadDocument: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAccountMutation = useMutation({
|
||||||
|
mutationFn: (data: UserUpdate) =>
|
||||||
|
UserService.userUpdateUser({ requestBody: data }),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({ title: "Account updated successfully" });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["currentUser"] });
|
||||||
|
},
|
||||||
|
onError: (err: ApiError) => handleServerError(err)
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleStorageChange = (event: StorageEvent) => {
|
||||||
|
if (event.key === "access_token") {
|
||||||
|
const tokenExists = event.newValue !== null;
|
||||||
|
setLoggedIn(tokenExists);
|
||||||
|
if (!tokenExists) {
|
||||||
|
queryClient.removeQueries({ queryKey: ["currentUser"] });
|
||||||
|
} else {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["currentUser"] });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("storage", handleStorageChange);
|
||||||
|
return () => window.removeEventListener("storage", handleStorageChange);
|
||||||
|
}, [queryClient]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
signUpMutation,
|
signUpMutation,
|
||||||
loginMutation,
|
loginMutation,
|
||||||
|
updateAccountMutation,
|
||||||
logout,
|
logout,
|
||||||
user,
|
user,
|
||||||
error,
|
error,
|
||||||
resetError: () => setError(null),
|
resetError: () => setError(null)
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export { isLoggedIn }
|
export { isLoggedIn };
|
||||||
export default useAuth
|
export default useAuth;
|
||||||
|
@ -15,6 +15,12 @@ import { ThemeProvider } from "./context/theme-context";
|
|||||||
import "./index.css";
|
import "./index.css";
|
||||||
// Generated Routes
|
// Generated Routes
|
||||||
import { routeTree } from "./routeTree.gen";
|
import { routeTree } from "./routeTree.gen";
|
||||||
|
import { OpenAPI } from "./client";
|
||||||
|
|
||||||
|
OpenAPI.BASE = import.meta.env.VITE_API_URL;
|
||||||
|
OpenAPI.TOKEN = async () => {
|
||||||
|
return localStorage.getItem("access_token") ?? "";
|
||||||
|
};
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HTMLAttributes, useState } from "react";
|
import { HTMLAttributes } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { PasswordInput } from "@/components/password-input";
|
import { PasswordInput } from "@/components/password-input";
|
||||||
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
|
||||||
type UserAuthFormProps = HTMLAttributes<HTMLDivElement>;
|
type UserAuthFormProps = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ const formSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const { loginMutation, resetError } = useAuth();
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof formSchema>>({
|
const form = useForm<z.infer<typeof formSchema>>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
@ -44,13 +45,13 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(data: z.infer<typeof formSchema>) {
|
async function onSubmit(data: z.infer<typeof formSchema>) {
|
||||||
setIsLoading(true);
|
resetError();
|
||||||
console.log(data);
|
try {
|
||||||
|
await loginMutation.mutateAsync({formData: {username: data.email, password: data.password}});
|
||||||
setTimeout(() => {
|
} catch {
|
||||||
setIsLoading(false);
|
// Error handled in useAuth hook
|
||||||
}, 3000);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -91,8 +92,8 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Button className="mt-2" disabled={isLoading}>
|
<Button className="mt-2" disabled={loginMutation.isPending}>
|
||||||
Login
|
{loginMutation.isPending ? "Logging in..." : "Login"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="relative my-2">
|
<div className="relative my-2">
|
||||||
|
@ -16,6 +16,8 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { PasswordInput } from "@/components/password-input";
|
import { PasswordInput } from "@/components/password-input";
|
||||||
import { registerUser } from "@/lib/api";
|
import { registerUser } from "@/lib/api";
|
||||||
import { ApiError } from "@/errors/api-error";
|
import { ApiError } from "@/errors/api-error";
|
||||||
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
import { PhoneInput } from "@/components/ui/phone-number-input";
|
||||||
|
|
||||||
type SignUpFormProps = HTMLAttributes<HTMLDivElement>;
|
type SignUpFormProps = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
@ -64,6 +66,7 @@ const formSchema = z
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function SignUpForm({ className, ...props }: SignUpFormProps) {
|
export function SignUpForm({ className, ...props }: SignUpFormProps) {
|
||||||
|
const { signUpMutation } = useAuth();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||||
@ -85,12 +88,7 @@ export function SignUpForm({ className, ...props }: SignUpFormProps) {
|
|||||||
setSuccessMessage(null);
|
setSuccessMessage(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await registerUser({
|
signUpMutation.mutate(data);
|
||||||
username: data.username,
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
phone_number: data.phone_number
|
|
||||||
});
|
|
||||||
setSuccessMessage("Account created successfully!");
|
setSuccessMessage("Account created successfully!");
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
setError(error instanceof ApiError ? error.response : "Something went wrong.");
|
setError(error instanceof ApiError ? error.response : "Something went wrong.");
|
||||||
@ -137,7 +135,7 @@ export function SignUpForm({ className, ...props }: SignUpFormProps) {
|
|||||||
<FormItem className="space-y-1">
|
<FormItem className="space-y-1">
|
||||||
<FormLabel>Phone Number</FormLabel>
|
<FormLabel>Phone Number</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="+1234567890" {...field} />
|
<PhoneInput {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@ -33,9 +32,6 @@ export default function Dashboard() {
|
|||||||
<Main>
|
<Main>
|
||||||
<div className="mb-2 flex items-center justify-between space-y-2">
|
<div className="mb-2 flex items-center justify-between space-y-2">
|
||||||
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
|
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Button>Download</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<Tabs
|
<Tabs
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
|
@ -18,8 +18,6 @@ export default function MainPage() {
|
|||||||
<Pricing />
|
<Pricing />
|
||||||
<Faq />
|
<Faq />
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
|
|
||||||
{/* [Optional] Consider inserting an image or illustration here to reinforce the platform’s purpose. */}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export default function Nba() {
|
|
||||||
return <h1>NBA Testing</h1>;
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ const appText = new Map<string, string>([
|
|||||||
["notConnected", "Not Connected"]
|
["notConnected", "Not Connected"]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export default function Apps() {
|
export default function Products() {
|
||||||
const [sort, setSort] = useState("ascending");
|
const [sort, setSort] = useState("ascending");
|
||||||
const [appType, setAppType] = useState("all");
|
const [appType, setAppType] = useState("all");
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
@ -1,86 +1,63 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { format } from "date-fns";
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { CalendarIcon, CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { toast } from "@/hooks/useToast";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import
|
||||||
import {
|
{
|
||||||
Command,
|
|
||||||
CommandEmpty,
|
|
||||||
CommandGroup,
|
|
||||||
CommandInput,
|
|
||||||
CommandItem,
|
|
||||||
CommandList
|
|
||||||
} from "@/components/ui/command";
|
|
||||||
import {
|
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl, FormField,
|
||||||
FormDescription,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage
|
FormMessage
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import useAuth from "@/hooks/useAuth";
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger
|
|
||||||
} from "@/components/ui/popover";
|
|
||||||
|
|
||||||
const languages = [
|
// const languages = [
|
||||||
{ label: "English", value: "en" },
|
// { label: "English", value: "en" },
|
||||||
{ label: "French", value: "fr" },
|
// { label: "French", value: "fr" },
|
||||||
{ label: "German", value: "de" },
|
// { label: "German", value: "de" },
|
||||||
{ label: "Spanish", value: "es" },
|
// { label: "Spanish", value: "es" },
|
||||||
{ label: "Portuguese", value: "pt" },
|
// { label: "Portuguese", value: "pt" },
|
||||||
{ label: "Russian", value: "ru" },
|
// { label: "Russian", value: "ru" },
|
||||||
{ label: "Japanese", value: "ja" },
|
// { label: "Japanese", value: "ja" },
|
||||||
{ label: "Korean", value: "ko" },
|
// { label: "Korean", value: "ko" },
|
||||||
{ label: "Chinese", value: "zh" }
|
// { label: "Chinese", value: "zh" }
|
||||||
] as const;
|
// ] as const;
|
||||||
|
|
||||||
const accountFormSchema = z.object({
|
const accountFormSchema = z.object({
|
||||||
name: z
|
username: z.string().min(3).max(30),
|
||||||
.string()
|
email: z.string().email(),
|
||||||
.min(2, {
|
phone_number: z.string(),
|
||||||
message: "Name must be at least 2 characters."
|
// password: z.string().min(8).max(128),
|
||||||
})
|
first_name: z.string().optional(),
|
||||||
.max(30, {
|
last_name: z.string().optional(),
|
||||||
message: "Name must not be longer than 30 characters."
|
// dob: z.date({
|
||||||
}),
|
// required_error: "A date of birth is required."
|
||||||
dob: z.date({
|
// }),
|
||||||
required_error: "A date of birth is required."
|
// language: z.string({
|
||||||
}),
|
// required_error: "Please select a language."
|
||||||
language: z.string({
|
// })
|
||||||
required_error: "Please select a language."
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type AccountFormValues = z.infer<typeof accountFormSchema>;
|
type AccountFormValues = z.infer<typeof accountFormSchema>;
|
||||||
|
|
||||||
// This can come from your database or API.
|
|
||||||
const defaultValues: Partial<AccountFormValues> = {
|
|
||||||
name: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AccountForm() {
|
export function AccountForm() {
|
||||||
|
const { updateAccountMutation, user } = useAuth();
|
||||||
|
|
||||||
const form = useForm<AccountFormValues>({
|
const form = useForm<AccountFormValues>({
|
||||||
resolver: zodResolver(accountFormSchema),
|
resolver: zodResolver(accountFormSchema)
|
||||||
defaultValues
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(data: AccountFormValues) {
|
function onSubmit(data: AccountFormValues) {
|
||||||
toast({
|
updateAccountMutation.mutate({
|
||||||
title: "You submitted the following values:",
|
email: data.email,
|
||||||
description: (
|
// password: data.password,
|
||||||
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
password: "null",
|
||||||
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
phone_number: data.phone_number,
|
||||||
</pre>
|
username: data.username,
|
||||||
)
|
first_name: data.first_name,
|
||||||
|
last_name: data.last_name
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,125 +66,57 @@ export function AccountForm() {
|
|||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Name</FormLabel>
|
<FormLabel>Username</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="Your name" {...field} />
|
<Input placeholder="Your username" defaultValue={user?.username} {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
|
||||||
This is the name that will be displayed on your profile and in
|
|
||||||
emails.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="dob"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-col">
|
<FormItem>
|
||||||
<FormLabel>Date of birth</FormLabel>
|
<FormLabel>Email</FormLabel>
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Button
|
<Input placeholder="Your email" defaultValue={user?.email} {...field} />
|
||||||
variant={"outline"}
|
|
||||||
className={cn(
|
|
||||||
"w-[240px] pl-3 text-left font-normal",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}>
|
|
||||||
{field.value ? (
|
|
||||||
format(field.value, "MMM d, yyyy")
|
|
||||||
) : (
|
|
||||||
<span>Pick a date</span>
|
|
||||||
)}
|
|
||||||
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
|
||||||
<Calendar
|
|
||||||
mode="single"
|
|
||||||
selected={field.value}
|
|
||||||
onSelect={field.onChange}
|
|
||||||
disabled={(date: Date) =>
|
|
||||||
date > new Date() || date < new Date("1900-01-01")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormDescription>
|
|
||||||
Your date of birth is used to calculate your age.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="language"
|
name="phone_number"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-col">
|
<FormItem>
|
||||||
<FormLabel>Language</FormLabel>
|
<FormLabel>Phone Number</FormLabel>
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Button
|
<Input placeholder="Your phone number" defaultValue={user?.phone_number} {...field} />
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
className={cn(
|
|
||||||
"w-[200px] justify-between",
|
|
||||||
!field.value && "text-muted-foreground"
|
|
||||||
)}>
|
|
||||||
{field.value
|
|
||||||
? languages.find(
|
|
||||||
(language) => language.value === field.value
|
|
||||||
)?.label
|
|
||||||
: "Select language"}
|
|
||||||
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[200px] p-0">
|
|
||||||
<Command>
|
|
||||||
<CommandInput placeholder="Search language..." />
|
|
||||||
<CommandEmpty>No language found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
<CommandList>
|
|
||||||
{languages.map((language) => (
|
|
||||||
<CommandItem
|
|
||||||
value={language.label}
|
|
||||||
key={language.value}
|
|
||||||
onSelect={() => {
|
|
||||||
form.setValue("language", language.value);
|
|
||||||
}}>
|
|
||||||
<CheckIcon
|
|
||||||
className={cn(
|
|
||||||
"mr-2 h-4 w-4",
|
|
||||||
language.value === field.value
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{language.label}
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandList>
|
|
||||||
</CommandGroup>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
<FormDescription>
|
|
||||||
This is the language that will be used in the dashboard.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Button type="submit">Update account</Button>
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="password"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="password" placeholder="New password" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Update Account</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Outlet } from "@tanstack/react-router";
|
import { Outlet } from "@tanstack/react-router";
|
||||||
import {
|
import {
|
||||||
IconBrowserCheck,
|
IconBrowserCheck,
|
||||||
|
IconLock,
|
||||||
IconNotification,
|
IconNotification,
|
||||||
IconPalette,
|
IconPalette,
|
||||||
IconTool,
|
|
||||||
IconUser
|
IconUser
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
@ -53,26 +53,26 @@ const sidebarNavItems = [
|
|||||||
{
|
{
|
||||||
title: "Profile",
|
title: "Profile",
|
||||||
icon: <IconUser size={18} />,
|
icon: <IconUser size={18} />,
|
||||||
href: "/settings"
|
href: "/dashboard/settings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Account",
|
title: "Security",
|
||||||
icon: <IconTool size={18} />,
|
icon: <IconLock size={18} />,
|
||||||
href: "/settings/account"
|
href: "/dashboard/settings/security"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Appearance",
|
title: "Appearance",
|
||||||
icon: <IconPalette size={18} />,
|
icon: <IconPalette size={18} />,
|
||||||
href: "/settings/appearance"
|
href: "/dashboard/settings/appearance"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Notifications",
|
title: "Notifications",
|
||||||
icon: <IconNotification size={18} />,
|
icon: <IconNotification size={18} />,
|
||||||
href: "/settings/notifications"
|
href: "/dashboard/settings/notifications"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Display",
|
title: "Display",
|
||||||
icon: <IconBrowserCheck size={18} />,
|
icon: <IconBrowserCheck size={18} />,
|
||||||
href: "/settings/display"
|
href: "/dashboard/settings/display"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useFieldArray, useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { toast } from "@/hooks/useToast";
|
import { toast } from "@/hooks/useToast";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@ -15,63 +14,71 @@ import {
|
|||||||
FormMessage
|
FormMessage
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import { PhoneInput } from "@/components/ui/phone-number-input";
|
||||||
Select,
|
import useAuth from "@/hooks/useAuth";
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
|
|
||||||
const profileFormSchema = z.object({
|
const profileFormSchema = z.object({
|
||||||
username: z
|
username: z
|
||||||
.string()
|
.string({
|
||||||
.min(2, {
|
required_error: "Please enter your desired username"
|
||||||
message: "Username must be at least 2 characters."
|
|
||||||
})
|
})
|
||||||
.max(30, {
|
.min(3, {
|
||||||
message: "Username must not be longer than 30 characters."
|
message: "Username must be at least 3 characters."
|
||||||
|
})
|
||||||
|
.max(64, {
|
||||||
|
message: "Username must not be longer than 64 characters."
|
||||||
}),
|
}),
|
||||||
email: z
|
email: z
|
||||||
.string({
|
.string()
|
||||||
required_error: "Please select an email to display."
|
.email({
|
||||||
|
message: "Please enter your email address."
|
||||||
})
|
})
|
||||||
.email(),
|
.email(),
|
||||||
bio: z.string().max(160).min(4),
|
phone_number: z
|
||||||
urls: z
|
.string({
|
||||||
.array(
|
required_error: "Please input a valid phone number."
|
||||||
z.object({
|
})
|
||||||
value: z.string().url({ message: "Please enter a valid URL." })
|
.min(2, {
|
||||||
|
message: "Phone number must be at least 2 characters."
|
||||||
|
})
|
||||||
|
.max(16, {
|
||||||
|
message: "Phone number must not be longer than 16 characters."
|
||||||
|
}),
|
||||||
|
first_name: z
|
||||||
|
.string()
|
||||||
|
.max(64, {
|
||||||
|
message: "First name must not be longer than 64 characters."
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
last_name: z
|
||||||
|
.string()
|
||||||
|
.max(64, {
|
||||||
|
message: "Last name must not be longer than 64 characters."
|
||||||
})
|
})
|
||||||
)
|
|
||||||
.optional()
|
.optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
type ProfileFormValues = z.infer<typeof profileFormSchema>;
|
type ProfileFormValues = z.infer<typeof profileFormSchema>;
|
||||||
|
|
||||||
// This can come from your database or API.
|
|
||||||
const defaultValues: Partial<ProfileFormValues> = {
|
|
||||||
bio: "I own a computer.",
|
|
||||||
urls: [
|
|
||||||
{ value: "https://shadcn.com" },
|
|
||||||
{ value: "http://twitter.com/shadcn" }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function ProfileForm() {
|
export default function ProfileForm() {
|
||||||
|
const { user, updateAccountMutation } = useAuth();
|
||||||
|
|
||||||
|
const defaultValues: ProfileFormValues = {
|
||||||
|
username: user?.username ?? "",
|
||||||
|
email: user?.email ?? "",
|
||||||
|
phone_number: user?.phone_number ?? "",
|
||||||
|
first_name: user?.first_name ?? "",
|
||||||
|
last_name: user?.last_name ?? ""
|
||||||
|
};
|
||||||
|
|
||||||
const form = useForm<ProfileFormValues>({
|
const form = useForm<ProfileFormValues>({
|
||||||
resolver: zodResolver(profileFormSchema),
|
resolver: zodResolver(profileFormSchema),
|
||||||
defaultValues,
|
defaultValues,
|
||||||
mode: "onChange"
|
mode: "onChange"
|
||||||
});
|
});
|
||||||
|
|
||||||
const { fields, append } = useFieldArray({
|
|
||||||
name: "urls",
|
|
||||||
control: form.control
|
|
||||||
});
|
|
||||||
|
|
||||||
function onSubmit(data: ProfileFormValues) {
|
function onSubmit(data: ProfileFormValues) {
|
||||||
|
updateAccountMutation.mutate(data);
|
||||||
toast({
|
toast({
|
||||||
title: "You submitted the following values:",
|
title: "You submitted the following values:",
|
||||||
description: (
|
description: (
|
||||||
@ -92,11 +99,11 @@ export default function ProfileForm() {
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Username</FormLabel>
|
<FormLabel>Username</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="shadcn" {...field} />
|
<Input placeholder="Username" {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
This is your public display name. It can be your real name or a
|
This is your public display name. It can be your real name or a
|
||||||
pseudonym. You can only change this once every 30 days.
|
pseudonym.
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
@ -108,18 +115,9 @@ export default function ProfileForm() {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Email</FormLabel>
|
<FormLabel>Email</FormLabel>
|
||||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<Input type="email" {...field} />
|
||||||
<SelectValue placeholder="Select a verified email to display" />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="m@example.com">m@example.com</SelectItem>
|
|
||||||
<SelectItem value="m@google.com">m@google.com</SelectItem>
|
|
||||||
<SelectItem value="m@support.com">m@support.com</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
You can manage verified email addresses in your{" "}
|
You can manage verified email addresses in your{" "}
|
||||||
<Link to="/">email settings</Link>.
|
<Link to="/">email settings</Link>.
|
||||||
@ -130,56 +128,44 @@ export default function ProfileForm() {
|
|||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="bio"
|
name="first_name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Bio</FormLabel>
|
<FormLabel>First Name</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Input placeholder="John" {...field} />
|
||||||
placeholder="Tell us a little bit about yourself"
|
|
||||||
className="resize-none"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
|
||||||
You can <span>@mention</span> other users and organizations to
|
|
||||||
link to them.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div>
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
key={field.id}
|
name="last_name"
|
||||||
name={`urls.${index}.value`}
|
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className={cn(index !== 0 && "sr-only")}>
|
<FormLabel>Last Name</FormLabel>
|
||||||
URLs
|
|
||||||
</FormLabel>
|
|
||||||
<FormDescription className={cn(index !== 0 && "sr-only")}>
|
|
||||||
Add links to your website, blog, or social media profiles.
|
|
||||||
</FormDescription>
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input {...field} />
|
<Input placeholder="Doe" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="phone_number"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Phone number</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<PhoneInput {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className="mt-2"
|
|
||||||
onClick={() => append({ value: "" })}>
|
|
||||||
Add URL
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Button type="submit">Update profile</Button>
|
<Button type="submit">Update profile</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
0
frontend/src/pages/settings/security/index.tsx
Normal file
0
frontend/src/pages/settings/security/index.tsx
Normal file
@ -32,41 +32,47 @@ const authSignIn2LazyImport = createFileRoute('/(auth)/sign-in-2')()
|
|||||||
const authForgotPasswordLazyImport = createFileRoute(
|
const authForgotPasswordLazyImport = createFileRoute(
|
||||||
'/(auth)/forgot-password',
|
'/(auth)/forgot-password',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedSettingsRouteLazyImport = createFileRoute(
|
|
||||||
'/_authenticated/settings',
|
|
||||||
)()
|
|
||||||
const AuthenticatedUsersIndexLazyImport = createFileRoute(
|
|
||||||
'/_authenticated/users/',
|
|
||||||
)()
|
|
||||||
const AuthenticatedTasksIndexLazyImport = createFileRoute(
|
|
||||||
'/_authenticated/tasks/',
|
|
||||||
)()
|
|
||||||
const AuthenticatedSettingsIndexLazyImport = createFileRoute(
|
|
||||||
'/_authenticated/settings/',
|
|
||||||
)()
|
|
||||||
const AuthenticatedNbaIndexLazyImport = createFileRoute(
|
|
||||||
'/_authenticated/nba/',
|
|
||||||
)()
|
|
||||||
const AuthenticatedHelpCenterIndexLazyImport = createFileRoute(
|
const AuthenticatedHelpCenterIndexLazyImport = createFileRoute(
|
||||||
'/_authenticated/help-center/',
|
'/_authenticated/help-center/',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedChatsIndexLazyImport = createFileRoute(
|
const AuthenticatedDashboardSettingsRouteLazyImport = createFileRoute(
|
||||||
'/_authenticated/chats/',
|
'/_authenticated/dashboard/settings',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedAppsIndexLazyImport = createFileRoute(
|
const AuthenticatedDashboardUsersIndexLazyImport = createFileRoute(
|
||||||
'/_authenticated/apps/',
|
'/_authenticated/dashboard/users/',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedSettingsNotificationsLazyImport = createFileRoute(
|
const AuthenticatedDashboardTasksIndexLazyImport = createFileRoute(
|
||||||
'/_authenticated/settings/notifications',
|
'/_authenticated/dashboard/tasks/',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedSettingsDisplayLazyImport = createFileRoute(
|
const AuthenticatedDashboardSettingsIndexLazyImport = createFileRoute(
|
||||||
'/_authenticated/settings/display',
|
'/_authenticated/dashboard/settings/',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedSettingsAppearanceLazyImport = createFileRoute(
|
const AuthenticatedDashboardSalesIndexLazyImport = createFileRoute(
|
||||||
'/_authenticated/settings/appearance',
|
'/_authenticated/dashboard/sales/',
|
||||||
)()
|
)()
|
||||||
const AuthenticatedSettingsAccountLazyImport = createFileRoute(
|
const AuthenticatedDashboardProductsIndexLazyImport = createFileRoute(
|
||||||
'/_authenticated/settings/account',
|
'/_authenticated/dashboard/products/',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardChatsIndexLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/chats/',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardSettingsNotificationsLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/settings/notifications',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardSettingsDisplayLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/settings/display',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardSettingsAppearanceLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/settings/appearance',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardSettingsAccountLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/settings/account',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardSalesDiscountsLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/sales/discounts',
|
||||||
|
)()
|
||||||
|
const AuthenticatedDashboardSalesCouponsLazyImport = createFileRoute(
|
||||||
|
'/_authenticated/dashboard/sales/coupons',
|
||||||
)()
|
)()
|
||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
@ -148,15 +154,6 @@ const authForgotPasswordLazyRoute = authForgotPasswordLazyImport
|
|||||||
import('./routes/(auth)/forgot-password.lazy').then((d) => d.Route),
|
import('./routes/(auth)/forgot-password.lazy').then((d) => d.Route),
|
||||||
)
|
)
|
||||||
|
|
||||||
const AuthenticatedSettingsRouteLazyRoute =
|
|
||||||
AuthenticatedSettingsRouteLazyImport.update({
|
|
||||||
id: '/settings',
|
|
||||||
path: '/settings',
|
|
||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
|
||||||
} as any).lazy(() =>
|
|
||||||
import('./routes/_authenticated/settings/route.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const authSignInRoute = authSignInImport.update({
|
const authSignInRoute = authSignInImport.update({
|
||||||
id: '/(auth)/sign-in',
|
id: '/(auth)/sign-in',
|
||||||
path: '/sign-in',
|
path: '/sign-in',
|
||||||
@ -175,41 +172,6 @@ const auth500Route = auth500Import.update({
|
|||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
const AuthenticatedUsersIndexLazyRoute =
|
|
||||||
AuthenticatedUsersIndexLazyImport.update({
|
|
||||||
id: '/users/',
|
|
||||||
path: '/users/',
|
|
||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
|
||||||
} as any).lazy(() =>
|
|
||||||
import('./routes/_authenticated/users/index.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const AuthenticatedTasksIndexLazyRoute =
|
|
||||||
AuthenticatedTasksIndexLazyImport.update({
|
|
||||||
id: '/tasks/',
|
|
||||||
path: '/tasks/',
|
|
||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
|
||||||
} as any).lazy(() =>
|
|
||||||
import('./routes/_authenticated/tasks/index.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const AuthenticatedSettingsIndexLazyRoute =
|
|
||||||
AuthenticatedSettingsIndexLazyImport.update({
|
|
||||||
id: '/',
|
|
||||||
path: '/',
|
|
||||||
getParentRoute: () => AuthenticatedSettingsRouteLazyRoute,
|
|
||||||
} as any).lazy(() =>
|
|
||||||
import('./routes/_authenticated/settings/index.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const AuthenticatedNbaIndexLazyRoute = AuthenticatedNbaIndexLazyImport.update({
|
|
||||||
id: '/nba/',
|
|
||||||
path: '/nba/',
|
|
||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
|
||||||
} as any).lazy(() =>
|
|
||||||
import('./routes/_authenticated/nba/index.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const AuthenticatedHelpCenterIndexLazyRoute =
|
const AuthenticatedHelpCenterIndexLazyRoute =
|
||||||
AuthenticatedHelpCenterIndexLazyImport.update({
|
AuthenticatedHelpCenterIndexLazyImport.update({
|
||||||
id: '/help-center/',
|
id: '/help-center/',
|
||||||
@ -221,25 +183,6 @@ const AuthenticatedHelpCenterIndexLazyRoute =
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const AuthenticatedChatsIndexLazyRoute =
|
|
||||||
AuthenticatedChatsIndexLazyImport.update({
|
|
||||||
id: '/chats/',
|
|
||||||
path: '/chats/',
|
|
||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
|
||||||
} as any).lazy(() =>
|
|
||||||
import('./routes/_authenticated/chats/index.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const AuthenticatedAppsIndexLazyRoute = AuthenticatedAppsIndexLazyImport.update(
|
|
||||||
{
|
|
||||||
id: '/apps/',
|
|
||||||
path: '/apps/',
|
|
||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
|
||||||
} as any,
|
|
||||||
).lazy(() =>
|
|
||||||
import('./routes/_authenticated/apps/index.lazy').then((d) => d.Route),
|
|
||||||
)
|
|
||||||
|
|
||||||
const AuthenticatedDashboardIndexRoute =
|
const AuthenticatedDashboardIndexRoute =
|
||||||
AuthenticatedDashboardIndexImport.update({
|
AuthenticatedDashboardIndexImport.update({
|
||||||
id: '/dashboard/',
|
id: '/dashboard/',
|
||||||
@ -247,46 +190,145 @@ const AuthenticatedDashboardIndexRoute =
|
|||||||
getParentRoute: () => AuthenticatedRouteRoute,
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
const AuthenticatedSettingsNotificationsLazyRoute =
|
const AuthenticatedDashboardSettingsRouteLazyRoute =
|
||||||
AuthenticatedSettingsNotificationsLazyImport.update({
|
AuthenticatedDashboardSettingsRouteLazyImport.update({
|
||||||
|
id: '/dashboard/settings',
|
||||||
|
path: '/dashboard/settings',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/settings/route.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardUsersIndexLazyRoute =
|
||||||
|
AuthenticatedDashboardUsersIndexLazyImport.update({
|
||||||
|
id: '/dashboard/users/',
|
||||||
|
path: '/dashboard/users/',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/users/index.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardTasksIndexLazyRoute =
|
||||||
|
AuthenticatedDashboardTasksIndexLazyImport.update({
|
||||||
|
id: '/dashboard/tasks/',
|
||||||
|
path: '/dashboard/tasks/',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/tasks/index.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardSettingsIndexLazyRoute =
|
||||||
|
AuthenticatedDashboardSettingsIndexLazyImport.update({
|
||||||
|
id: '/',
|
||||||
|
path: '/',
|
||||||
|
getParentRoute: () => AuthenticatedDashboardSettingsRouteLazyRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/settings/index.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardSalesIndexLazyRoute =
|
||||||
|
AuthenticatedDashboardSalesIndexLazyImport.update({
|
||||||
|
id: '/dashboard/sales/',
|
||||||
|
path: '/dashboard/sales/',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/sales/index.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardProductsIndexLazyRoute =
|
||||||
|
AuthenticatedDashboardProductsIndexLazyImport.update({
|
||||||
|
id: '/dashboard/products/',
|
||||||
|
path: '/dashboard/products/',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/products/index.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardChatsIndexLazyRoute =
|
||||||
|
AuthenticatedDashboardChatsIndexLazyImport.update({
|
||||||
|
id: '/dashboard/chats/',
|
||||||
|
path: '/dashboard/chats/',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/chats/index.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardSettingsNotificationsLazyRoute =
|
||||||
|
AuthenticatedDashboardSettingsNotificationsLazyImport.update({
|
||||||
id: '/notifications',
|
id: '/notifications',
|
||||||
path: '/notifications',
|
path: '/notifications',
|
||||||
getParentRoute: () => AuthenticatedSettingsRouteLazyRoute,
|
getParentRoute: () => AuthenticatedDashboardSettingsRouteLazyRoute,
|
||||||
} as any).lazy(() =>
|
} as any).lazy(() =>
|
||||||
import('./routes/_authenticated/settings/notifications.lazy').then(
|
import(
|
||||||
(d) => d.Route,
|
'./routes/_authenticated/dashboard/settings/notifications.lazy'
|
||||||
),
|
).then((d) => d.Route),
|
||||||
)
|
)
|
||||||
|
|
||||||
const AuthenticatedSettingsDisplayLazyRoute =
|
const AuthenticatedDashboardSettingsDisplayLazyRoute =
|
||||||
AuthenticatedSettingsDisplayLazyImport.update({
|
AuthenticatedDashboardSettingsDisplayLazyImport.update({
|
||||||
id: '/display',
|
id: '/display',
|
||||||
path: '/display',
|
path: '/display',
|
||||||
getParentRoute: () => AuthenticatedSettingsRouteLazyRoute,
|
getParentRoute: () => AuthenticatedDashboardSettingsRouteLazyRoute,
|
||||||
} as any).lazy(() =>
|
} as any).lazy(() =>
|
||||||
import('./routes/_authenticated/settings/display.lazy').then(
|
import('./routes/_authenticated/dashboard/settings/display.lazy').then(
|
||||||
(d) => d.Route,
|
(d) => d.Route,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const AuthenticatedSettingsAppearanceLazyRoute =
|
const AuthenticatedDashboardSettingsAppearanceLazyRoute =
|
||||||
AuthenticatedSettingsAppearanceLazyImport.update({
|
AuthenticatedDashboardSettingsAppearanceLazyImport.update({
|
||||||
id: '/appearance',
|
id: '/appearance',
|
||||||
path: '/appearance',
|
path: '/appearance',
|
||||||
getParentRoute: () => AuthenticatedSettingsRouteLazyRoute,
|
getParentRoute: () => AuthenticatedDashboardSettingsRouteLazyRoute,
|
||||||
} as any).lazy(() =>
|
} as any).lazy(() =>
|
||||||
import('./routes/_authenticated/settings/appearance.lazy').then(
|
import('./routes/_authenticated/dashboard/settings/appearance.lazy').then(
|
||||||
(d) => d.Route,
|
(d) => d.Route,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const AuthenticatedSettingsAccountLazyRoute =
|
const AuthenticatedDashboardSettingsAccountLazyRoute =
|
||||||
AuthenticatedSettingsAccountLazyImport.update({
|
AuthenticatedDashboardSettingsAccountLazyImport.update({
|
||||||
id: '/account',
|
id: '/account',
|
||||||
path: '/account',
|
path: '/account',
|
||||||
getParentRoute: () => AuthenticatedSettingsRouteLazyRoute,
|
getParentRoute: () => AuthenticatedDashboardSettingsRouteLazyRoute,
|
||||||
} as any).lazy(() =>
|
} as any).lazy(() =>
|
||||||
import('./routes/_authenticated/settings/account.lazy').then(
|
import('./routes/_authenticated/dashboard/settings/account.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardSalesDiscountsLazyRoute =
|
||||||
|
AuthenticatedDashboardSalesDiscountsLazyImport.update({
|
||||||
|
id: '/dashboard/sales/discounts',
|
||||||
|
path: '/dashboard/sales/discounts',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/sales/discounts.lazy').then(
|
||||||
|
(d) => d.Route,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticatedDashboardSalesCouponsLazyRoute =
|
||||||
|
AuthenticatedDashboardSalesCouponsLazyImport.update({
|
||||||
|
id: '/dashboard/sales/coupons',
|
||||||
|
path: '/dashboard/sales/coupons',
|
||||||
|
getParentRoute: () => AuthenticatedRouteRoute,
|
||||||
|
} as any).lazy(() =>
|
||||||
|
import('./routes/_authenticated/dashboard/sales/coupons.lazy').then(
|
||||||
(d) => d.Route,
|
(d) => d.Route,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -330,13 +372,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof authSignInImport
|
preLoaderRoute: typeof authSignInImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
'/_authenticated/settings': {
|
|
||||||
id: '/_authenticated/settings'
|
|
||||||
path: '/settings'
|
|
||||||
fullPath: '/settings'
|
|
||||||
preLoaderRoute: typeof AuthenticatedSettingsRouteLazyImport
|
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
|
||||||
}
|
|
||||||
'/(auth)/forgot-password': {
|
'/(auth)/forgot-password': {
|
||||||
id: '/(auth)/forgot-password'
|
id: '/(auth)/forgot-password'
|
||||||
path: '/forgot-password'
|
path: '/forgot-password'
|
||||||
@ -393,33 +428,12 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof errors503LazyImport
|
preLoaderRoute: typeof errors503LazyImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
'/_authenticated/settings/account': {
|
'/_authenticated/dashboard/settings': {
|
||||||
id: '/_authenticated/settings/account'
|
id: '/_authenticated/dashboard/settings'
|
||||||
path: '/account'
|
path: '/dashboard/settings'
|
||||||
fullPath: '/settings/account'
|
fullPath: '/dashboard/settings'
|
||||||
preLoaderRoute: typeof AuthenticatedSettingsAccountLazyImport
|
preLoaderRoute: typeof AuthenticatedDashboardSettingsRouteLazyImport
|
||||||
parentRoute: typeof AuthenticatedSettingsRouteLazyImport
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
}
|
|
||||||
'/_authenticated/settings/appearance': {
|
|
||||||
id: '/_authenticated/settings/appearance'
|
|
||||||
path: '/appearance'
|
|
||||||
fullPath: '/settings/appearance'
|
|
||||||
preLoaderRoute: typeof AuthenticatedSettingsAppearanceLazyImport
|
|
||||||
parentRoute: typeof AuthenticatedSettingsRouteLazyImport
|
|
||||||
}
|
|
||||||
'/_authenticated/settings/display': {
|
|
||||||
id: '/_authenticated/settings/display'
|
|
||||||
path: '/display'
|
|
||||||
fullPath: '/settings/display'
|
|
||||||
preLoaderRoute: typeof AuthenticatedSettingsDisplayLazyImport
|
|
||||||
parentRoute: typeof AuthenticatedSettingsRouteLazyImport
|
|
||||||
}
|
|
||||||
'/_authenticated/settings/notifications': {
|
|
||||||
id: '/_authenticated/settings/notifications'
|
|
||||||
path: '/notifications'
|
|
||||||
fullPath: '/settings/notifications'
|
|
||||||
preLoaderRoute: typeof AuthenticatedSettingsNotificationsLazyImport
|
|
||||||
parentRoute: typeof AuthenticatedSettingsRouteLazyImport
|
|
||||||
}
|
}
|
||||||
'/_authenticated/dashboard/': {
|
'/_authenticated/dashboard/': {
|
||||||
id: '/_authenticated/dashboard/'
|
id: '/_authenticated/dashboard/'
|
||||||
@ -428,20 +442,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthenticatedDashboardIndexImport
|
preLoaderRoute: typeof AuthenticatedDashboardIndexImport
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
}
|
}
|
||||||
'/_authenticated/apps/': {
|
|
||||||
id: '/_authenticated/apps/'
|
|
||||||
path: '/apps'
|
|
||||||
fullPath: '/apps'
|
|
||||||
preLoaderRoute: typeof AuthenticatedAppsIndexLazyImport
|
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
|
||||||
}
|
|
||||||
'/_authenticated/chats/': {
|
|
||||||
id: '/_authenticated/chats/'
|
|
||||||
path: '/chats'
|
|
||||||
fullPath: '/chats'
|
|
||||||
preLoaderRoute: typeof AuthenticatedChatsIndexLazyImport
|
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
|
||||||
}
|
|
||||||
'/_authenticated/help-center/': {
|
'/_authenticated/help-center/': {
|
||||||
id: '/_authenticated/help-center/'
|
id: '/_authenticated/help-center/'
|
||||||
path: '/help-center'
|
path: '/help-center'
|
||||||
@ -449,32 +449,88 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthenticatedHelpCenterIndexLazyImport
|
preLoaderRoute: typeof AuthenticatedHelpCenterIndexLazyImport
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
}
|
}
|
||||||
'/_authenticated/nba/': {
|
'/_authenticated/dashboard/sales/coupons': {
|
||||||
id: '/_authenticated/nba/'
|
id: '/_authenticated/dashboard/sales/coupons'
|
||||||
path: '/nba'
|
path: '/dashboard/sales/coupons'
|
||||||
fullPath: '/nba'
|
fullPath: '/dashboard/sales/coupons'
|
||||||
preLoaderRoute: typeof AuthenticatedNbaIndexLazyImport
|
preLoaderRoute: typeof AuthenticatedDashboardSalesCouponsLazyImport
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
}
|
}
|
||||||
'/_authenticated/settings/': {
|
'/_authenticated/dashboard/sales/discounts': {
|
||||||
id: '/_authenticated/settings/'
|
id: '/_authenticated/dashboard/sales/discounts'
|
||||||
|
path: '/dashboard/sales/discounts'
|
||||||
|
fullPath: '/dashboard/sales/discounts'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardSalesDiscountsLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/settings/account': {
|
||||||
|
id: '/_authenticated/dashboard/settings/account'
|
||||||
|
path: '/account'
|
||||||
|
fullPath: '/dashboard/settings/account'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardSettingsAccountLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedDashboardSettingsRouteLazyImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/settings/appearance': {
|
||||||
|
id: '/_authenticated/dashboard/settings/appearance'
|
||||||
|
path: '/appearance'
|
||||||
|
fullPath: '/dashboard/settings/appearance'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardSettingsAppearanceLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedDashboardSettingsRouteLazyImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/settings/display': {
|
||||||
|
id: '/_authenticated/dashboard/settings/display'
|
||||||
|
path: '/display'
|
||||||
|
fullPath: '/dashboard/settings/display'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardSettingsDisplayLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedDashboardSettingsRouteLazyImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/settings/notifications': {
|
||||||
|
id: '/_authenticated/dashboard/settings/notifications'
|
||||||
|
path: '/notifications'
|
||||||
|
fullPath: '/dashboard/settings/notifications'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardSettingsNotificationsLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedDashboardSettingsRouteLazyImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/chats/': {
|
||||||
|
id: '/_authenticated/dashboard/chats/'
|
||||||
|
path: '/dashboard/chats'
|
||||||
|
fullPath: '/dashboard/chats'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardChatsIndexLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/products/': {
|
||||||
|
id: '/_authenticated/dashboard/products/'
|
||||||
|
path: '/dashboard/products'
|
||||||
|
fullPath: '/dashboard/products'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardProductsIndexLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/sales/': {
|
||||||
|
id: '/_authenticated/dashboard/sales/'
|
||||||
|
path: '/dashboard/sales'
|
||||||
|
fullPath: '/dashboard/sales'
|
||||||
|
preLoaderRoute: typeof AuthenticatedDashboardSalesIndexLazyImport
|
||||||
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
|
}
|
||||||
|
'/_authenticated/dashboard/settings/': {
|
||||||
|
id: '/_authenticated/dashboard/settings/'
|
||||||
path: '/'
|
path: '/'
|
||||||
fullPath: '/settings/'
|
fullPath: '/dashboard/settings/'
|
||||||
preLoaderRoute: typeof AuthenticatedSettingsIndexLazyImport
|
preLoaderRoute: typeof AuthenticatedDashboardSettingsIndexLazyImport
|
||||||
parentRoute: typeof AuthenticatedSettingsRouteLazyImport
|
parentRoute: typeof AuthenticatedDashboardSettingsRouteLazyImport
|
||||||
}
|
}
|
||||||
'/_authenticated/tasks/': {
|
'/_authenticated/dashboard/tasks/': {
|
||||||
id: '/_authenticated/tasks/'
|
id: '/_authenticated/dashboard/tasks/'
|
||||||
path: '/tasks'
|
path: '/dashboard/tasks'
|
||||||
fullPath: '/tasks'
|
fullPath: '/dashboard/tasks'
|
||||||
preLoaderRoute: typeof AuthenticatedTasksIndexLazyImport
|
preLoaderRoute: typeof AuthenticatedDashboardTasksIndexLazyImport
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
}
|
}
|
||||||
'/_authenticated/users/': {
|
'/_authenticated/dashboard/users/': {
|
||||||
id: '/_authenticated/users/'
|
id: '/_authenticated/dashboard/users/'
|
||||||
path: '/users'
|
path: '/dashboard/users'
|
||||||
fullPath: '/users'
|
fullPath: '/dashboard/users'
|
||||||
preLoaderRoute: typeof AuthenticatedUsersIndexLazyImport
|
preLoaderRoute: typeof AuthenticatedDashboardUsersIndexLazyImport
|
||||||
parentRoute: typeof AuthenticatedRouteImport
|
parentRoute: typeof AuthenticatedRouteImport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,53 +538,65 @@ declare module '@tanstack/react-router' {
|
|||||||
|
|
||||||
// Create and export the route tree
|
// Create and export the route tree
|
||||||
|
|
||||||
interface AuthenticatedSettingsRouteLazyRouteChildren {
|
interface AuthenticatedDashboardSettingsRouteLazyRouteChildren {
|
||||||
AuthenticatedSettingsAccountLazyRoute: typeof AuthenticatedSettingsAccountLazyRoute
|
AuthenticatedDashboardSettingsAccountLazyRoute: typeof AuthenticatedDashboardSettingsAccountLazyRoute
|
||||||
AuthenticatedSettingsAppearanceLazyRoute: typeof AuthenticatedSettingsAppearanceLazyRoute
|
AuthenticatedDashboardSettingsAppearanceLazyRoute: typeof AuthenticatedDashboardSettingsAppearanceLazyRoute
|
||||||
AuthenticatedSettingsDisplayLazyRoute: typeof AuthenticatedSettingsDisplayLazyRoute
|
AuthenticatedDashboardSettingsDisplayLazyRoute: typeof AuthenticatedDashboardSettingsDisplayLazyRoute
|
||||||
AuthenticatedSettingsNotificationsLazyRoute: typeof AuthenticatedSettingsNotificationsLazyRoute
|
AuthenticatedDashboardSettingsNotificationsLazyRoute: typeof AuthenticatedDashboardSettingsNotificationsLazyRoute
|
||||||
AuthenticatedSettingsIndexLazyRoute: typeof AuthenticatedSettingsIndexLazyRoute
|
AuthenticatedDashboardSettingsIndexLazyRoute: typeof AuthenticatedDashboardSettingsIndexLazyRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticatedSettingsRouteLazyRouteChildren: AuthenticatedSettingsRouteLazyRouteChildren =
|
const AuthenticatedDashboardSettingsRouteLazyRouteChildren: AuthenticatedDashboardSettingsRouteLazyRouteChildren =
|
||||||
{
|
{
|
||||||
AuthenticatedSettingsAccountLazyRoute:
|
AuthenticatedDashboardSettingsAccountLazyRoute:
|
||||||
AuthenticatedSettingsAccountLazyRoute,
|
AuthenticatedDashboardSettingsAccountLazyRoute,
|
||||||
AuthenticatedSettingsAppearanceLazyRoute:
|
AuthenticatedDashboardSettingsAppearanceLazyRoute:
|
||||||
AuthenticatedSettingsAppearanceLazyRoute,
|
AuthenticatedDashboardSettingsAppearanceLazyRoute,
|
||||||
AuthenticatedSettingsDisplayLazyRoute:
|
AuthenticatedDashboardSettingsDisplayLazyRoute:
|
||||||
AuthenticatedSettingsDisplayLazyRoute,
|
AuthenticatedDashboardSettingsDisplayLazyRoute,
|
||||||
AuthenticatedSettingsNotificationsLazyRoute:
|
AuthenticatedDashboardSettingsNotificationsLazyRoute:
|
||||||
AuthenticatedSettingsNotificationsLazyRoute,
|
AuthenticatedDashboardSettingsNotificationsLazyRoute,
|
||||||
AuthenticatedSettingsIndexLazyRoute: AuthenticatedSettingsIndexLazyRoute,
|
AuthenticatedDashboardSettingsIndexLazyRoute:
|
||||||
|
AuthenticatedDashboardSettingsIndexLazyRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticatedSettingsRouteLazyRouteWithChildren =
|
const AuthenticatedDashboardSettingsRouteLazyRouteWithChildren =
|
||||||
AuthenticatedSettingsRouteLazyRoute._addFileChildren(
|
AuthenticatedDashboardSettingsRouteLazyRoute._addFileChildren(
|
||||||
AuthenticatedSettingsRouteLazyRouteChildren,
|
AuthenticatedDashboardSettingsRouteLazyRouteChildren,
|
||||||
)
|
)
|
||||||
|
|
||||||
interface AuthenticatedRouteRouteChildren {
|
interface AuthenticatedRouteRouteChildren {
|
||||||
AuthenticatedSettingsRouteLazyRoute: typeof AuthenticatedSettingsRouteLazyRouteWithChildren
|
AuthenticatedDashboardSettingsRouteLazyRoute: typeof AuthenticatedDashboardSettingsRouteLazyRouteWithChildren
|
||||||
AuthenticatedDashboardIndexRoute: typeof AuthenticatedDashboardIndexRoute
|
AuthenticatedDashboardIndexRoute: typeof AuthenticatedDashboardIndexRoute
|
||||||
AuthenticatedAppsIndexLazyRoute: typeof AuthenticatedAppsIndexLazyRoute
|
|
||||||
AuthenticatedChatsIndexLazyRoute: typeof AuthenticatedChatsIndexLazyRoute
|
|
||||||
AuthenticatedHelpCenterIndexLazyRoute: typeof AuthenticatedHelpCenterIndexLazyRoute
|
AuthenticatedHelpCenterIndexLazyRoute: typeof AuthenticatedHelpCenterIndexLazyRoute
|
||||||
AuthenticatedNbaIndexLazyRoute: typeof AuthenticatedNbaIndexLazyRoute
|
AuthenticatedDashboardSalesCouponsLazyRoute: typeof AuthenticatedDashboardSalesCouponsLazyRoute
|
||||||
AuthenticatedTasksIndexLazyRoute: typeof AuthenticatedTasksIndexLazyRoute
|
AuthenticatedDashboardSalesDiscountsLazyRoute: typeof AuthenticatedDashboardSalesDiscountsLazyRoute
|
||||||
AuthenticatedUsersIndexLazyRoute: typeof AuthenticatedUsersIndexLazyRoute
|
AuthenticatedDashboardChatsIndexLazyRoute: typeof AuthenticatedDashboardChatsIndexLazyRoute
|
||||||
|
AuthenticatedDashboardProductsIndexLazyRoute: typeof AuthenticatedDashboardProductsIndexLazyRoute
|
||||||
|
AuthenticatedDashboardSalesIndexLazyRoute: typeof AuthenticatedDashboardSalesIndexLazyRoute
|
||||||
|
AuthenticatedDashboardTasksIndexLazyRoute: typeof AuthenticatedDashboardTasksIndexLazyRoute
|
||||||
|
AuthenticatedDashboardUsersIndexLazyRoute: typeof AuthenticatedDashboardUsersIndexLazyRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticatedRouteRouteChildren: AuthenticatedRouteRouteChildren = {
|
const AuthenticatedRouteRouteChildren: AuthenticatedRouteRouteChildren = {
|
||||||
AuthenticatedSettingsRouteLazyRoute:
|
AuthenticatedDashboardSettingsRouteLazyRoute:
|
||||||
AuthenticatedSettingsRouteLazyRouteWithChildren,
|
AuthenticatedDashboardSettingsRouteLazyRouteWithChildren,
|
||||||
AuthenticatedDashboardIndexRoute: AuthenticatedDashboardIndexRoute,
|
AuthenticatedDashboardIndexRoute: AuthenticatedDashboardIndexRoute,
|
||||||
AuthenticatedAppsIndexLazyRoute: AuthenticatedAppsIndexLazyRoute,
|
|
||||||
AuthenticatedChatsIndexLazyRoute: AuthenticatedChatsIndexLazyRoute,
|
|
||||||
AuthenticatedHelpCenterIndexLazyRoute: AuthenticatedHelpCenterIndexLazyRoute,
|
AuthenticatedHelpCenterIndexLazyRoute: AuthenticatedHelpCenterIndexLazyRoute,
|
||||||
AuthenticatedNbaIndexLazyRoute: AuthenticatedNbaIndexLazyRoute,
|
AuthenticatedDashboardSalesCouponsLazyRoute:
|
||||||
AuthenticatedTasksIndexLazyRoute: AuthenticatedTasksIndexLazyRoute,
|
AuthenticatedDashboardSalesCouponsLazyRoute,
|
||||||
AuthenticatedUsersIndexLazyRoute: AuthenticatedUsersIndexLazyRoute,
|
AuthenticatedDashboardSalesDiscountsLazyRoute:
|
||||||
|
AuthenticatedDashboardSalesDiscountsLazyRoute,
|
||||||
|
AuthenticatedDashboardChatsIndexLazyRoute:
|
||||||
|
AuthenticatedDashboardChatsIndexLazyRoute,
|
||||||
|
AuthenticatedDashboardProductsIndexLazyRoute:
|
||||||
|
AuthenticatedDashboardProductsIndexLazyRoute,
|
||||||
|
AuthenticatedDashboardSalesIndexLazyRoute:
|
||||||
|
AuthenticatedDashboardSalesIndexLazyRoute,
|
||||||
|
AuthenticatedDashboardTasksIndexLazyRoute:
|
||||||
|
AuthenticatedDashboardTasksIndexLazyRoute,
|
||||||
|
AuthenticatedDashboardUsersIndexLazyRoute:
|
||||||
|
AuthenticatedDashboardUsersIndexLazyRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticatedRouteRouteWithChildren =
|
const AuthenticatedRouteRouteWithChildren =
|
||||||
@ -540,7 +608,6 @@ export interface FileRoutesByFullPath {
|
|||||||
'/500': typeof errors500LazyRoute
|
'/500': typeof errors500LazyRoute
|
||||||
'/otp': typeof authOtpRoute
|
'/otp': typeof authOtpRoute
|
||||||
'/sign-in': typeof authSignInRoute
|
'/sign-in': typeof authSignInRoute
|
||||||
'/settings': typeof AuthenticatedSettingsRouteLazyRouteWithChildren
|
|
||||||
'/forgot-password': typeof authForgotPasswordLazyRoute
|
'/forgot-password': typeof authForgotPasswordLazyRoute
|
||||||
'/sign-in-2': typeof authSignIn2LazyRoute
|
'/sign-in-2': typeof authSignIn2LazyRoute
|
||||||
'/sign-up': typeof authSignUpLazyRoute
|
'/sign-up': typeof authSignUpLazyRoute
|
||||||
@ -548,18 +615,21 @@ export interface FileRoutesByFullPath {
|
|||||||
'/403': typeof errors403LazyRoute
|
'/403': typeof errors403LazyRoute
|
||||||
'/404': typeof errors404LazyRoute
|
'/404': typeof errors404LazyRoute
|
||||||
'/503': typeof errors503LazyRoute
|
'/503': typeof errors503LazyRoute
|
||||||
'/settings/account': typeof AuthenticatedSettingsAccountLazyRoute
|
'/dashboard/settings': typeof AuthenticatedDashboardSettingsRouteLazyRouteWithChildren
|
||||||
'/settings/appearance': typeof AuthenticatedSettingsAppearanceLazyRoute
|
|
||||||
'/settings/display': typeof AuthenticatedSettingsDisplayLazyRoute
|
|
||||||
'/settings/notifications': typeof AuthenticatedSettingsNotificationsLazyRoute
|
|
||||||
'/dashboard': typeof AuthenticatedDashboardIndexRoute
|
'/dashboard': typeof AuthenticatedDashboardIndexRoute
|
||||||
'/apps': typeof AuthenticatedAppsIndexLazyRoute
|
|
||||||
'/chats': typeof AuthenticatedChatsIndexLazyRoute
|
|
||||||
'/help-center': typeof AuthenticatedHelpCenterIndexLazyRoute
|
'/help-center': typeof AuthenticatedHelpCenterIndexLazyRoute
|
||||||
'/nba': typeof AuthenticatedNbaIndexLazyRoute
|
'/dashboard/sales/coupons': typeof AuthenticatedDashboardSalesCouponsLazyRoute
|
||||||
'/settings/': typeof AuthenticatedSettingsIndexLazyRoute
|
'/dashboard/sales/discounts': typeof AuthenticatedDashboardSalesDiscountsLazyRoute
|
||||||
'/tasks': typeof AuthenticatedTasksIndexLazyRoute
|
'/dashboard/settings/account': typeof AuthenticatedDashboardSettingsAccountLazyRoute
|
||||||
'/users': typeof AuthenticatedUsersIndexLazyRoute
|
'/dashboard/settings/appearance': typeof AuthenticatedDashboardSettingsAppearanceLazyRoute
|
||||||
|
'/dashboard/settings/display': typeof AuthenticatedDashboardSettingsDisplayLazyRoute
|
||||||
|
'/dashboard/settings/notifications': typeof AuthenticatedDashboardSettingsNotificationsLazyRoute
|
||||||
|
'/dashboard/chats': typeof AuthenticatedDashboardChatsIndexLazyRoute
|
||||||
|
'/dashboard/products': typeof AuthenticatedDashboardProductsIndexLazyRoute
|
||||||
|
'/dashboard/sales': typeof AuthenticatedDashboardSalesIndexLazyRoute
|
||||||
|
'/dashboard/settings/': typeof AuthenticatedDashboardSettingsIndexLazyRoute
|
||||||
|
'/dashboard/tasks': typeof AuthenticatedDashboardTasksIndexLazyRoute
|
||||||
|
'/dashboard/users': typeof AuthenticatedDashboardUsersIndexLazyRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
@ -575,18 +645,20 @@ export interface FileRoutesByTo {
|
|||||||
'/403': typeof errors403LazyRoute
|
'/403': typeof errors403LazyRoute
|
||||||
'/404': typeof errors404LazyRoute
|
'/404': typeof errors404LazyRoute
|
||||||
'/503': typeof errors503LazyRoute
|
'/503': typeof errors503LazyRoute
|
||||||
'/settings/account': typeof AuthenticatedSettingsAccountLazyRoute
|
|
||||||
'/settings/appearance': typeof AuthenticatedSettingsAppearanceLazyRoute
|
|
||||||
'/settings/display': typeof AuthenticatedSettingsDisplayLazyRoute
|
|
||||||
'/settings/notifications': typeof AuthenticatedSettingsNotificationsLazyRoute
|
|
||||||
'/dashboard': typeof AuthenticatedDashboardIndexRoute
|
'/dashboard': typeof AuthenticatedDashboardIndexRoute
|
||||||
'/apps': typeof AuthenticatedAppsIndexLazyRoute
|
|
||||||
'/chats': typeof AuthenticatedChatsIndexLazyRoute
|
|
||||||
'/help-center': typeof AuthenticatedHelpCenterIndexLazyRoute
|
'/help-center': typeof AuthenticatedHelpCenterIndexLazyRoute
|
||||||
'/nba': typeof AuthenticatedNbaIndexLazyRoute
|
'/dashboard/sales/coupons': typeof AuthenticatedDashboardSalesCouponsLazyRoute
|
||||||
'/settings': typeof AuthenticatedSettingsIndexLazyRoute
|
'/dashboard/sales/discounts': typeof AuthenticatedDashboardSalesDiscountsLazyRoute
|
||||||
'/tasks': typeof AuthenticatedTasksIndexLazyRoute
|
'/dashboard/settings/account': typeof AuthenticatedDashboardSettingsAccountLazyRoute
|
||||||
'/users': typeof AuthenticatedUsersIndexLazyRoute
|
'/dashboard/settings/appearance': typeof AuthenticatedDashboardSettingsAppearanceLazyRoute
|
||||||
|
'/dashboard/settings/display': typeof AuthenticatedDashboardSettingsDisplayLazyRoute
|
||||||
|
'/dashboard/settings/notifications': typeof AuthenticatedDashboardSettingsNotificationsLazyRoute
|
||||||
|
'/dashboard/chats': typeof AuthenticatedDashboardChatsIndexLazyRoute
|
||||||
|
'/dashboard/products': typeof AuthenticatedDashboardProductsIndexLazyRoute
|
||||||
|
'/dashboard/sales': typeof AuthenticatedDashboardSalesIndexLazyRoute
|
||||||
|
'/dashboard/settings': typeof AuthenticatedDashboardSettingsIndexLazyRoute
|
||||||
|
'/dashboard/tasks': typeof AuthenticatedDashboardTasksIndexLazyRoute
|
||||||
|
'/dashboard/users': typeof AuthenticatedDashboardUsersIndexLazyRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
@ -596,7 +668,6 @@ export interface FileRoutesById {
|
|||||||
'/(auth)/500': typeof auth500Route
|
'/(auth)/500': typeof auth500Route
|
||||||
'/(auth)/otp': typeof authOtpRoute
|
'/(auth)/otp': typeof authOtpRoute
|
||||||
'/(auth)/sign-in': typeof authSignInRoute
|
'/(auth)/sign-in': typeof authSignInRoute
|
||||||
'/_authenticated/settings': typeof AuthenticatedSettingsRouteLazyRouteWithChildren
|
|
||||||
'/(auth)/forgot-password': typeof authForgotPasswordLazyRoute
|
'/(auth)/forgot-password': typeof authForgotPasswordLazyRoute
|
||||||
'/(auth)/sign-in-2': typeof authSignIn2LazyRoute
|
'/(auth)/sign-in-2': typeof authSignIn2LazyRoute
|
||||||
'/(auth)/sign-up': typeof authSignUpLazyRoute
|
'/(auth)/sign-up': typeof authSignUpLazyRoute
|
||||||
@ -605,18 +676,21 @@ export interface FileRoutesById {
|
|||||||
'/(errors)/404': typeof errors404LazyRoute
|
'/(errors)/404': typeof errors404LazyRoute
|
||||||
'/(errors)/500': typeof errors500LazyRoute
|
'/(errors)/500': typeof errors500LazyRoute
|
||||||
'/(errors)/503': typeof errors503LazyRoute
|
'/(errors)/503': typeof errors503LazyRoute
|
||||||
'/_authenticated/settings/account': typeof AuthenticatedSettingsAccountLazyRoute
|
'/_authenticated/dashboard/settings': typeof AuthenticatedDashboardSettingsRouteLazyRouteWithChildren
|
||||||
'/_authenticated/settings/appearance': typeof AuthenticatedSettingsAppearanceLazyRoute
|
|
||||||
'/_authenticated/settings/display': typeof AuthenticatedSettingsDisplayLazyRoute
|
|
||||||
'/_authenticated/settings/notifications': typeof AuthenticatedSettingsNotificationsLazyRoute
|
|
||||||
'/_authenticated/dashboard/': typeof AuthenticatedDashboardIndexRoute
|
'/_authenticated/dashboard/': typeof AuthenticatedDashboardIndexRoute
|
||||||
'/_authenticated/apps/': typeof AuthenticatedAppsIndexLazyRoute
|
|
||||||
'/_authenticated/chats/': typeof AuthenticatedChatsIndexLazyRoute
|
|
||||||
'/_authenticated/help-center/': typeof AuthenticatedHelpCenterIndexLazyRoute
|
'/_authenticated/help-center/': typeof AuthenticatedHelpCenterIndexLazyRoute
|
||||||
'/_authenticated/nba/': typeof AuthenticatedNbaIndexLazyRoute
|
'/_authenticated/dashboard/sales/coupons': typeof AuthenticatedDashboardSalesCouponsLazyRoute
|
||||||
'/_authenticated/settings/': typeof AuthenticatedSettingsIndexLazyRoute
|
'/_authenticated/dashboard/sales/discounts': typeof AuthenticatedDashboardSalesDiscountsLazyRoute
|
||||||
'/_authenticated/tasks/': typeof AuthenticatedTasksIndexLazyRoute
|
'/_authenticated/dashboard/settings/account': typeof AuthenticatedDashboardSettingsAccountLazyRoute
|
||||||
'/_authenticated/users/': typeof AuthenticatedUsersIndexLazyRoute
|
'/_authenticated/dashboard/settings/appearance': typeof AuthenticatedDashboardSettingsAppearanceLazyRoute
|
||||||
|
'/_authenticated/dashboard/settings/display': typeof AuthenticatedDashboardSettingsDisplayLazyRoute
|
||||||
|
'/_authenticated/dashboard/settings/notifications': typeof AuthenticatedDashboardSettingsNotificationsLazyRoute
|
||||||
|
'/_authenticated/dashboard/chats/': typeof AuthenticatedDashboardChatsIndexLazyRoute
|
||||||
|
'/_authenticated/dashboard/products/': typeof AuthenticatedDashboardProductsIndexLazyRoute
|
||||||
|
'/_authenticated/dashboard/sales/': typeof AuthenticatedDashboardSalesIndexLazyRoute
|
||||||
|
'/_authenticated/dashboard/settings/': typeof AuthenticatedDashboardSettingsIndexLazyRoute
|
||||||
|
'/_authenticated/dashboard/tasks/': typeof AuthenticatedDashboardTasksIndexLazyRoute
|
||||||
|
'/_authenticated/dashboard/users/': typeof AuthenticatedDashboardUsersIndexLazyRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
@ -627,7 +701,6 @@ export interface FileRouteTypes {
|
|||||||
| '/500'
|
| '/500'
|
||||||
| '/otp'
|
| '/otp'
|
||||||
| '/sign-in'
|
| '/sign-in'
|
||||||
| '/settings'
|
|
||||||
| '/forgot-password'
|
| '/forgot-password'
|
||||||
| '/sign-in-2'
|
| '/sign-in-2'
|
||||||
| '/sign-up'
|
| '/sign-up'
|
||||||
@ -635,18 +708,21 @@ export interface FileRouteTypes {
|
|||||||
| '/403'
|
| '/403'
|
||||||
| '/404'
|
| '/404'
|
||||||
| '/503'
|
| '/503'
|
||||||
| '/settings/account'
|
| '/dashboard/settings'
|
||||||
| '/settings/appearance'
|
|
||||||
| '/settings/display'
|
|
||||||
| '/settings/notifications'
|
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
| '/apps'
|
|
||||||
| '/chats'
|
|
||||||
| '/help-center'
|
| '/help-center'
|
||||||
| '/nba'
|
| '/dashboard/sales/coupons'
|
||||||
| '/settings/'
|
| '/dashboard/sales/discounts'
|
||||||
| '/tasks'
|
| '/dashboard/settings/account'
|
||||||
| '/users'
|
| '/dashboard/settings/appearance'
|
||||||
|
| '/dashboard/settings/display'
|
||||||
|
| '/dashboard/settings/notifications'
|
||||||
|
| '/dashboard/chats'
|
||||||
|
| '/dashboard/products'
|
||||||
|
| '/dashboard/sales'
|
||||||
|
| '/dashboard/settings/'
|
||||||
|
| '/dashboard/tasks'
|
||||||
|
| '/dashboard/users'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to:
|
to:
|
||||||
| '/'
|
| '/'
|
||||||
@ -661,18 +737,20 @@ export interface FileRouteTypes {
|
|||||||
| '/403'
|
| '/403'
|
||||||
| '/404'
|
| '/404'
|
||||||
| '/503'
|
| '/503'
|
||||||
| '/settings/account'
|
|
||||||
| '/settings/appearance'
|
|
||||||
| '/settings/display'
|
|
||||||
| '/settings/notifications'
|
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
| '/apps'
|
|
||||||
| '/chats'
|
|
||||||
| '/help-center'
|
| '/help-center'
|
||||||
| '/nba'
|
| '/dashboard/sales/coupons'
|
||||||
| '/settings'
|
| '/dashboard/sales/discounts'
|
||||||
| '/tasks'
|
| '/dashboard/settings/account'
|
||||||
| '/users'
|
| '/dashboard/settings/appearance'
|
||||||
|
| '/dashboard/settings/display'
|
||||||
|
| '/dashboard/settings/notifications'
|
||||||
|
| '/dashboard/chats'
|
||||||
|
| '/dashboard/products'
|
||||||
|
| '/dashboard/sales'
|
||||||
|
| '/dashboard/settings'
|
||||||
|
| '/dashboard/tasks'
|
||||||
|
| '/dashboard/users'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
| '/'
|
| '/'
|
||||||
@ -680,7 +758,6 @@ export interface FileRouteTypes {
|
|||||||
| '/(auth)/500'
|
| '/(auth)/500'
|
||||||
| '/(auth)/otp'
|
| '/(auth)/otp'
|
||||||
| '/(auth)/sign-in'
|
| '/(auth)/sign-in'
|
||||||
| '/_authenticated/settings'
|
|
||||||
| '/(auth)/forgot-password'
|
| '/(auth)/forgot-password'
|
||||||
| '/(auth)/sign-in-2'
|
| '/(auth)/sign-in-2'
|
||||||
| '/(auth)/sign-up'
|
| '/(auth)/sign-up'
|
||||||
@ -689,18 +766,21 @@ export interface FileRouteTypes {
|
|||||||
| '/(errors)/404'
|
| '/(errors)/404'
|
||||||
| '/(errors)/500'
|
| '/(errors)/500'
|
||||||
| '/(errors)/503'
|
| '/(errors)/503'
|
||||||
| '/_authenticated/settings/account'
|
| '/_authenticated/dashboard/settings'
|
||||||
| '/_authenticated/settings/appearance'
|
|
||||||
| '/_authenticated/settings/display'
|
|
||||||
| '/_authenticated/settings/notifications'
|
|
||||||
| '/_authenticated/dashboard/'
|
| '/_authenticated/dashboard/'
|
||||||
| '/_authenticated/apps/'
|
|
||||||
| '/_authenticated/chats/'
|
|
||||||
| '/_authenticated/help-center/'
|
| '/_authenticated/help-center/'
|
||||||
| '/_authenticated/nba/'
|
| '/_authenticated/dashboard/sales/coupons'
|
||||||
| '/_authenticated/settings/'
|
| '/_authenticated/dashboard/sales/discounts'
|
||||||
| '/_authenticated/tasks/'
|
| '/_authenticated/dashboard/settings/account'
|
||||||
| '/_authenticated/users/'
|
| '/_authenticated/dashboard/settings/appearance'
|
||||||
|
| '/_authenticated/dashboard/settings/display'
|
||||||
|
| '/_authenticated/dashboard/settings/notifications'
|
||||||
|
| '/_authenticated/dashboard/chats/'
|
||||||
|
| '/_authenticated/dashboard/products/'
|
||||||
|
| '/_authenticated/dashboard/sales/'
|
||||||
|
| '/_authenticated/dashboard/settings/'
|
||||||
|
| '/_authenticated/dashboard/tasks/'
|
||||||
|
| '/_authenticated/dashboard/users/'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,14 +847,16 @@ export const routeTree = rootRoute
|
|||||||
"/_authenticated": {
|
"/_authenticated": {
|
||||||
"filePath": "_authenticated/route.tsx",
|
"filePath": "_authenticated/route.tsx",
|
||||||
"children": [
|
"children": [
|
||||||
"/_authenticated/settings",
|
"/_authenticated/dashboard/settings",
|
||||||
"/_authenticated/dashboard/",
|
"/_authenticated/dashboard/",
|
||||||
"/_authenticated/apps/",
|
|
||||||
"/_authenticated/chats/",
|
|
||||||
"/_authenticated/help-center/",
|
"/_authenticated/help-center/",
|
||||||
"/_authenticated/nba/",
|
"/_authenticated/dashboard/sales/coupons",
|
||||||
"/_authenticated/tasks/",
|
"/_authenticated/dashboard/sales/discounts",
|
||||||
"/_authenticated/users/"
|
"/_authenticated/dashboard/chats/",
|
||||||
|
"/_authenticated/dashboard/products/",
|
||||||
|
"/_authenticated/dashboard/sales/",
|
||||||
|
"/_authenticated/dashboard/tasks/",
|
||||||
|
"/_authenticated/dashboard/users/"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/(auth)/500": {
|
"/(auth)/500": {
|
||||||
@ -786,17 +868,6 @@ export const routeTree = rootRoute
|
|||||||
"/(auth)/sign-in": {
|
"/(auth)/sign-in": {
|
||||||
"filePath": "(auth)/sign-in.tsx"
|
"filePath": "(auth)/sign-in.tsx"
|
||||||
},
|
},
|
||||||
"/_authenticated/settings": {
|
|
||||||
"filePath": "_authenticated/settings/route.lazy.tsx",
|
|
||||||
"parent": "/_authenticated",
|
|
||||||
"children": [
|
|
||||||
"/_authenticated/settings/account",
|
|
||||||
"/_authenticated/settings/appearance",
|
|
||||||
"/_authenticated/settings/display",
|
|
||||||
"/_authenticated/settings/notifications",
|
|
||||||
"/_authenticated/settings/"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"/(auth)/forgot-password": {
|
"/(auth)/forgot-password": {
|
||||||
"filePath": "(auth)/forgot-password.lazy.tsx"
|
"filePath": "(auth)/forgot-password.lazy.tsx"
|
||||||
},
|
},
|
||||||
@ -821,52 +892,71 @@ export const routeTree = rootRoute
|
|||||||
"/(errors)/503": {
|
"/(errors)/503": {
|
||||||
"filePath": "(errors)/503.lazy.tsx"
|
"filePath": "(errors)/503.lazy.tsx"
|
||||||
},
|
},
|
||||||
"/_authenticated/settings/account": {
|
"/_authenticated/dashboard/settings": {
|
||||||
"filePath": "_authenticated/settings/account.lazy.tsx",
|
"filePath": "_authenticated/dashboard/settings/route.lazy.tsx",
|
||||||
"parent": "/_authenticated/settings"
|
"parent": "/_authenticated",
|
||||||
},
|
"children": [
|
||||||
"/_authenticated/settings/appearance": {
|
"/_authenticated/dashboard/settings/account",
|
||||||
"filePath": "_authenticated/settings/appearance.lazy.tsx",
|
"/_authenticated/dashboard/settings/appearance",
|
||||||
"parent": "/_authenticated/settings"
|
"/_authenticated/dashboard/settings/display",
|
||||||
},
|
"/_authenticated/dashboard/settings/notifications",
|
||||||
"/_authenticated/settings/display": {
|
"/_authenticated/dashboard/settings/"
|
||||||
"filePath": "_authenticated/settings/display.lazy.tsx",
|
]
|
||||||
"parent": "/_authenticated/settings"
|
|
||||||
},
|
|
||||||
"/_authenticated/settings/notifications": {
|
|
||||||
"filePath": "_authenticated/settings/notifications.lazy.tsx",
|
|
||||||
"parent": "/_authenticated/settings"
|
|
||||||
},
|
},
|
||||||
"/_authenticated/dashboard/": {
|
"/_authenticated/dashboard/": {
|
||||||
"filePath": "_authenticated/dashboard/index.tsx",
|
"filePath": "_authenticated/dashboard/index.tsx",
|
||||||
"parent": "/_authenticated"
|
"parent": "/_authenticated"
|
||||||
},
|
},
|
||||||
"/_authenticated/apps/": {
|
|
||||||
"filePath": "_authenticated/apps/index.lazy.tsx",
|
|
||||||
"parent": "/_authenticated"
|
|
||||||
},
|
|
||||||
"/_authenticated/chats/": {
|
|
||||||
"filePath": "_authenticated/chats/index.lazy.tsx",
|
|
||||||
"parent": "/_authenticated"
|
|
||||||
},
|
|
||||||
"/_authenticated/help-center/": {
|
"/_authenticated/help-center/": {
|
||||||
"filePath": "_authenticated/help-center/index.lazy.tsx",
|
"filePath": "_authenticated/help-center/index.lazy.tsx",
|
||||||
"parent": "/_authenticated"
|
"parent": "/_authenticated"
|
||||||
},
|
},
|
||||||
"/_authenticated/nba/": {
|
"/_authenticated/dashboard/sales/coupons": {
|
||||||
"filePath": "_authenticated/nba/index.lazy.tsx",
|
"filePath": "_authenticated/dashboard/sales/coupons.lazy.tsx",
|
||||||
"parent": "/_authenticated"
|
"parent": "/_authenticated"
|
||||||
},
|
},
|
||||||
"/_authenticated/settings/": {
|
"/_authenticated/dashboard/sales/discounts": {
|
||||||
"filePath": "_authenticated/settings/index.lazy.tsx",
|
"filePath": "_authenticated/dashboard/sales/discounts.lazy.tsx",
|
||||||
"parent": "/_authenticated/settings"
|
|
||||||
},
|
|
||||||
"/_authenticated/tasks/": {
|
|
||||||
"filePath": "_authenticated/tasks/index.lazy.tsx",
|
|
||||||
"parent": "/_authenticated"
|
"parent": "/_authenticated"
|
||||||
},
|
},
|
||||||
"/_authenticated/users/": {
|
"/_authenticated/dashboard/settings/account": {
|
||||||
"filePath": "_authenticated/users/index.lazy.tsx",
|
"filePath": "_authenticated/dashboard/settings/account.lazy.tsx",
|
||||||
|
"parent": "/_authenticated/dashboard/settings"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/settings/appearance": {
|
||||||
|
"filePath": "_authenticated/dashboard/settings/appearance.lazy.tsx",
|
||||||
|
"parent": "/_authenticated/dashboard/settings"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/settings/display": {
|
||||||
|
"filePath": "_authenticated/dashboard/settings/display.lazy.tsx",
|
||||||
|
"parent": "/_authenticated/dashboard/settings"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/settings/notifications": {
|
||||||
|
"filePath": "_authenticated/dashboard/settings/notifications.lazy.tsx",
|
||||||
|
"parent": "/_authenticated/dashboard/settings"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/chats/": {
|
||||||
|
"filePath": "_authenticated/dashboard/chats/index.lazy.tsx",
|
||||||
|
"parent": "/_authenticated"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/products/": {
|
||||||
|
"filePath": "_authenticated/dashboard/products/index.lazy.tsx",
|
||||||
|
"parent": "/_authenticated"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/sales/": {
|
||||||
|
"filePath": "_authenticated/dashboard/sales/index.lazy.tsx",
|
||||||
|
"parent": "/_authenticated"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/settings/": {
|
||||||
|
"filePath": "_authenticated/dashboard/settings/index.lazy.tsx",
|
||||||
|
"parent": "/_authenticated/dashboard/settings"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/tasks/": {
|
||||||
|
"filePath": "_authenticated/dashboard/tasks/index.lazy.tsx",
|
||||||
|
"parent": "/_authenticated"
|
||||||
|
},
|
||||||
|
"/_authenticated/dashboard/users/": {
|
||||||
|
"filePath": "_authenticated/dashboard/users/index.lazy.tsx",
|
||||||
"parent": "/_authenticated"
|
"parent": "/_authenticated"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
import SignIn from "@/pages/auth/sign-in";
|
import SignIn from "@/pages/auth/sign-in";
|
||||||
|
import { isLoggedIn } from "@/hooks/useAuth";
|
||||||
|
|
||||||
export const Route = createFileRoute("/(auth)/sign-in")({
|
export const Route = createFileRoute("/(auth)/sign-in")({
|
||||||
component: SignIn
|
component: SignIn,
|
||||||
|
beforeLoad: async() => {
|
||||||
|
if(isLoggedIn()) {
|
||||||
|
throw redirect({
|
||||||
|
to:"/dashboard"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
|
||||||
import Apps from "@/pages/apps";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/apps/")({
|
|
||||||
component: Apps
|
|
||||||
});
|
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import Chats from "@/pages/chats";
|
import Chats from "@/pages/chats";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/chats/")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/chats/")({
|
||||||
component: Chats
|
component: Chats
|
||||||
});
|
});
|
@ -0,0 +1,6 @@
|
|||||||
|
import Products from '@/pages/products'
|
||||||
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute('/_authenticated/dashboard/products/')({
|
||||||
|
component: Products,
|
||||||
|
})
|
@ -0,0 +1,11 @@
|
|||||||
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute(
|
||||||
|
'/_authenticated/dashboard/sales/coupons',
|
||||||
|
)({
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return <div>Hello "/_authenticated/dashboard/discounts/coupons"!</div>
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute(
|
||||||
|
'/_authenticated/dashboard/sales/discounts',
|
||||||
|
)({
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return <div>Hello "/_authenticated/dashboard/discounts/discounts"!</div>
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createLazyFileRoute(
|
||||||
|
'/_authenticated/dashboard/sales/',
|
||||||
|
)({
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return <div>Hello "/_authenticated/dashboard/discounts/"!</div>
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import SettingsAccount from "@/pages/settings/account";
|
import SettingsAccount from "@/pages/settings/account";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/settings/account")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/settings/account")({
|
||||||
component: SettingsAccount
|
component: SettingsAccount
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import SettingsAppearance from "@/pages/settings/appearance";
|
import SettingsAppearance from "@/pages/settings/appearance";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/settings/appearance")(
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/settings/appearance")(
|
||||||
{ component: SettingsAppearance }
|
{ component: SettingsAppearance }
|
||||||
);
|
);
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import SettingsDisplay from "@/pages/settings/display";
|
import SettingsDisplay from "@/pages/settings/display";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/settings/display")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/settings/display")({
|
||||||
component: SettingsDisplay
|
component: SettingsDisplay
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import SettingsProfile from "@/pages/settings/profile";
|
import SettingsProfile from "@/pages/settings/profile";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/settings/")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/settings/")({
|
||||||
component: SettingsProfile
|
component: SettingsProfile
|
||||||
});
|
});
|
@ -2,7 +2,7 @@ import { createLazyFileRoute } from "@tanstack/react-router";
|
|||||||
import SettingsNotifications from "@/pages/settings/notifications";
|
import SettingsNotifications from "@/pages/settings/notifications";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute(
|
export const Route = createLazyFileRoute(
|
||||||
"/_authenticated/settings/notifications"
|
"/_authenticated/dashboard/settings/notifications"
|
||||||
)({
|
)({
|
||||||
component: SettingsNotifications
|
component: SettingsNotifications
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import Settings from "@/pages/settings";
|
import Settings from "@/pages/settings";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/settings")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/settings")({
|
||||||
component: Settings
|
component: Settings
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import Tasks from "@/pages/tasks";
|
import Tasks from "@/pages/tasks";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/tasks/")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/tasks/")({
|
||||||
component: Tasks
|
component: Tasks
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import Users from "@/pages/users";
|
import Users from "@/pages/users";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/users/")({
|
export const Route = createLazyFileRoute("/_authenticated/dashboard/users/")({
|
||||||
component: Users
|
component: Users
|
||||||
});
|
});
|
@ -1,6 +0,0 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
|
||||||
import Nba from "@/pages/nba";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/_authenticated/nba/")({
|
|
||||||
component: Nba
|
|
||||||
});
|
|
@ -5,12 +5,13 @@ import { SearchProvider } from "@/context/search-context";
|
|||||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||||
import { AppSidebar } from "@/components/layout/app-sidebar";
|
import { AppSidebar } from "@/components/layout/app-sidebar";
|
||||||
import SkipToMain from "@/components/skip-to-main";
|
import SkipToMain from "@/components/skip-to-main";
|
||||||
|
import { isLoggedIn } from "@/hooks/useAuth";
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authenticated")({
|
export const Route = createFileRoute("/_authenticated")({
|
||||||
beforeLoad: async ({ location }) => {
|
beforeLoad: async ({ location }) => {
|
||||||
if (false) {
|
if(!isLoggedIn()) {
|
||||||
throw redirect({
|
throw redirect({
|
||||||
to: "/401",
|
to: "/sign-in",
|
||||||
search: {
|
search: {
|
||||||
redirect: location.href
|
redirect: location.href
|
||||||
}
|
}
|
||||||
|
11
scripts/generate-client.sh
Executable file
11
scripts/generate-client.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
cd backend
|
||||||
|
python -c "import app.main; import json; print(json.dumps(app.main.app.openapi()))" > ../openapi.json
|
||||||
|
cd ..
|
||||||
|
mv openapi.json frontend/
|
||||||
|
cd frontend
|
||||||
|
npm run generate-client
|
Loading…
x
Reference in New Issue
Block a user