Skip to content

Commit

Permalink
hide store section from non-profile owners & handle empty orders as a…
Browse files Browse the repository at this point in the history
… valid state
  • Loading branch information
ivntsng committed Nov 17, 2024
1 parent 25e12b7 commit d9e5c50
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 83 deletions.
16 changes: 12 additions & 4 deletions frontend/src/components/pages/Orders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import OrderCard from "@/components/orders/OrderCard";
import Spinner from "@/components/ui/Spinner";
import { Button } from "@/components/ui/button";
import type { paths } from "@/gen/api";
import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";
import { ApiError } from "@/lib/types/api";
import ROUTES from "@/lib/types/routes";

type OrderWithProduct =
Expand All @@ -16,6 +18,7 @@ const OrdersPage: React.FC = () => {
const { api, currentUser, isAuthenticated, isLoading } = useAuthentication();
const [orders, setOrders] = useState<OrderWithProduct[] | null>(null);
const [loadingOrders, setLoadingOrders] = useState(true);
const { addErrorAlert } = useAlertQueue();

useEffect(() => {
const fetchOrders = async () => {
Expand All @@ -26,12 +29,17 @@ const OrdersPage: React.FC = () => {
"/orders/user-orders-with-products",
);
if (error) {
console.error("Failed to fetch orders", error);
const apiError = error as ApiError;
if (apiError.status === 500) {
addErrorAlert({
message: "Failed to fetch orders",
detail: apiError.message || "An unexpected error occurred",
});
}
setOrders([]);
} else {
setOrders(data);
setOrders(data || []);
}
} catch (error) {
console.error("Error fetching orders", error);
} finally {
setLoadingOrders(false);
}
Expand Down
163 changes: 89 additions & 74 deletions frontend/src/components/pages/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ export const RenderProfile = (props: RenderProfileProps) => {
}
};

const getListingsTabLabel = () => {
return canEdit
? "Your Robot Listings"
: `${user.first_name || user.username}'s Robot Listings`;
};

return (
<div className="space-y-8 mb-12">
<Card className="w-full max-w-4xl mx-auto">
Expand Down Expand Up @@ -274,67 +280,72 @@ export const RenderProfile = (props: RenderProfileProps) => {

<Card className="w-full max-w-4xl mx-auto">
<CardHeader>
<h2 className="text-2xl font-bold">Store</h2>
{canEdit && <h2 className="text-2xl font-bold">Store</h2>}
</CardHeader>
<CardContent>
<div className="mb-4">
{user.stripe_connect &&
!user.stripe_connect.onboarding_completed ? (
<p className="text-gray-6 text-sm">
Your Stripe account setup is not complete. Please resolve
outstanding requirements to begin selling robots. It may take
some time for Stripe to process your info between submissions.
</p>
) : user.stripe_connect &&
user.stripe_connect.onboarding_completed ? (
<p className="text-gray-6 text-sm">
You are set up to sell robots on K-Scale.
</p>
) : (
<p>Complete seller onboarding to sell robots on K-Scale</p>
)}
</div>
<div className="flex sm:flex-row flex-col gap-2 mb-8">
<Tooltip content="View your orders" position="bottom">
<Button
onClick={() => navigate(ROUTES.ORDERS.path)}
variant="outline"
>
Orders
</Button>
</Tooltip>
{!user.stripe_connect ? (
<Tooltip content="Start selling on K-Scale" position="bottom">
<Button
onClick={() => navigate(ROUTES.SELL.ONBOARDING.path)}
variant="outline"
>
Start Seller Onboarding
</Button>
</Tooltip>
) : !user.stripe_connect.onboarding_completed ? (
<Tooltip content="Continue onboarding" position="bottom">
<Button
onClick={() => navigate(ROUTES.SELL.ONBOARDING.path)}
variant="outline"
>
Complete Seller Onboarding
</Button>
</Tooltip>
) : (
<Tooltip
content="Sell Robots or View Your Dashboard"
position="bottom"
>
<Button
onClick={() => navigate(ROUTES.SELL.DASHBOARD.path)}
variant="outline"
>
Seller Dashboard
</Button>
</Tooltip>
)}
</div>
{canEdit && (
<>
<div className="mb-4">
{user.stripe_connect &&
!user.stripe_connect.onboarding_completed ? (
<p className="text-gray-6 text-sm">
Your Stripe account setup is not complete. Please resolve
outstanding requirements to begin selling robots. It may
take some time for Stripe to process your info between
submissions.
</p>
) : user.stripe_connect &&
user.stripe_connect.onboarding_completed ? (
<p className="text-gray-6 text-sm">
You are set up to sell robots on K-Scale.
</p>
) : (
<p>Complete seller onboarding to sell robots on K-Scale</p>
)}
</div>
<div className="flex sm:flex-row flex-col gap-2 mb-8">
<Tooltip content="View your orders" position="bottom">
<Button
onClick={() => navigate(ROUTES.ORDERS.path)}
variant="outline"
>
Orders
</Button>
</Tooltip>
{!user.stripe_connect ? (
<Tooltip content="Start selling on K-Scale" position="bottom">
<Button
onClick={() => navigate(ROUTES.SELL.ONBOARDING.path)}
variant="outline"
>
Start Seller Onboarding
</Button>
</Tooltip>
) : !user.stripe_connect.onboarding_completed ? (
<Tooltip content="Continue onboarding" position="bottom">
<Button
onClick={() => navigate(ROUTES.SELL.ONBOARDING.path)}
variant="outline"
>
Complete Seller Onboarding
</Button>
</Tooltip>
) : (
<Tooltip
content="Sell Robots or View Your Dashboard"
position="bottom"
>
<Button
onClick={() => navigate(ROUTES.SELL.DASHBOARD.path)}
variant="outline"
>
Seller Dashboard
</Button>
</Tooltip>
)}
</div>
</>
)}
<div className="flex flex-col items-center space-y-4">
<Tabs
defaultValue="own"
Expand All @@ -352,28 +363,32 @@ export const RenderProfile = (props: RenderProfileProps) => {
value="own"
className="data-[state=active]:bg-gray-3"
>
Your Robot Listings
{getListingsTabLabel()}
</TabsTrigger>
</Button>
<Button
variant="outline"
asChild
className={`text-xs sm:text-sm px-2 sm:px-4 hover:bg-gray-11 ${value === "upvoted" ? "border text-gray-12" : ""}`}
>
<TabsTrigger
value="upvoted"
className="data-[state=active]:bg-gray-3"
{canEdit && (
<Button
variant="outline"
asChild
className={`text-xs sm:text-sm px-2 sm:px-4 hover:bg-gray-11 ${value === "upvoted" ? "border text-gray-12" : ""}`}
>
Upvoted Robots
</TabsTrigger>
</Button>
<TabsTrigger
value="upvoted"
className="data-[state=active]:bg-gray-3"
>
Upvoted Robots
</TabsTrigger>
</Button>
)}
</TabsList>
<TabsContent value="own">
<MyListingGrid userId={user.id} />
</TabsContent>
<TabsContent value="upvoted">
<UpvotedGrid page={upvotedPage} setPage={setUpvotedPage} />
</TabsContent>
{canEdit && (
<TabsContent value="upvoted">
<UpvotedGrid page={upvotedPage} setPage={setUpvotedPage} />
</TabsContent>
)}
</Tabs>
</div>
</CardContent>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface ApiValidationError {
}

export interface ApiError {
status: number;
message: string;
detail: string | ApiValidationError[];
}

Expand Down
24 changes: 19 additions & 5 deletions store/app/routers/orders.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Defines the router endpoints for handling Orders."""

from logging import getLogger
from typing import Annotated, Dict, List

from fastapi import APIRouter, Depends, HTTPException, status
Expand All @@ -14,6 +15,8 @@

router = APIRouter()

logger = getLogger(__name__)


class ProductInfo(BaseModel):
id: str
Expand Down Expand Up @@ -77,13 +80,24 @@ async def get_user_orders_with_products(
orders = await crud.get_orders_by_user_id(user.id)
orders_with_products = []
for order in orders:
if order.stripe_product_id is None:
continue # Skip orders without a stripe_product_id
product = await stripe.get_product(order.stripe_product_id, crud)
orders_with_products.append(OrderWithProduct(order=order, product=ProductInfo(**product)))
try:
if order.stripe_product_id is None:
continue
product = await stripe.get_product(order.stripe_product_id, crud)
orders_with_products.append(OrderWithProduct(order=order, product=ProductInfo(**product)))
except Exception as e:
logger.error(
"Error processing order", extra={"order_id": order.id, "error": str(e), "user_id": user.id}
)
continue
return orders_with_products
except ItemNotFoundError:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No orders found for this user")
return []
except Exception as e:
logger.error("Error fetching user orders", extra={"user_id": user.id, "error": str(e)})
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error fetching orders: {str(e)}"
)


class UpdateOrderAddressRequest(BaseModel):
Expand Down

0 comments on commit d9e5c50

Please sign in to comment.