feat: public StorefrontDetailView for customer store URL and products
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 35s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 35s
This commit is contained in:
parent
6fee9fc665
commit
4589b86f1d
2 changed files with 72 additions and 1 deletions
|
|
@ -5,7 +5,8 @@ from .views import (
|
|||
RegisterView, LoginView, LogoutView, VerifyOtpView, ProfileView,
|
||||
ProductViewSet, BulkUploadView, BulkUploadLogsView, BulkUploadErrorReportView, OrderViewSet, ReturnRequestViewSet,
|
||||
WalletView, WithdrawView, SubmitGstinView, PresignedUrlView, MockUploadView, MockDownloadView,
|
||||
UploadEAadhaarView, VerifyPANView, VerifyBankView, SendWhatsAppOtpView, SendEmailOtpView, ProductSearchView, DashboardStatsView
|
||||
UploadEAadhaarView, VerifyPANView, VerifyBankView, SendWhatsAppOtpView, SendEmailOtpView, ProductSearchView, DashboardStatsView,
|
||||
StorefrontDetailView
|
||||
)
|
||||
from .logs_views import LogsView, LogStatsView, LogClearView
|
||||
|
||||
|
|
@ -64,5 +65,9 @@ urlpatterns = [
|
|||
path('shipping/reverse/label/', ReverseLabelView.as_view(), name='shipping-reverse-label'),
|
||||
path('shipping/reverse/update-qc/', ReverseUpdateQCView.as_view(), name='shipping-reverse-update-qc'),
|
||||
|
||||
# Public Storefront Endpoints
|
||||
path('store/<str:slug>/', StorefrontDetailView.as_view(), name='store-detail'),
|
||||
path('storefront/<str:slug>/', StorefrontDetailView.as_view(), name='storefront-detail'),
|
||||
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
|
|
|
|||
66
api/views.py
66
api/views.py
|
|
@ -2641,3 +2641,69 @@ class ProductSearchView(APIView):
|
|||
except Exception as err:
|
||||
logger.error(f"Fallback Product Search Error: {err}", exc_info=True)
|
||||
return Response({'error': str(err), 'results': [], 'count': 0}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
class StorefrontDetailView(APIView):
|
||||
"""
|
||||
Public API endpoint to retrieve a seller's storefront details and products by store_slug.
|
||||
Accessible by customers opening tradhox.com/store/<slug>/
|
||||
GET /api/store/<slug>/
|
||||
GET /api/storefront/<slug>/
|
||||
"""
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def get(self, request, slug):
|
||||
clean_slug = str(slug or '').strip()
|
||||
if not clean_slug:
|
||||
return Response({'error': 'Store slug is required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Look up profile by store_slug, or fallback to username or store_name
|
||||
profile = (
|
||||
SupplierProfile.objects.filter(store_slug__iexact=clean_slug).first()
|
||||
or SupplierProfile.objects.filter(user__username__iexact=clean_slug).first()
|
||||
or SupplierProfile.objects.filter(store_name__iexact=clean_slug).first()
|
||||
)
|
||||
|
||||
if not profile:
|
||||
return Response({'error': 'Storefront not found', 'slug': clean_slug}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# Build S3 / CDN URLs for banner and logo
|
||||
banner_url = get_document_url(profile.banner_s3_key, request) if profile.banner_s3_key else (profile.banner_url or '')
|
||||
logo_url = get_document_url(profile.logo_s3_key, request) if profile.logo_s3_key else (profile.store_logo or profile.artisan_avatar or '')
|
||||
|
||||
location = ", ".join([part for part in [profile.city, profile.state] if part]).strip()
|
||||
if not location:
|
||||
location = profile.workshop_location or "India"
|
||||
|
||||
# Fetch active products for this seller
|
||||
products_qs = Product.objects.filter(supplier=profile.user, is_active=True).order_by('-id')
|
||||
products_data = ProductSerializer(products_qs, many=True).data
|
||||
|
||||
# Ensure product image URLs are properly resolved
|
||||
for p in products_data:
|
||||
img = p.get('image')
|
||||
if img and not str(img).startswith(('http://', 'https://', 'data:')):
|
||||
p['image'] = get_document_url(img, request)
|
||||
|
||||
categories = [c.name for c in profile.categories.all()]
|
||||
|
||||
store_data = {
|
||||
'id': profile.user.id,
|
||||
'name': profile.store_name or profile.user.username,
|
||||
'slug': profile.store_slug or clean_slug,
|
||||
'tagline': profile.featured_tagline or profile.business_bio or "Authentic Handcrafted Treasures from Master Artisans",
|
||||
'bio': profile.business_bio or "Generational master artisanal workshop preserving authentic Indian heritage crafts.",
|
||||
'banner_url': banner_url,
|
||||
'logo_url': logo_url,
|
||||
'location': location,
|
||||
'craft_lineage_years': profile.craft_lineage_years,
|
||||
'is_verified': profile.is_verified or profile.status == 'approved',
|
||||
'categories': categories,
|
||||
'total_products': len(products_data),
|
||||
}
|
||||
|
||||
return Response({
|
||||
'status': 'success',
|
||||
'store': store_data,
|
||||
'products': products_data
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
|
|
|||
Loading…
Reference in a new issue