feat: multi-module improvements across merchant, store, i18n, and customer systems
All checks were successful
All checks were successful
- Fix platform-grouped merchant sidebar menu with core items at root level - Add merchant store management (detail page, create store, team page) - Fix store settings 500 error by removing dead stripe/API tab - Move onboarding translations to module-owned locale files - Fix onboarding banner i18n with server-side rendering + context inheritance - Refactor login language selectors to use languageSelector() function (LANG-002) - Move HTTPException handling to global exception handler in merchant routes (API-003) - Add language selector to all login pages and portal headers - Fix customer module: drop order stats from customer model, add to orders module - Fix admin menu config visibility for super admin platform context - Fix storefront auth and layout issues - Add missing i18n translations for onboarding steps (en/fr/de/lb) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,9 +20,13 @@ from app.modules.tenancy.schemas import (
|
||||
MerchantPortalProfileResponse,
|
||||
MerchantPortalProfileUpdate,
|
||||
MerchantPortalStoreListResponse,
|
||||
MerchantStoreCreate,
|
||||
MerchantStoreDetailResponse,
|
||||
MerchantStoreUpdate,
|
||||
)
|
||||
from app.modules.tenancy.schemas.auth import UserContext
|
||||
from app.modules.tenancy.services.merchant_service import merchant_service
|
||||
from app.modules.tenancy.services.merchant_store_service import merchant_store_service
|
||||
|
||||
from .email_verification import email_verification_api_router
|
||||
from .merchant_auth import merchant_auth_router
|
||||
@@ -63,14 +67,113 @@ async def merchant_stores(
|
||||
db, merchant.id, skip=skip, limit=limit
|
||||
)
|
||||
|
||||
can_create, _ = merchant_store_service.can_create_store(db, merchant.id)
|
||||
|
||||
return MerchantPortalStoreListResponse(
|
||||
stores=stores,
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
can_create_store=can_create,
|
||||
)
|
||||
|
||||
|
||||
@_account_router.post("/stores", response_model=MerchantStoreDetailResponse)
|
||||
async def create_merchant_store(
|
||||
store_data: MerchantStoreCreate,
|
||||
current_user: UserContext = Depends(get_current_merchant_api),
|
||||
merchant=Depends(get_merchant_for_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Create a new store under the merchant.
|
||||
|
||||
Checks subscription tier store limits before creation.
|
||||
New stores are created with is_active=True, is_verified=False.
|
||||
"""
|
||||
# Service raises MaxStoresReachedException, StoreAlreadyExistsException,
|
||||
# or StoreValidationException — all handled by global exception handler.
|
||||
result = merchant_store_service.create_store(
|
||||
db,
|
||||
merchant.id,
|
||||
store_data.model_dump(),
|
||||
)
|
||||
db.commit()
|
||||
logger.info(
|
||||
f"Merchant {merchant.id} ({current_user.username}) created store "
|
||||
f"'{store_data.store_code}'"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@_account_router.get("/stores/{store_id}", response_model=MerchantStoreDetailResponse)
|
||||
async def get_merchant_store_detail(
|
||||
store_id: int,
|
||||
merchant=Depends(get_merchant_for_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get detailed store information with ownership validation.
|
||||
|
||||
Returns store details including platform assignments.
|
||||
"""
|
||||
# StoreNotFoundException handled by global exception handler
|
||||
return merchant_store_service.get_store_detail(db, merchant.id, store_id)
|
||||
|
||||
|
||||
@_account_router.put("/stores/{store_id}", response_model=MerchantStoreDetailResponse)
|
||||
async def update_merchant_store(
|
||||
store_id: int,
|
||||
update_data: MerchantStoreUpdate,
|
||||
current_user: UserContext = Depends(get_current_merchant_api),
|
||||
merchant=Depends(get_merchant_for_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Update store details (merchant-allowed fields only).
|
||||
|
||||
Only name, description, and contact info fields can be updated.
|
||||
"""
|
||||
# StoreNotFoundException handled by global exception handler
|
||||
result = merchant_store_service.update_store(
|
||||
db,
|
||||
merchant.id,
|
||||
store_id,
|
||||
update_data.model_dump(exclude_unset=True),
|
||||
)
|
||||
db.commit()
|
||||
logger.info(
|
||||
f"Merchant {merchant.id} ({current_user.username}) updated store {store_id}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@_account_router.get("/platforms")
|
||||
async def get_merchant_platforms(
|
||||
merchant=Depends(get_merchant_for_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get platforms available for the merchant (from active subscriptions).
|
||||
|
||||
Used by the store creation/edit UI to show platform selection options.
|
||||
"""
|
||||
return merchant_store_service.get_subscribed_platform_ids(db, merchant.id)
|
||||
|
||||
|
||||
@_account_router.get("/team")
|
||||
async def merchant_team_overview(
|
||||
merchant=Depends(get_merchant_for_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Get team members across all stores owned by the merchant.
|
||||
|
||||
Returns a list of stores with their team members grouped by store.
|
||||
"""
|
||||
return merchant_store_service.get_merchant_team_overview(db, merchant.id)
|
||||
|
||||
|
||||
@_account_router.get("/profile", response_model=MerchantPortalProfileResponse)
|
||||
async def merchant_profile(
|
||||
request: Request,
|
||||
|
||||
Reference in New Issue
Block a user