Skip to content

Commit

Permalink
Clean tech debt (#521)
Browse files Browse the repository at this point in the history
* Update contributing with stripe instructions

* Cleaned up route naming conventions and refined profil and admin UI

* Robot listingID clickable on creation
  • Loading branch information
Winston-Hsiao authored Oct 31, 2024
1 parent 220f9d1 commit 12671d2
Show file tree
Hide file tree
Showing 16 changed files with 171 additions and 86 deletions.
21 changes: 20 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ You can contribute to the K-Scale Store project in various ways, such as reporti
4. [Syncing Frontend and Backend](#syncing-frontend-and-backend)
5. [React Setup](#react-setup)
6. [Testing](#testing)
7. [Stripe Setup](#stripe-setup)

---

Expand Down Expand Up @@ -143,27 +144,33 @@ DYNAMO_ENDPOINT=http://127.0.0.1:4566 dynamodb-admin
Create a Python virtual environment using [uv](https://astral.sh/blog/uv) or [virtualenv](https://virtualenv.pypa.io/en/latest/) with **Python 3.11 or later**:

1. **Using `uv`**:

```bash
uv venv .venv --python 3.11
```

2. **Using `virtualenv`**: If you choose to use `virtualenv`, ensure that **Python 3.11** is installed on your machine. You can check if it's installed by running:

```bash
python3.11 --version
```

If Python 3.11 is installed, create the virtual environment with:

```bash
python3.11 -m venv .venv
```

**Note**: If Python 3.11 is not installed on your machine, you will need to install it before proceeding. For macOS, you can install Python 3.11 using `Homebrew`:

```bash
brew install [email protected]
```

### Activate the virtual environment:
### Activate the virtual environment:

Once the virtual environment is created, activate it:

```bash
source .venv/bin/activate
```
Expand Down Expand Up @@ -249,6 +256,18 @@ make test-frontend # Run only the frontend tests
make test-backend # Run only the backend tests
```

## Stripe Setup

Run this to recieve stripe webhooks locally:

```bash
stripe listen --forward-to localhost:8080/stripe/webhook
```

Make sure to set the `STRIPE_WEBHOOK_SECRET` environment variable to the value
shown in the terminal and source it to the terminal you are running
`make start-backend` in.

## Optional

Install pre-commit from [here](https://pre-commit.com/) to run the formatting and static checks automatically when you commit.
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

# K-Scale Store

This is the code for our online store. This is a simple store for buying and selling robots and robot parts.
This is the codebase for our online store.

This site contains:

- a marketplace for buying and selling robots and robot parts
- a developer terminal for managing and interacting with your robots

If you would like to contribute, see the [CONTRIBUTING.md](CONTRIBUTING.md) file and check out the [project tracker](https://github.com/orgs/kscalelabs/projects/8/views/1).
2 changes: 1 addition & 1 deletion frontend/src/components/modals/CancelOrderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";

type Order =
paths["/orders/get_user_orders"]["get"]["responses"][200]["content"]["application/json"][0];
paths["/orders/user-orders"]["get"]["responses"][200]["content"]["application/json"][0];

interface CancelOrderModalProps {
isOpen: boolean;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/modals/EditAddressModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";

type Order =
paths["/orders/get_user_orders"]["get"]["responses"][200]["content"]["application/json"][0];
paths["/orders/user-orders"]["get"]["responses"][200]["content"]["application/json"][0];

interface EditAddressModalProps {
isOpen: boolean;
Expand Down Expand Up @@ -50,7 +50,7 @@ const EditAddressModal: React.FC<EditAddressModalProps> = ({
e.preventDefault();
try {
const { data, error } = await client.PUT(
"/orders/update_order_address/{order_id}",
"/orders/update-order-address/{order_id}",
{
params: { path: { order_id: order.id } },
body: address,
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/components/modals/RegisterRobotModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { ApiError } from "@/lib/types/api";
import { Plus } from "lucide-react";
import { ExternalLink, Plus } from "lucide-react";

interface RegisterRobotModalProps {
isOpen: boolean;
Expand Down Expand Up @@ -145,7 +145,14 @@ export function RegisterRobotModal({
</div>
</div>
{error && <div className="text-red-500 text-sm mt-2">{error}</div>}
<div className="flex justify-end">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Button
onClick={() => window.open("/browse", "_blank")}
variant="default"
>
<span className="mr-2">Browse Listings</span>
<ExternalLink className="h-3 w-3" />
</Button>
<Button
onClick={handleAdd}
disabled={!name || !listingId || isLoading}
Expand Down
14 changes: 8 additions & 6 deletions frontend/src/components/orders/OrderCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import { formatPrice } from "@/lib/utils/formatNumber";
import { normalizeStatus } from "@/lib/utils/formatString";

type OrderWithProduct =
paths["/orders/get_user_orders_with_products"]["get"]["responses"][200]["content"]["application/json"][0];
paths["/orders/user-orders-with-products"]["get"]["responses"][200]["content"]["application/json"][0];

type Order =
paths["/orders/get_user_orders"]["get"]["responses"][200]["content"]["application/json"][0];
paths["/orders/user-orders"]["get"]["responses"][200]["content"]["application/json"][0];

const orderStatuses = [
"processing",
Expand Down Expand Up @@ -77,9 +77,9 @@ const OrderCard: React.FC<{ orderWithProduct: OrderWithProduct }> = ({
};

return (
<div className="bg-white shadow-md rounded-lg p-4 md:p-6 mb-4 w-full">
<div className="bg-white 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 text-lg sm:text-xl">
<p className="text-gray-11 mb-2 sm:text-lg">
Status:{" "}
<span className={`font-semibold ${getTextColor(order.status)}`}>
{normalizeStatus(order.status)}
Expand Down Expand Up @@ -110,9 +110,11 @@ const OrderCard: React.FC<{ orderWithProduct: OrderWithProduct }> = ({
</DropdownMenuContent>
</DropdownMenu>
</div>
<p>Quantity: {order.quantity}x</p>
<p className="text-gray-12">Quantity: {order.quantity}</p>
<p>
<span className="font-medium">{formatPrice(order.amount)}</span>{" "}
<span className="text-gray-12 font-medium">
{formatPrice(order.amount)}
</span>{" "}
<span className="font-light">
= {formatPrice(unitPrice)} x {order.quantity}
</span>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/pages/Orders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { paths } from "@/gen/api";
import { useAuthentication } from "@/hooks/useAuth";

type OrderWithProduct =
paths["/orders/get_user_orders_with_products"]["get"]["responses"][200]["content"]["application/json"][0];
paths["/orders/user-orders-with-products"]["get"]["responses"][200]["content"]["application/json"][0];

const OrdersPage: React.FC = () => {
const navigate = useNavigate();
Expand All @@ -22,7 +22,7 @@ const OrdersPage: React.FC = () => {
setLoadingOrders(true);
try {
const { data, error } = await api.client.GET(
"/orders/get_user_orders_with_products",
"/orders/user-orders-with-products",
);
if (error) {
console.error("Failed to fetch orders", error);
Expand Down
100 changes: 56 additions & 44 deletions frontend/src/components/pages/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,22 @@ export const RenderProfile = (props: RenderProfileProps) => {
<div className="space-y-8 mb-12">
<Card className="w-full max-w-4xl mx-auto">
<CardHeader className="flex flex-col items-center space-y-4">
<h1 className="text-3xl font-bold text-primary-9">
{user.first_name || user.last_name
? `${user.first_name || ""} ${user.last_name || ""}`
: "No name set"}
</h1>
<p className="text-sm text-gray-11">
Joined on{" "}
{user.created_at ? formatJoinDate(user.created_at) : "Unknown date"}
</p>
<p className="text-sm text-gray-11">Username: {user.username}</p>
<div className="flex flex-col items-center space-y-2 mb-4">
<h1 className="text-3xl font-bold text-primary-9">
{user.first_name || user.last_name
? `${user.first_name || ""} ${user.last_name || ""}`
: "No name set"}
</h1>
<p className="text-sm text-gray-1 bg-gray-10 px-3 py-1 rounded-md">
@{user.username}
</p>
<p className="text-sm text-gray-11">
Joined on{" "}
{user.created_at
? formatJoinDate(user.created_at)
: "Unknown date"}
</p>
</div>
{!isEditing && canEdit && (
<div className="flex space-x-2">
<Button onClick={() => navigate("/keys")} variant="primary">
Expand All @@ -166,7 +172,7 @@ export const RenderProfile = (props: RenderProfileProps) => {
</Button>
</div>
)}
{isAdmin && (
{isAdmin && !canEdit && (
<Button onClick={handleSetModerator} variant="outline">
{user.permissions?.includes("is_mod")
? "Remove Moderator"
Expand All @@ -188,6 +194,10 @@ export const RenderProfile = (props: RenderProfileProps) => {
>
Username
</label>
<p className="text-xs text-gray-10 italic">
Changing your username will change the URL for all your
posted listings.
</p>
<Input
id="username"
type="text"
Expand Down Expand Up @@ -216,36 +226,38 @@ export const RenderProfile = (props: RenderProfileProps) => {
)}
</div>

<div>
<label
htmlFor="first_name"
className="block text-lg font-medium"
>
First Name
</label>
<Input
id="first_name"
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
className="mt-1 block w-full"
/>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label
htmlFor="first_name"
className="block text-lg font-medium"
>
First Name
</label>
<Input
id="first_name"
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
className="mt-1 block w-full"
/>
</div>

<div>
<label
htmlFor="last_name"
className="block text-lg font-medium"
>
Last Name
</label>
<Input
id="last_name"
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
className="mt-1 block w-full"
/>
<div>
<label
htmlFor="last_name"
className="block text-lg font-medium"
>
Last Name
</label>
<Input
id="last_name"
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
className="mt-1 block w-full"
/>
</div>
</div>

<div>
Expand All @@ -256,8 +268,8 @@ export const RenderProfile = (props: RenderProfileProps) => {
id="bio"
value={bio}
onChange={(e) => setBio(e.target.value)}
className="mt-1 block w-full rounded-md border-gray-11 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
rows={4}
className="mt-1 block w-full rounded-md border-gray-11 shadow-sm focus:border-primary-9 focus:ring focus:ring-primary-9 focus:ring-opacity-50"
rows={3}
/>
</div>

Expand Down Expand Up @@ -316,13 +328,13 @@ export const RenderProfile = (props: RenderProfileProps) => {
<TabsList className="flex justify-center space-x-4 mb-4">
<TabsTrigger
value="own"
className="px-4 py-2 rounded-lg transition-colors duration-300 hover:bg-gray-8 data-[state=active]:bg-primary-9 data-[state=active]:text-white"
className="px-3 py-1.5 rounded-md transition-colors duration-300 hover:bg-gray-11 hover:text-gray-1 data-[state=active]:bg-primary-9 data-[state=active]:text-gray-1"
>
Overview
</TabsTrigger>
<TabsTrigger
value="upvoted"
className="px-4 py-2 rounded-lg transition-colors duration-300 hover:bg-gray-8 data-[state=active]:bg-primary-9 data-[state=active]:text-white"
className="px-3 py-1.5 rounded-md transition-colors duration-300 hover:bg-gray-11 hover:text-gray-1 data-[state=active]:bg-primary-9 data-[state=active]:text-gray-1"
>
Upvoted
</TabsTrigger>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/pages/StompyPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const StompyPro: React.FC = () => {
"Regular software updates",
],
price: 1600000,
// productId: "prod_Qyzd8f0gFMis7c", // developer
productId: "prod_R0n3nkCO4aQdlg", // production
productId: "prod_Qyzd8f0gFMis7c", // developer
// productId: "prod_R0n3nkCO4aQdlg", // production
};

return (
Expand Down
31 changes: 27 additions & 4 deletions frontend/src/components/pages/Terminal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

import Spinner from "@/components/ui/Spinner";
import { Button } from "@/components/ui/button";
Expand All @@ -23,7 +22,6 @@ type ListingInfo = {
};

const TerminalPage: React.FC = () => {
const navigate = useNavigate();
const { api, currentUser, isAuthenticated, isLoading } = useAuthentication();
const [robots, setRobots] = useState<Robot[] | null>(null);
const [loadingRobots, setLoadingRobots] = useState(true);
Expand Down Expand Up @@ -110,6 +108,26 @@ const TerminalPage: React.FC = () => {
throw error;
}

// Fetch the listing info for the newly created robot
const { data: listingData, error: listingError } = await api.client.GET(
"/listings/batch",
{
params: { query: { ids: [robotData.listing_id] } },
},
);

if (!listingError && listingData.listings.length > 0) {
const listing = listingData.listings[0];
setListingInfos((prev) => ({
...prev,
[listing.id]: {
id: listing.id,
username: listing.username || "",
slug: listing.slug,
},
}));
}

setRobots((prev) => (prev ? [...prev, data] : [data]));
setIsRegisterModalOpen(false);
} catch (error) {
Expand Down Expand Up @@ -259,8 +277,13 @@ const TerminalPage: React.FC = () => {
<p className="text-gray-12 font-medium sm:text-lg">
No robots yet. Link your robot to a listing to get started.
</p>
<Button onClick={() => navigate("/browse")} variant="default">
Browse Listings
<Button
variant="primary"
onClick={() => setIsRegisterModalOpen(true)}
className="flex items-center"
>
<Plus className="mr-2 h-4 w-4" />
<span className="mr-2">Register Robot</span>
</Button>
</div>
)}
Expand Down
Loading

0 comments on commit 12671d2

Please sign in to comment.