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:
Thastertyn 2025-03-16 22:24:54 +01:00
parent d87c280109
commit d0690f2f06
67 changed files with 3389 additions and 1066 deletions

2
.gitignore vendored
View File

@ -293,3 +293,5 @@ cython_debug/
#.idea/ #.idea/
!frontend/src/lib !frontend/src/lib
frontend/openapi.json

View File

@ -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
} }

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
View File

30
frontend/biome.json Normal file
View 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"
}
}
}

View 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
}
]
});

File diff suppressed because it is too large Load Diff

View File

@ -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",

View 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;
}
}

View 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;
};

View 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;
};

View 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;
}
}

View 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(),
},
};

View 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);
}
});
};

View 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';

View 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/'
});
}
}

View 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);

View File

@ -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>
);
}

View 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;

View File

@ -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>
); );

View File

@ -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
} }
] ]

View File

@ -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>

View File

@ -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>

View File

@ -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}

View 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 };

View File

@ -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";

View File

@ -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;

View File

@ -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: {

View File

@ -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">

View File

@ -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>

View File

@ -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"

View File

@ -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 platforms purpose. */}
</div> </div>
</> </>
); );

View File

@ -1,3 +0,0 @@
export default function Nba() {
return <h1>NBA Testing</h1>;
}

View File

@ -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("");

View File

@ -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>
); );

View File

@ -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"
} }
]; ];

View File

@ -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>

View 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"
} }
} }

View File

@ -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"
})
}
}
}); });

View File

@ -1,6 +0,0 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Apps from "@/pages/apps";
export const Route = createLazyFileRoute("/_authenticated/apps/")({
component: Apps
});

View File

@ -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
}); });

View File

@ -0,0 +1,6 @@
import Products from '@/pages/products'
import { createLazyFileRoute } from '@tanstack/react-router'
export const Route = createLazyFileRoute('/_authenticated/dashboard/products/')({
component: Products,
})

View File

@ -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>
}

View File

@ -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>
}

View File

@ -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>
}

View File

@ -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
}); });

View File

@ -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 }
); );

View File

@ -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
}); });

View File

@ -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
}); });

View File

@ -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
}); });

View File

@ -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
}); });

View File

@ -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
}); });

View File

@ -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
}); });

View File

@ -1,6 +0,0 @@
import { createLazyFileRoute } from "@tanstack/react-router";
import Nba from "@/pages/nba";
export const Route = createLazyFileRoute("/_authenticated/nba/")({
component: Nba
});

View File

@ -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
View 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