Skip to content

Commit

Permalink
Listings for sale improvements (#585)
Browse files Browse the repository at this point in the history
* Listings for sale UI improvements

* Fixed accidental quantity changes

* UI improvements

* Decrement listing quantity on purchase, affirm for orders over , and fix quantity 1 checkout

* Small UI Improvements
  • Loading branch information
Winston-Hsiao authored Nov 11, 2024
1 parent 985761b commit 3aee0ef
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 210 deletions.
2 changes: 2 additions & 0 deletions frontend/src/components/listing/ListingPayment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const ListingPayment = ({
listingId={listingId}
stripeProductId={stripeProductId}
label="Purchase Now"
inventoryType={inventoryType}
inventoryQuantity={inventoryQuantity}
/>
</div>
</div>
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/components/listing/ListingRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {
createdAt={createdAt}
/>

<hr className="border-gray-2 my-4" />

<ListingDescription
listingId={listingId}
description={description}
edit={canEdit}
/>

{/* Add payment section if price exists */}
{isForSale && (
<>
Expand All @@ -93,14 +101,6 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {
</>
)}

<hr className="border-gray-2 my-4" />

<ListingDescription
listingId={listingId}
description={description}
edit={canEdit}
/>

<hr className="border-gray-200 my-4" />

{/* Build this robot */}
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/components/listings/ListingGridCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { paths } from "@/gen/api";
import { formatPrice } from "@/lib/utils/formatNumber";

import { RenderDescription } from "../listing/ListingDescription";

Expand Down Expand Up @@ -59,6 +60,20 @@ const ListingGridCard = ({
/>
</div>
)}
{listing?.price_amount && (
<div className="mt-2 text-sm text-gray-300">
{formatPrice(listing.price_amount)}
{listing.inventory_type === "finite" &&
listing.inventory_quantity !== null && (
<span className="ml-2 text-gray-400">
({listing.inventory_quantity} available)
</span>
)}
{listing.inventory_type === "preorder" && (
<span className="ml-2 text-gray-400">(Pre-order)</span>
)}
</div>
)}
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/nav/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const Navbar = () => {
variant="ghost"
className="px-3 py-2 text-gray-1"
>
<Link to={ROUTES.LOGIN.path}>Sign In</Link>
<Link to={ROUTES.LOGIN.path}>Log In</Link>
</Button>
<Button
asChild
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/orders/OrderCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const OrderCard: React.FC<{ orderWithProduct: OrderWithProduct }> = ({
return (
<div className="bg-gray-1 shadow-md rounded-lg p-4 md:p-6 w-full">
<h2 className="text-gray-12 font-bold text-2xl mb-1">{product.name}</h2>
<p className="text-gray-11 mb-2 sm:text-lg">
<p className="text-gray-11 mb-1 sm:text-lg">
Status:{" "}
<span className={`font-semibold ${getTextColor(order.status)}`}>
{normalizeStatus(order.status)}
Expand Down Expand Up @@ -199,8 +199,8 @@ const OrderCard: React.FC<{ orderWithProduct: OrderWithProduct }> = ({
</p>
)}

<div className="mt-4 text-sm bg-gray-3 p-3 rounded-md text-gray-12">
<h3 className="font-semibold text-lg">Shipping Address</h3>
<div className="mt-4 text-xs bg-gray-3 p-3 rounded-md text-gray-12">
<h3 className="font-semibold">Shipping Address</h3>
<p>{order.shipping_name}</p>
<p>{order.shipping_address_line1}</p>
{order.shipping_address_line2 && <p>{order.shipping_address_line2}</p>}
Expand Down
43 changes: 33 additions & 10 deletions frontend/src/components/pages/CreateSell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const CreateSell = () => {

useEffect(() => {
if (auth.currentUser && slug) {
setPreviewUrl(`/item/${auth.currentUser.username}/${slug}`);
setPreviewUrl(`/bot/${auth.currentUser.username}/${slug}`);
}
}, [auth.currentUser, slug]);

Expand Down Expand Up @@ -309,6 +309,35 @@ const CreateSell = () => {
return true;
};

const handleInventoryTypeChange = (
e: React.ChangeEvent<HTMLSelectElement>,
) => {
const newType = e.target.value as typeof inventoryType;
setInventoryType(newType);
setValue("inventory_type", newType);

// Reset related fields based on inventory type
if (newType === "infinite") {
setValue("inventory_quantity", null);
setValue("preorder_release_date", null);
} else if (newType === "preorder") {
setValue("inventory_quantity", null);
setIsReservation(false);
setValue("is_reservation", false);
setValue("reservation_deposit_amount", null);
} else if (newType === "finite") {
setValue("preorder_release_date", null);
}

// Trigger validation
trigger([
"inventory_quantity",
"preorder_release_date",
"is_reservation",
"reservation_deposit_amount",
]);
};

return (
<RequireAuthentication>
<Container className="max-w-xl">
Expand Down Expand Up @@ -453,15 +482,8 @@ const CreateSell = () => {
<select
className="w-full p-2 rounded-md border border-gray-7 bg-gray-3 text-gray-12"
{...register("inventory_type")}
onChange={(e) => {
setInventoryType(
e.target.value as typeof inventoryType,
);
setValue(
"inventory_type",
e.target.value as typeof inventoryType,
);
}}
onChange={handleInventoryTypeChange}
value={inventoryType}
>
<option value="infinite">Unlimited</option>
<option value="finite">Limited Quantity</option>
Expand All @@ -479,6 +501,7 @@ const CreateSell = () => {
<Input
type="number"
min="1"
onWheel={(e) => (e.target as HTMLInputElement).blur()} // prevent changing value when scrolling
{...register("inventory_quantity", {
valueAsNumber: true,
validate: (value) =>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/pages/CreateShare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const CreateShare = () => {

useEffect(() => {
if (auth.currentUser && slug) {
setPreviewUrl(`/item/${auth.currentUser.username}/${slug}`);
setPreviewUrl(`/bot/${auth.currentUser.username}/${slug}`);
}
}, [auth.currentUser, slug]);

Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/pages/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useNavigate } from "react-router-dom";

import AuthBlock from "@/components/auth/AuthBlock";

const Login = () => {
const navigate = useNavigate();

return (
<div className="mx-8">
<div className="flex justify-center items-center">
<AuthBlock title={<span className="text-gray-2">Welcome back!</span>} />
<AuthBlock title="Welcome back!" onClosed={() => navigate(-1)} />
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/pages/OrderSuccess.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const OrderSuccess: React.FC = () => {

return (
<div className="pt-4 min-h-screen">
<Card className="mt-8">
<Card className="mt-8 max-w-4xl mx-auto">
<CardContent className="p-6 flex flex-col items-center">
<CheckCircle className="w-16 h-16 text-green-500 mb-4" />
<h2 className="text-2xl font-bold mb-2">Order Successful!</h2>
Expand All @@ -37,7 +37,7 @@ const OrderSuccess: React.FC = () => {
<Button asChild>
<a href="/">Return to Home</a>
</Button>
<Button asChild variant="default">
<Button asChild variant="outline">
<a href="/orders">View Your Orders</a>
</Button>
</div>
Expand Down
142 changes: 70 additions & 72 deletions frontend/src/components/pages/SellerOnboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

import RequireAuthentication from "@/components/auth/RequireAuthentication";
import Spinner from "@/components/ui/Spinner";
import { Button } from "@/components/ui/button";
import { useAlertQueue } from "@/hooks/useAlertQueue";
Expand Down Expand Up @@ -40,19 +41,14 @@ export default function SellerOnboarding() {

if (auth.isLoading) {
return (
<div className="container mx-auto px-4 py-8">
<div className="mx-auto px-4 py-8">
<div className="flex justify-center max-w-2xl mx-auto">
<Spinner />
</div>
</div>
);
}

if (!auth.isAuthenticated) {
navigate(ROUTES.LOGIN.path);
return null;
}

const handleCreateAccount = async () => {
try {
setAccountCreatePending(true);
Expand Down Expand Up @@ -81,73 +77,75 @@ export default function SellerOnboarding() {
const showStripeConnect = connectedAccountId && stripeConnectInstance;

return (
<div className="container mx-auto px-4 py-8 min-h-screen">
<div className="max-w-2xl mx-auto">
<h1 className="text-3xl font-bold my-8">Start Selling on K-Scale</h1>

{!connectedAccountId && (
<div className="mb-8">
<p className="mb-4">
Set up your K-Scale connected Stripe account to start selling
robots and receiving payments.
</p>

<Button
onClick={handleCreateAccount}
disabled={accountCreatePending}
variant="outline"
>
{accountCreatePending
? "Creating account..."
: "Start seller onboarding"}
</Button>
</div>
)}

{showStripeConnect && stripeConnectInstance && (
<ConnectComponentsProvider connectInstance={stripeConnectInstance}>
<ConnectAccountOnboarding
onExit={() => {
setOnboardingExited(true);
auth.fetchCurrentUser();
if (auth.currentUser?.stripe_connect_onboarding_completed) {
navigate(ROUTES.SELL.DASHBOARD.path);
}
}}
/>
</ConnectComponentsProvider>
)}

{(connectedAccountId || accountCreatePending || onboardingExited) && (
<div className="mt-10 p-3 rounded-lg text-sm">
{connectedAccountId && (
<div className="flex flex-col gap-2">
<span className="font-semibold">
Complete the onboarding process to start selling robots.
</span>
<div className="flex items-center gap-2">
Connected account ID:{" "}
<code className="font-mono bg-gray-11 rounded-md p-1">
{connectedAccountId}
</code>
<RequireAuthentication>
<div className="mx-auto min-h-screen">
<div className="max-w-3xl mx-auto bg-gray-2 text-gray-12 py-4 px-10 rounded-md">
<h1 className="text-3xl font-bold my-8">Start Selling on K-Scale</h1>

{!connectedAccountId && (
<div className="mb-8">
<p className="mb-4">
Set up your K-Scale connected Stripe account to start selling
robots and receiving payments.
</p>

<Button
onClick={handleCreateAccount}
disabled={accountCreatePending}
variant="outline"
>
{accountCreatePending
? "Creating account..."
: "Start seller onboarding"}
</Button>
</div>
)}

{showStripeConnect && stripeConnectInstance && (
<ConnectComponentsProvider connectInstance={stripeConnectInstance}>
<ConnectAccountOnboarding
onExit={() => {
setOnboardingExited(true);
auth.fetchCurrentUser();
if (auth.currentUser?.stripe_connect_onboarding_completed) {
navigate(ROUTES.SELL.DASHBOARD.path);
}
}}
/>
</ConnectComponentsProvider>
)}

{(connectedAccountId || accountCreatePending || onboardingExited) && (
<div className="mt-10 p-3 rounded-lg text-sm">
{connectedAccountId && (
<div className="flex flex-col gap-2">
<span className="font-semibold">
Complete the onboarding process to start selling robots.
</span>
<div className="flex items-center gap-2">
Connected account ID:{" "}
<code className="font-mono bg-gray-5 rounded-md p-1">
{connectedAccountId}
</code>
</div>
<span className="font-semibold">
This usually takes a few steps/submissions.
</span>
<span className="font-light">
It may take some time for Stripe to process your info
between submissions. Continue through your account page or
refresh this page to check/update your application status.
</span>
</div>
<span className="font-semibold">
This usually takes a few steps/submissions.
</span>
<span className="font-light">
It may take some time for Stripe to process your info between
submissions. Continue through your account page or refresh
this page to check/update your application status.
</span>
</div>
)}
{accountCreatePending && (
<p>Creating a K-Scale Stripe connected account...</p>
)}
{onboardingExited && <p>Account setup completed</p>}
</div>
)}
)}
{accountCreatePending && (
<p>Creating a K-Scale Stripe connected account...</p>
)}
{onboardingExited && <p>Account setup completed</p>}
</div>
)}
</div>
</div>
</div>
</RequireAuthentication>
);
}
Loading

0 comments on commit 3aee0ef

Please sign in to comment.