Skip to content

Commit 834c9c0

Browse files
committed
feat(product): add product post and list and info
1 parent a3d3d5d commit 834c9c0

File tree

15 files changed

+284
-47
lines changed

15 files changed

+284
-47
lines changed

src/app/(default)/marketplace/page.tsx

+35-5
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,47 @@
22

33
import {MarketTitle} from "@/component/market/marketTitle";
44
import {useEffect, useState} from "react";
5-
import {marketplace_data, MarketplaceData} from "@/app/(default)/marketplace/data";
65
import {MarketItem} from "@/component/market/marketItem";
6+
import {ProductList, ProductListParam} from "@/server/types";
7+
import {ProductApi} from "@/server/ProductApi";
8+
import {AppWrite} from "@/server/Client";
79

810
export default function MarketPlacePage(){
9-
const [ItemData,setItemData] = useState<MarketplaceData[]>([])
11+
const [ItemData,setItemData] = useState<ProductList[]>([])
12+
const [Query,setQuery] = useState<ProductListParam>({
13+
limit: 50,
14+
page: 1,
15+
order: "created_at",
16+
search: ""
17+
} as ProductListParam
18+
)
19+
const [Tags, setTags] = useState<string[]>([]);
20+
const api = new ProductApi();
1021
useEffect(() => {
11-
setItemData(marketplace_data)
12-
}, []);
22+
api
23+
.List(
24+
Query.limit,
25+
Query.page,
26+
Query.order,
27+
Query.search
28+
)
29+
.then((data) => {
30+
if (data.status === 200 && data.data) {
31+
const json: AppWrite<ProductList[]> = JSON.parse(data.data);
32+
if (json.code === 200 && json.data) {
33+
setItemData(json.data)
34+
setTags(json.data.map((item) => {
35+
return item.data.type.split(",").map((tag) => {
36+
return tag
37+
})
38+
}).flat())
39+
}
40+
}
41+
})
42+
}, [Query]);
1343
return (
1444
<div className="market">
15-
<MarketTitle/>
45+
<MarketTitle tags={Tags} query={setQuery}/>
1646
<div className="market-display">
1747
{
1848
ItemData.map((item,index) => {

src/app/(default)/r/[owner]/[repo]/(default)/layout.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default function RepoLayout(props: { children: React.ReactNode, params: P
5252
setNotFound(true);
5353
return;
5454
}
55+
const products = json.data.products;
5556
setRepo(json.data);
5657
let branches: BranchModel[] = [];
5758
context.setRepoCtx({
@@ -60,6 +61,7 @@ export default function RepoLayout(props: { children: React.ReactNode, params: P
6061
repoName: repo,
6162
repoInfo: json.data,
6263
branches: branches,
64+
products: products
6365
})
6466
const branchesRes = await api.Bhtc(owner, repo);
6567
if (branchesRes.status !== 200 || !branchesRes.data) {
@@ -91,6 +93,7 @@ export default function RepoLayout(props: { children: React.ReactNode, params: P
9193
repoName: repo,
9294
repoInfo: json.data,
9395
branches: branches,
96+
products: products
9497
})
9598
}
9699

src/app/(default)/r/[owner]/[repo]/post/page.tsx

+57-23
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,18 @@ import {Form, useForm} from "@mantine/form";
66
import {CommitModel, DataProductPostParam} from "@/server/types";
77
import {RepoApi} from "@/server/RepoApi";
88
import {AppWrite} from "@/server/Client";
9-
import {Box, Button, Divider, Input, InputLabel, LoadingOverlay, Select} from "@mantine/core";
9+
import {
10+
Box,
11+
Button,
12+
Divider,
13+
Input,
14+
InputLabel,
15+
LoadingOverlay,
16+
Pill,
17+
PillGroup, PillsInput,
18+
PillsInputField,
19+
Select
20+
} from "@mantine/core";
1021
import {notifications} from "@mantine/notifications";
1122
import {ProductApi} from "@/server/ProductApi";
1223

@@ -76,22 +87,19 @@ export default function ProductPostPage(){
7687
}
7788
return null;
7889
},
79-
type: (value) => {
80-
if (!value) {
81-
return "Type is required";
82-
}
83-
return null;
84-
},
8590
price: (value) => {
86-
if (!value) {
87-
return "Price is required";
91+
if (value % 1 !== 0 && value !== 0) {
92+
return "Price is invalid, it is integer";
8893
}
8994
if (value < 0){
9095
return "Price is invalid, it is eqt 0";
9196
}
9297
},
9398
}
9499
});
100+
101+
const [Topics,setTopics] = useState<string[]>([]);
102+
const [Topic,setTopic] = useState<string>("");
95103
const onSubmit = () => {
96104
if (Publishing) return;
97105
setPublishing(true);
@@ -109,18 +117,19 @@ export default function ProductPostPage(){
109117
name: form.getValues().name,
110118
description: form.getValues().description,
111119
license: form.getValues().license,
112-
price: form.getValues().price,
120+
price: Number(form.getValues().price),
113121
hash: form.getValues().hash,
114-
type: form.getValues().type,
122+
type: Topics.join(","),
115123
}
124+
console.log(param);
116125
product_api.Post(Parma!.owner, Parma!.repo, param)
117126
.then((res) => {
118127
if (res.status === 200) {
119128
const json:AppWrite<string> = JSON.parse(res.data);
120-
if (json.code === 200 && json.data) {
129+
if (json.code === 200 ) {
121130
notifications.show({
122131
title: "Success",
123-
message: "Post success"
132+
message: "Post Jobs Submit success, Please You wait"
124133
})
125134
setTimeout(() => {
126135
window.location.href = "/r/" + Parma!.owner + "/" + Parma!.repo;
@@ -139,7 +148,7 @@ export default function ProductPostPage(){
139148
POST
140149
</h1>
141150
<Box pos="relative">
142-
<LoadingOverlay visible={Publishing} loaderProps={{ children: 'Forking...' }} />
151+
<LoadingOverlay visible={Publishing} loaderProps={{ children: 'Publish...' }} />
143152
<Form
144153
form={form}
145154
id="LayoutModelRepositoryPOST"
@@ -158,7 +167,7 @@ export default function ProductPostPage(){
158167
}
159168
})
160169
}
161-
{...form.getInputProps("owner")}
170+
{...form.getInputProps("hash")}
162171
/>
163172
<InputLabel className="w-full">
164173
name<a style={{color: 'red'}}> *</a>
@@ -208,14 +217,39 @@ export default function ProductPostPage(){
208217
</InputLabel>
209218
<InputLabel className="w-full">
210219
type<a style={{color: 'red'}}> *</a>
211-
<Input
212-
name="type"
213-
placeholder="Enter product type"
214-
type="tel"
215-
aria-autocomplete={"none"}
216-
key={form.key("type")}
217-
{...form.getInputProps("type")}
218-
/>
220+
<PillsInput>
221+
<PillGroup>
222+
{Topics.map((item, index) => (
223+
<Pill key={index}>{item}</Pill>
224+
))}
225+
<PillsInputField
226+
value={Topic}
227+
placeholder="Add a Type"
228+
onChange={(e) => {
229+
setTopic(e.target.value)
230+
}}
231+
onKeyDown={(e) => {
232+
if (e.key === "Enter") {
233+
if (Topic.length > 0) {
234+
setTopics([...Topics, Topic])
235+
setTopic("")
236+
}
237+
}
238+
if (e.key === "Backspace") {
239+
if (Topic.length === 0) {
240+
setTopics(Topics.slice(0, -1))
241+
}
242+
}
243+
if (e.key === "Tab") {
244+
if (Topic.length > 0) {
245+
setTopics([...Topics, Topic])
246+
setTopic("")
247+
}
248+
}
249+
}}
250+
/>
251+
</PillGroup>
252+
</PillsInput>
219253
</InputLabel>
220254
<Divider/>
221255
<div style={{

src/app/(default)/u/[username]/(default)/page.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import React, {useEffect, useState} from "react";
66
import {AppWrite} from "@/server/Client";
77
import {UserDashBored} from "@/server/types";
88
import {UserApi} from "@/server/UserApi";
9+
import {ProductList} from "@/component/user/productlist";
910

1011
export default function UserPage(props: { params: Promise<{ username: string}> }) {
1112
const api = new UserApi();
@@ -38,7 +39,7 @@ export default function UserPage(props: { params: Promise<{ username: string}> }
3839
<UserRepoList userDash={userDash}/>
3940
</TabsPanel>
4041
<TabsPanel value="product">
41-
product
42+
<ProductList userDash={userDash}/>
4243
</TabsPanel>
4344
<TabsPanel value="team">
4445
team

src/component/market/marketItem.tsx

+24-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import {MarketplaceData} from "@/app/(default)/marketplace/data";
1+
import {ProductList} from "@/server/types";
22

33
export interface MarketItemProps {
4-
data: MarketplaceData;
4+
data: ProductList;
55
}
66

77

88
export const MarketItem = ({data}: MarketItemProps) => {
9+
const product = data.data;
10+
const owner = data.owner;
11+
// const repo = data.repo;
912
return (
1013
<div className="market-item">
1114

@@ -14,14 +17,28 @@ export const MarketItem = ({data}: MarketItemProps) => {
1417
display: "flex",
1518
gap: "1rem"
1619
}}>
17-
<img src={data.image} alt={data.name}/>
18-
<span>{data.name}</span>
20+
<img style={{
21+
borderRadius: "50%",
22+
}} src={owner.avatar || ""} alt={data.owner.uid}/>
23+
<span>{product.name}</span>
1924
</div>
20-
<span className="price">{data.price}</span>
25+
<span className="price">{product.price === 0 ? <a style={{
26+
color: "green"
27+
}}>
28+
Free
29+
</a> : <a style={{
30+
color: "red"
31+
}}>
32+
{`$${product.price}`}
33+
</a>}</span>
2134
</div>
2235
<div className="market-item-info">
23-
<p>{data.description.substring(0,50)} {data.description.length > 50 ? "..." : ""}</p>
24-
<p className="tags">{data.tags.map((tag) => <span key={tag}>{tag}</span>)}</p>
36+
{
37+
product.description && (
38+
<p>{product.description.substring(0,50)} {product.description.length > 50 ? "..." : ""}</p>
39+
)
40+
}
41+
<p className="tags">{product.type.split(",").map((tag) => <span key={tag}>{tag}</span>)}</p>
2542
</div>
2643
</div>
2744
)

src/component/market/marketTitle.tsx

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import {Button, Input, MultiSelect, Select} from "@mantine/core";
2+
import {ProductListParam} from "@/server/types";
23

3-
export const MarketTitle = () => {
4+
interface MarketTitleProps {
5+
tags: string[],
6+
query: (value: (((prevState: ProductListParam) => ProductListParam) | ProductListParam)) => void
7+
}
8+
9+
export const MarketTitle = ({tags, query}: MarketTitleProps) => {
410
return (
511
<div className="market-title">
612
<div className="market-image">
@@ -25,7 +31,7 @@ export const MarketTitle = () => {
2531
<a>Sort</a>
2632
<Select
2733
defaultValue={'Most popular'}
28-
data={['Most popular', 'Recently Updated', 'Most relevant', 'Most expensive','Most Cheapest']}
34+
data={['Most popular', 'Recently Updated', 'Most relevant', 'Most expensive', 'Most Cheapest']}
2935
/>
3036
</div>
3137
<div style={{
@@ -36,7 +42,15 @@ export const MarketTitle = () => {
3642
<a>Type</a>
3743
<MultiSelect
3844
defaultValue={['All']}
39-
data={['All', 'Data', 'Code', 'Book', 'Image', 'Video', 'Audio',]}
45+
data={['All',...tags]}
46+
onChange={(value) => {
47+
query((prev) => {
48+
return {
49+
...prev,
50+
search: value.join(',')
51+
}
52+
})
53+
}}
4054
/>
4155
</div>
4256
</div>

src/component/repo/fileaction.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {BranchModel, CommitModel, Repository} from "@/server/types";
33
import {useDisclosure} from "@mantine/hooks";
44
import {CloneModel} from "@/component/repo/clone";
55
import dayjs from "dayjs";
6+
import {useRouter} from "next/navigation";
67

78
interface FileActionProps {
89
branch: BranchModel[],
@@ -15,6 +16,7 @@ interface FileActionProps {
1516

1617
export const FileAction = ({branch, default_branch, echange, repo, owner, head}: FileActionProps) => {
1718
const [opened, {open, close}] = useDisclosure(false);
19+
const nav = useRouter().replace;
1820
const relative_time = () => {
1921
if (head) {
2022
const date = new Date(Number(head.time) * 1000);
@@ -70,7 +72,7 @@ export const FileAction = ({branch, default_branch, echange, repo, owner, head}:
7072
</div>
7173
<div className="file-action-right">
7274
<Button onClick={open}>Clone</Button>
73-
<Button>Publish</Button>
75+
<Button onClick={()=>nav(`/r/${owner}/${repo.name}/post`)}>Publish</Button>
7476
<div className="cmt">
7577
<a>{head.id.substring(0, 7)} {relative_time()}</a>
7678
<a>{repo.nums_commit} commits</a>

src/component/repo/repointro.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {Button, Input} from "@mantine/core";
77
import ClipboardJS from "clipboard";
88
import {notifications} from "@mantine/notifications";
99
import {FaRegClipboard} from "react-icons/fa";
10+
import usePageContext from "@/store/usePageContext";
1011

1112
interface RepoIntroProps {
1213
repo: Repository,
@@ -20,6 +21,7 @@ export const RepoIntro = ({repo, owner, head}: RepoIntroProps) => {
2021
const api = new RepoApi();
2122
const http = "https://" + window.location.host + "/git/" + owner + "/" + repo.name + ".git"
2223
const ssh = "git@" + window.location.host.split(":")[0] + ":" + owner + "/" + repo.name + ".git";
24+
const context = usePageContext();
2325
const Init = async () => {
2426
const readme = await api.File(owner, repo.name, "README.md",head.id);
2527
if (readme.status === 200 && readme.data) {
@@ -142,6 +144,16 @@ export const RepoIntro = ({repo, owner, head}: RepoIntroProps) => {
142144
</span>
143145
<a>{repo.nums_release}</a>
144146
</div>
147+
{
148+
context.repoCtx && (
149+
<div className="repo-intro-card-list-item">
150+
<span>
151+
Product
152+
</span>
153+
<a>{context.repoCtx.products.length}</a>
154+
</div>
155+
)
156+
}
145157
</div>
146158

147159
</div>

0 commit comments

Comments
 (0)