Minor tweaks to sign up on frontend and database structure

This commit is contained in:
Thastertyn 2025-03-17 19:27:09 +01:00
parent d0690f2f06
commit 260bef98cd
10 changed files with 56 additions and 1037 deletions

View File

@ -1,18 +1,26 @@
from typing import Optional
from fastapi import APIRouter 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.crud.shop_crud import create_shop
from app.schemas.shop_schemas import ShopCreate from app.schemas.shop_schemas import ShopCreate, ShopPublic
from app.schemas.user_schemas import Token
router = APIRouter(prefix="/shop", tags=["Dashboard"]) router = APIRouter(prefix="/shop", tags=["Dashboard"])
@router.post("", status_code=201) @router.post("", status_code=201)
def register_new_shop( async def register_new_shop(
session: SessionDep, session: SessionDep,
current_user: CurrentOwnerUser, current_user: CurrentOwnerUser,
shop_data: ShopCreate shop_data: ShopCreate
) -> bool: ) -> bool:
create_shop(session, shop_data, current_user) create_shop(session, shop_data, current_user)
return True return True
@router.get("")
async def get_owned_shop_info(
current_user: CurrentOwnerUser
) -> Optional[ShopPublic]:
return current_user.owned_shop

View File

@ -31,7 +31,7 @@ def create_shop(session: Session, shop_data: ShopCreate, creator: User) -> None:
description=shop_data.description, description=shop_data.description,
status=ShopStatus.INACTIVE, status=ShopStatus.INACTIVE,
contact_email=creator.email, contact_email=creator.email,
phone_number=creator.phone_number, contact_phone_number=creator.phone_number,
currency=shop_data.currency currency=shop_data.currency
) )
session.add(new_shop) session.add(new_shop)

View File

@ -21,7 +21,7 @@ class ShopLinkEntry(BaseModel):
class ShopLinks(BaseModel): class ShopLinks(BaseModel):
links: list[ShopLinkEntry] links: list[ShopLinkEntry] = []
class ShopBusinessHourEntry(BaseModel): class ShopBusinessHourEntry(BaseModel):
@ -31,15 +31,15 @@ class ShopBusinessHourEntry(BaseModel):
class ShopBusinessHours(BaseModel): class ShopBusinessHours(BaseModel):
hours: list[ShopBusinessHourEntry] hours: list[ShopBusinessHourEntry] = []
class ShopAddress(SQLModel): class ShopAddress(SQLModel):
street: str = Field(max_length=255, nullable=False) street: str = Field(default="", max_length=255, nullable=False)
city: str = Field(max_length=100, nullable=False) city: str = Field(default="", max_length=100, nullable=False)
state: str = Field(max_length=100, nullable=True) state: Optional[str] = Field(default=None, max_length=100, nullable=True)
postal_code: str = Field(max_length=20, nullable=False) postal_code: str = Field(default="", max_length=20, nullable=False)
country: str = Field(max_length=100, nullable=False) country: str = Field(default="", max_length=100, nullable=False)
class Shop(SQLModel, table=True): class Shop(SQLModel, table=True):
@ -55,13 +55,19 @@ class Shop(SQLModel, table=True):
status: ShopStatus = Field(sa_column=Column(Enum(ShopStatus))) status: ShopStatus = Field(sa_column=Column(Enum(ShopStatus)))
logo: Optional[str] = Field(max_length=100) logo: Optional[str] = Field(max_length=100)
contact_email: str = Field(max_length=128, nullable=False, unique=True) contact_email: str = Field(max_length=128, nullable=False, unique=True)
phone_number: str = Field(max_length=15, nullable=False) contact_phone_number: str = Field(max_length=15, nullable=False)
address: ShopAddress = Field(sa_column=Column(JSONB, nullable=False, default=lambda: {})) address: ShopAddress = Field(
business_hours: ShopBusinessHours = Field(sa_column=Column(JSONB, nullable=False, default=lambda: {})) sa_column=Column(JSONB, nullable=False, default=lambda: ShopAddress().dict())
links: ShopLinks = Field(sa_column=Column(JSONB, nullable=False, default=lambda: {})) )
business_hours: ShopBusinessHours = Field(
sa_column=Column(JSONB, nullable=False, default=lambda: ShopBusinessHours().dict())
)
links: ShopLinks = Field(
sa_column=Column(JSONB, nullable=False, default=lambda: ShopLinks().dict())
)
currency: str = Field(max_length=3, nullable=False) currency: str = Field(max_length=3, nullable=False)
owner: Optional["User"] = Relationship( owner: "User" = Relationship(
back_populates='owned_shop', back_populates='owned_shop',
sa_relationship_kwargs={"foreign_keys": "[Shop.owner_id]"} sa_relationship_kwargs={"foreign_keys": "[Shop.owner_id]"}
) )
@ -71,11 +77,5 @@ class Shop(SQLModel, table=True):
sa_relationship_kwargs={"foreign_keys": "[User.shop_id]"} sa_relationship_kwargs={"foreign_keys": "[User.shop_id]"}
) )
# __table_args__ = (
# CheckConstraint("business_hours ? 'hours'", name="check_business_hours_keys"),
# CheckConstraint("links ? 'links'", name="check_links_keys"),
# CheckConstraint("address ? 'address'", name="check_address_keys")
# )
__all__ = ["Shop", "ShopBusinessHours", "ShopLinks", "ShopStatus"] __all__ = ["Shop", "ShopBusinessHours", "ShopLinks", "ShopStatus"]

View File

@ -31,7 +31,7 @@ class User(SQLModel, table=True):
password: str = Field(max_length=60, nullable=False) password: str = Field(max_length=60, nullable=False)
first_name: Optional[str] = Field(max_length=64) first_name: Optional[str] = Field(max_length=64)
last_name: Optional[str] = Field(max_length=64) last_name: Optional[str] = Field(max_length=64)
phone_number: str = Field(max_length=15, nullable=False) phone_number: str = Field(max_length=16, nullable=False)
profile_picture: Optional[str] = Field(max_length=100) profile_picture: Optional[str] = Field(max_length=100)
created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) created_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)

View File

@ -1,14 +1,27 @@
from pydantic import EmailStr from datetime import datetime
from sqlmodel import Field, SQLModel from pydantic import EmailStr, BaseModel, Field
from sqlmodel import SQLModel
from app.database.models.shop_model import ShopAddress from app.database.models.shop_model import ShopAddress, ShopStatus, ShopBusinessHours, ShopLinks
class ShopCreate(SQLModel): class ShopCreate(BaseModel):
name: str = Field(max_length=100, nullable=False, unique=True) name: str = Field(max_length=100, nullable=False, unique=True)
description: str = Field(max_length=500, nullable=False) description: str = Field(max_length=500, nullable=False)
currency: str = Field(max_length=3, nullable=False) currency: str = Field(max_length=3, nullable=False)
contact_email: EmailStr = Field() contact_email: EmailStr = Field()
contact_phone_number: str = Field(min_length=2, max_length=16, schema_extra={"pattern": r'^\+[1-9]\d{1,14}$'}) contact_phone_number: str = Field(min_length=2, max_length=16, pattern=r'^\+[1-9]\d{1,14}$')
address: ShopAddress address: ShopAddress
class ShopPublic(BaseModel):
name: str
description: str
currency: str
contact_email: EmailStr
contact_phone_number: str
address: ShopAddress
business_hours: ShopBusinessHours
links: ShopLinks
status: ShopStatus

View File

@ -0,0 +1 @@
VITE_API_URL=

File diff suppressed because it is too large Load Diff

View File

View File

@ -14,10 +14,9 @@ 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 { registerUser } from "@/lib/api";
import { ApiError } from "@/errors/api-error";
import useAuth from "@/hooks/useAuth"; import useAuth from "@/hooks/useAuth";
import { PhoneInput } from "@/components/ui/phone-number-input"; import { PhoneInput } from "@/components/ui/phone-number-input";
import { toast } from "@/hooks/useToast";
type SignUpFormProps = HTMLAttributes<HTMLDivElement>; type SignUpFormProps = HTMLAttributes<HTMLDivElement>;
@ -68,8 +67,6 @@ const formSchema = z
export function SignUpForm({ className, ...props }: SignUpFormProps) { export function SignUpForm({ className, ...props }: SignUpFormProps) {
const { signUpMutation } = useAuth(); const { signUpMutation } = useAuth();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const form = useForm<z.infer<typeof formSchema>>({ const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
@ -84,17 +81,11 @@ export function SignUpForm({ className, ...props }: SignUpFormProps) {
async function onSubmit(data: z.infer<typeof formSchema>) { async function onSubmit(data: z.infer<typeof formSchema>) {
setIsLoading(true); setIsLoading(true);
setError(null); signUpMutation.mutate(data);
setSuccessMessage(null); toast({
title: "Successfully registered"
try { });
signUpMutation.mutate(data); setIsLoading(false);
setSuccessMessage("Account created successfully!");
} catch (error: unknown) {
setError(error instanceof ApiError ? error.response : "Something went wrong.");
} finally {
setIsLoading(false);
}
} }
return ( return (
@ -167,10 +158,6 @@ export function SignUpForm({ className, ...props }: SignUpFormProps) {
</FormItem> </FormItem>
)} )}
/> />
{error && <p className="text-red-500">{error}</p>}
{successMessage && (
<p className="text-green-500">{successMessage}</p>
)}
<Button className="mt-2" type="submit" disabled={isLoading}> <Button className="mt-2" type="submit" disabled={isLoading}>
{isLoading ? "Creating Account..." : "Create Account"} {isLoading ? "Creating Account..." : "Create Account"}
</Button> </Button>

View File

@ -12,11 +12,5 @@ export default defineConfig({
alias: { alias: {
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),
}, },
},
server: {
port: 31713
},
preview: {
port: 37173
} }
}) })