robodev 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/auth-chat/package.json +1 -1
- package/templates/backend/package.json +1 -1
- package/templates/empty/package.json +1 -1
- package/templates/marketplace/api/_lib.seed.test.ts +10 -0
- package/templates/marketplace/api/_lib.ts +114 -57
- package/templates/marketplace/api/categories.ts +1 -1
- package/templates/marketplace/api/listings/[id].ts +1 -1
- package/templates/marketplace/api/listings/paginate.ts +1 -1
- package/templates/marketplace/api/listings.ts +1 -1
- package/templates/marketplace/apps/fe/src/assets/locales/en/translation.json +1 -0
- package/templates/marketplace/apps/fe/src/assets/locales/sl/translation.json +1 -0
- package/templates/marketplace/apps/fe/src/components/features/marketplace/ListingDetailsPage.tsx +91 -49
- package/templates/marketplace/apps/fe/src/components/features/marketplace/ListingForm.tsx +278 -0
- package/templates/marketplace/apps/fe/src/components/features/marketplace/ListingFormPage.tsx +6 -222
- package/templates/marketplace/apps/fe/src/components/features/marketplace/ListingGallery.tsx +54 -0
- package/templates/marketplace/apps/fe/src/components/features/marketplace/MyListingsPage.tsx +61 -8
- package/templates/marketplace/apps/fe/src/components/features/marketplace/OrderStatusTag.tsx +24 -0
- package/templates/marketplace/apps/fe/src/components/features/marketplace/OrderSummaryCard.tsx +39 -0
- package/templates/marketplace/apps/fe/src/components/features/marketplace/OrdersPage.tsx +57 -45
- package/templates/marketplace/apps/fe/src/pages/(private)/my-listings.tsx +11 -1
- package/templates/marketplace/apps/fe/src/pages/(private)/sell/index.tsx +6 -10
- package/templates/marketplace/package.json +1 -1
- package/templates/space/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Tag } from "@povio/ui";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
|
|
4
|
+
type OrderStatus = "paid" | "shipped" | "completed";
|
|
5
|
+
|
|
6
|
+
const STATUS_COLOR: Record<OrderStatus, "warning" | "primary" | "success"> = {
|
|
7
|
+
paid: "warning",
|
|
8
|
+
shipped: "primary",
|
|
9
|
+
completed: "success",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
interface OrderStatusTagProps {
|
|
13
|
+
status: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function OrderStatusTag({ status }: OrderStatusTagProps) {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
const known = status === "paid" || status === "shipped" || status === "completed" ? status : null;
|
|
19
|
+
if (!known) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return <Tag color={STATUS_COLOR[known]}>{t(($) => $.orders.status[known])}</Tag>;
|
|
24
|
+
}
|
package/templates/marketplace/apps/fe/src/components/features/marketplace/OrderSummaryCard.tsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Button, Typography } from "@povio/ui/tanstack";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
|
|
4
|
+
import { Card } from "@/components/shared/ui/Card";
|
|
5
|
+
import type { OrderModels } from "@/openapi/order/order.models";
|
|
6
|
+
import { formatCents } from "@/utils/listing-form";
|
|
7
|
+
|
|
8
|
+
import { OrderStatusTag } from "./OrderStatusTag";
|
|
9
|
+
|
|
10
|
+
interface OrderSummaryCardProps {
|
|
11
|
+
order: OrderModels.OrderDto;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function OrderSummaryCard({ order }: OrderSummaryCardProps) {
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Card>
|
|
19
|
+
<Typography size="title-5">{order.items.map((item) => item.title).join(", ")}</Typography>
|
|
20
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
21
|
+
<OrderStatusTag status={order.status} />
|
|
22
|
+
<Typography
|
|
23
|
+
size="label-3"
|
|
24
|
+
className="text-text-default-2"
|
|
25
|
+
>
|
|
26
|
+
{formatCents(order.totalCents)}
|
|
27
|
+
</Typography>
|
|
28
|
+
</div>
|
|
29
|
+
<Button
|
|
30
|
+
size="xs"
|
|
31
|
+
variant="outlined"
|
|
32
|
+
color="secondary"
|
|
33
|
+
link={{ to: "/orders/$id", params: { id: order.id } }}
|
|
34
|
+
>
|
|
35
|
+
{t(($) => $.orders.view)}
|
|
36
|
+
</Button>
|
|
37
|
+
</Card>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Button,
|
|
3
|
-
TextArea,
|
|
4
|
-
TextInput,
|
|
5
|
-
Typography,
|
|
6
|
-
useForm,
|
|
7
|
-
useFormValue,
|
|
8
|
-
useToast,
|
|
9
|
-
} from "@povio/ui/tanstack";
|
|
1
|
+
import { Button, TextArea, TextInput, Typography, useForm, useFormValue, useToast } from "@povio/ui/tanstack";
|
|
10
2
|
import { useQueryClient } from "@tanstack/react-query";
|
|
11
3
|
import { useTranslation } from "react-i18next";
|
|
12
4
|
import { z } from "zod";
|
|
@@ -21,6 +13,9 @@ import { ReviewQueries } from "@/openapi/review/review.queries";
|
|
|
21
13
|
import { SaleQueries } from "@/openapi/sale/sale.queries";
|
|
22
14
|
import { formatCents } from "@/utils/listing-form";
|
|
23
15
|
|
|
16
|
+
import { OrderStatusTag } from "./OrderStatusTag";
|
|
17
|
+
import { OrderSummaryCard } from "./OrderSummaryCard";
|
|
18
|
+
|
|
24
19
|
export function OrdersPage() {
|
|
25
20
|
const { t } = useTranslation();
|
|
26
21
|
const { data: purchases, isLoading: loadingPurchases } = PurchaseQueries.useGetAll();
|
|
@@ -30,58 +25,59 @@ export function OrdersPage() {
|
|
|
30
25
|
<div className="flex flex-col gap-8">
|
|
31
26
|
<PageHeader title={t(($) => $.orders.title)} />
|
|
32
27
|
<section className="flex flex-col gap-3">
|
|
33
|
-
<Typography
|
|
28
|
+
<Typography
|
|
29
|
+
size="title-5"
|
|
30
|
+
variant="prominent-1"
|
|
31
|
+
>
|
|
34
32
|
{t(($) => $.orders.purchases)}
|
|
35
33
|
</Typography>
|
|
36
34
|
{loadingPurchases ? (
|
|
37
35
|
<LoadingState />
|
|
38
36
|
) : !purchases || purchases.length === 0 ? (
|
|
39
|
-
<Typography
|
|
37
|
+
<Typography
|
|
38
|
+
size="body-3"
|
|
39
|
+
className="text-text-default-2"
|
|
40
|
+
>
|
|
40
41
|
{t(($) => $.orders.emptyPurchases)}
|
|
41
42
|
</Typography>
|
|
42
43
|
) : (
|
|
43
|
-
purchases.map((order) =>
|
|
44
|
+
purchases.map((order) => (
|
|
45
|
+
<OrderSummaryCard
|
|
46
|
+
key={order.id}
|
|
47
|
+
order={order}
|
|
48
|
+
/>
|
|
49
|
+
))
|
|
44
50
|
)}
|
|
45
51
|
</section>
|
|
46
52
|
<section className="flex flex-col gap-3">
|
|
47
|
-
<Typography
|
|
53
|
+
<Typography
|
|
54
|
+
size="title-5"
|
|
55
|
+
variant="prominent-1"
|
|
56
|
+
>
|
|
48
57
|
{t(($) => $.orders.sales)}
|
|
49
58
|
</Typography>
|
|
50
59
|
{loadingSales ? (
|
|
51
60
|
<LoadingState />
|
|
52
61
|
) : !sales || sales.length === 0 ? (
|
|
53
|
-
<Typography
|
|
62
|
+
<Typography
|
|
63
|
+
size="body-3"
|
|
64
|
+
className="text-text-default-2"
|
|
65
|
+
>
|
|
54
66
|
{t(($) => $.orders.emptySales)}
|
|
55
67
|
</Typography>
|
|
56
68
|
) : (
|
|
57
|
-
sales.map((order) =>
|
|
69
|
+
sales.map((order) => (
|
|
70
|
+
<OrderSummaryCard
|
|
71
|
+
key={order.id}
|
|
72
|
+
order={order}
|
|
73
|
+
/>
|
|
74
|
+
))
|
|
58
75
|
)}
|
|
59
76
|
</section>
|
|
60
77
|
</div>
|
|
61
78
|
);
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
function OrderSummary({ order }: { order: OrderModels.OrderDto }) {
|
|
65
|
-
const { t } = useTranslation();
|
|
66
|
-
return (
|
|
67
|
-
<Card>
|
|
68
|
-
<Typography size="title-5">{order.items.map((item) => item.title).join(", ")}</Typography>
|
|
69
|
-
<Typography size="label-3" className="text-text-default-2">
|
|
70
|
-
{t(($) => $.orders.status[order.status as "paid" | "shipped" | "completed"])} ·{" "}
|
|
71
|
-
{formatCents(order.totalCents)}
|
|
72
|
-
</Typography>
|
|
73
|
-
<Button
|
|
74
|
-
size="xs"
|
|
75
|
-
variant="outlined"
|
|
76
|
-
color="secondary"
|
|
77
|
-
link={{ to: "/orders/$id", params: { id: order.id } }}
|
|
78
|
-
>
|
|
79
|
-
{order.id.slice(0, 8)}
|
|
80
|
-
</Button>
|
|
81
|
-
</Card>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
81
|
interface OrderDetailPageProps {
|
|
86
82
|
order: OrderModels.OrderDto;
|
|
87
83
|
role: "buyer" | "seller";
|
|
@@ -159,34 +155,47 @@ export function OrderDetailPage({ order, role }: OrderDetailPageProps) {
|
|
|
159
155
|
title={t(($) => $.orders.title)}
|
|
160
156
|
/>
|
|
161
157
|
<Card>
|
|
162
|
-
<
|
|
163
|
-
{t(($) => $.orders.status[order.status as "paid" | "shipped" | "completed"])}
|
|
164
|
-
</Typography>
|
|
158
|
+
<OrderStatusTag status={order.status} />
|
|
165
159
|
{order.items.map((item) => (
|
|
166
|
-
<Typography
|
|
160
|
+
<Typography
|
|
161
|
+
key={item.id}
|
|
162
|
+
size="body-3"
|
|
163
|
+
>
|
|
167
164
|
{item.qty} × {item.title} · {formatCents(item.unitPriceCents)}
|
|
168
165
|
</Typography>
|
|
169
166
|
))}
|
|
170
167
|
<Typography size="body-3">
|
|
171
168
|
{t(($) => $.orders.total)}: {formatCents(order.totalCents)}
|
|
172
169
|
</Typography>
|
|
173
|
-
<Typography
|
|
170
|
+
<Typography
|
|
171
|
+
size="label-3"
|
|
172
|
+
className="text-text-default-2"
|
|
173
|
+
>
|
|
174
174
|
{order.shippingName}, {order.shippingLine1}, {order.shippingCity}
|
|
175
175
|
</Typography>
|
|
176
176
|
{role === "seller" && order.status === "paid" ? (
|
|
177
|
-
<Button
|
|
177
|
+
<Button
|
|
178
|
+
size="xs"
|
|
179
|
+
onPress={() => void onShip()}
|
|
180
|
+
>
|
|
178
181
|
{t(($) => $.orders.ship)}
|
|
179
182
|
</Button>
|
|
180
183
|
) : null}
|
|
181
184
|
{role === "buyer" && order.status === "shipped" ? (
|
|
182
|
-
<Button
|
|
185
|
+
<Button
|
|
186
|
+
size="xs"
|
|
187
|
+
onPress={() => void onComplete()}
|
|
188
|
+
>
|
|
183
189
|
{t(($) => $.orders.complete)}
|
|
184
190
|
</Button>
|
|
185
191
|
) : null}
|
|
186
192
|
</Card>
|
|
187
193
|
|
|
188
194
|
{role === "buyer" && order.status === "completed" ? (
|
|
189
|
-
<form
|
|
195
|
+
<form
|
|
196
|
+
onSubmit={reviewForm.handleSubmit(onReview)}
|
|
197
|
+
className="flex max-w-xl flex-col gap-3"
|
|
198
|
+
>
|
|
190
199
|
<Typography size="title-5">{t(($) => $.orders.review)}</Typography>
|
|
191
200
|
<div className="flex flex-wrap gap-1">
|
|
192
201
|
{order.items.map((item) => (
|
|
@@ -214,7 +223,10 @@ export function OrderDetailPage({ order, role }: OrderDetailPageProps) {
|
|
|
214
223
|
field={{ form: reviewForm, name: "body" }}
|
|
215
224
|
label={t(($) => $.orders.reviewBody)}
|
|
216
225
|
/>
|
|
217
|
-
<Button
|
|
226
|
+
<Button
|
|
227
|
+
type="submit"
|
|
228
|
+
size="xs"
|
|
229
|
+
>
|
|
218
230
|
{t(($) => $.orders.reviewSubmit)}
|
|
219
231
|
</Button>
|
|
220
232
|
</form>
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
import { useTranslation } from "react-i18next";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
|
|
4
5
|
import { MyListingsPage } from "@/components/features/marketplace/MyListingsPage";
|
|
5
6
|
import { AppHead } from "@/components/shared/head/AppHead";
|
|
6
7
|
|
|
8
|
+
const myListingsSearchSchema = z.object({
|
|
9
|
+
create: z
|
|
10
|
+
.union([z.boolean(), z.string()])
|
|
11
|
+
.optional()
|
|
12
|
+
.transform((value) => value === true || value === "true"),
|
|
13
|
+
});
|
|
14
|
+
|
|
7
15
|
function PageComponent() {
|
|
8
16
|
const { t } = useTranslation();
|
|
17
|
+
const { create } = Route.useSearch();
|
|
9
18
|
return (
|
|
10
19
|
<>
|
|
11
20
|
<AppHead
|
|
12
21
|
title={t(($) => $.sell.head.title)}
|
|
13
22
|
description={t(($) => $.sell.head.description)}
|
|
14
23
|
/>
|
|
15
|
-
<MyListingsPage />
|
|
24
|
+
<MyListingsPage openCreate={create} />
|
|
16
25
|
</>
|
|
17
26
|
);
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
export const Route = createFileRoute("/(private)/my-listings")({
|
|
21
30
|
component: PageComponent,
|
|
31
|
+
validateSearch: myListingsSearchSchema,
|
|
22
32
|
});
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
-
import { useTranslation } from "react-i18next";
|
|
3
|
-
|
|
4
|
-
import { ListingFormPage } from "@/components/features/marketplace/ListingFormPage";
|
|
5
|
-
import { AppHead } from "@/components/shared/head/AppHead";
|
|
1
|
+
import { Navigate, createFileRoute } from "@tanstack/react-router";
|
|
6
2
|
|
|
7
3
|
function PageComponent() {
|
|
8
|
-
const { t } = useTranslation();
|
|
9
4
|
return (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
<Navigate
|
|
6
|
+
to="/my-listings"
|
|
7
|
+
search={{ create: true }}
|
|
8
|
+
replace
|
|
9
|
+
/>
|
|
14
10
|
);
|
|
15
11
|
}
|
|
16
12
|
|