Skip to content

Commit 0a53f56

Browse files
Merge pull request #5 from websurferdoteth/remove-categories
Remove categories
2 parents 2607baf + 097795f commit 0a53f56

File tree

7 files changed

+57
-110
lines changed

7 files changed

+57
-110
lines changed

packages/indexer/ponder.schema.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export const listings = onchainTable("listings", (t) => ({
1919
// Denormalized from metadata
2020
title: t.text(),
2121
description: t.text(),
22-
category: t.text(),
2322
image: t.text(),
2423
contact: t.json(),
2524
tags: t.json(),

packages/indexer/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ ponder.on("Marketplace:ListingCreated" as any, async ({ event, context }) => {
208208
// Top-level denormalized fields for convenient querying & UI
209209
title: typeof json?.title === "string" ? json.title : null,
210210
description: typeof json?.description === "string" ? json.description : null,
211-
category: typeof json?.category === "string" ? json.category : null,
212211
image: typeof json?.image === "string" ? json.image : null,
213212
contact: typeof json?.contact === "object" ? json.contact : (typeof json?.contact === "string" ? json.contact : null),
214213
tags: Array.isArray(json?.tags) ? json.tags : (typeof json?.tags === "string" ? json.tags : null),

packages/nextjs/app/listing/[id]/page.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ const ListingDetailsPageInner = () => {
110110
active
111111
title
112112
description
113-
category
114113
image
115114
contact
116115
tags
@@ -154,7 +153,6 @@ const ListingDetailsPageInner = () => {
154153

155154
const title = data?.title || indexed?.title || `Listing ${params?.id}`;
156155
const description = data?.description || indexed?.description || "";
157-
const category = data?.category || indexed?.category || "";
158156
const active = pointer?.active ?? indexed?.active ?? true;
159157
const seller = pointer?.creator || indexed?.creator || undefined;
160158

@@ -207,7 +205,7 @@ const ListingDetailsPageInner = () => {
207205
if (Array.isArray(raw)) return (raw as any[]).map(v => String(v)).filter(Boolean);
208206
if (typeof raw === "string")
209207
return raw
210-
.split(",")
208+
.split(/\s+/)
211209
.map(s => s.trim())
212210
.filter(Boolean);
213211
} catch {}
@@ -402,10 +400,8 @@ const ListingDetailsPageInner = () => {
402400
</div>
403401
</div>
404402

405-
{category || tags.length ? (
403+
{tags.length ? (
406404
<div className="flex items-center flex-wrap gap-1">
407-
{category ? <span className="opacity-70 text-sm">in {category}</span> : null}
408-
{category && tags.length ? <span className="opacity-40 text-xs px-1">|</span> : null}
409405
{tags.map(t => (
410406
<span key={t} className="badge badge-secondary badge-sm">
411407
{t}

packages/nextjs/app/listing/new/page.tsx

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const parseTags = (tags: string[] | string | null): string[] => {
5151
if (Array.isArray(tags)) return tags.map(String).filter(Boolean);
5252
if (typeof tags === "string")
5353
return tags
54-
.split(",")
54+
.split(/\s+/)
5555
.map(s => s.trim())
5656
.filter(Boolean);
5757
return [];
@@ -95,7 +95,6 @@ const NewListingPageInner = () => {
9595
const { composeCast, isMiniApp } = useMiniapp();
9696
const [title, setTitle] = useState("");
9797
const [description, setDescription] = useState("");
98-
const [category, setCategory] = useState("");
9998
const [tags, setTags] = useState<string[]>([]);
10099
const [price, setPrice] = useState("");
101100
const [currency, setCurrency] = useState("ETH");
@@ -234,7 +233,6 @@ const NewListingPageInner = () => {
234233
return {
235234
title,
236235
description,
237-
category,
238236
tags,
239237
price,
240238
currency,
@@ -380,7 +378,6 @@ const NewListingPageInner = () => {
380378

381379
setTitle(item.title || "");
382380
setDescription(item.description || "");
383-
setCategory(item.category || "");
384381
setPrice(item.price || "");
385382
setImageCid(item.image);
386383
setLocationId(item.locationId || "");
@@ -468,45 +465,8 @@ const NewListingPageInner = () => {
468465
onChange={e => setDescription(e.target.value)}
469466
/>
470467
<div className="space-y-1">
471-
<label className="text-sm opacity-80">Category</label>
472-
<select
473-
className="select select-bordered w-full"
474-
value={category}
475-
onChange={e => setCategory(e.target.value)}
476-
>
477-
<option value="">Select a category</option>
478-
<option value="vehicles">Vehicles</option>
479-
<option value="housing">Housing & Rooms</option>
480-
<option value="furniture">Furniture</option>
481-
<option value="appliances">Appliances</option>
482-
<option value="electronics">Electronics</option>
483-
<option value="tools">Tools & Equipment</option>
484-
<option value="garden_outdoor">Garden & Outdoor</option>
485-
<option value="home_improvement">Home Improvement</option>
486-
<option value="clothing_accessories">Clothing & Accessories</option>
487-
<option value="baby_kids">Baby & Kids</option>
488-
<option value="sports_fitness">Sports & Fitness</option>
489-
<option value="bikes">Bikes</option>
490-
<option value="pets">Pets & Supplies</option>
491-
<option value="farm_garden">Farm & Garden</option>
492-
<option value="business_industrial">Business & Industrial</option>
493-
<option value="services">Services</option>
494-
<option value="jobs">Jobs</option>
495-
<option value="classes">Classes & Lessons</option>
496-
<option value="events">Local Events</option>
497-
<option value="free_stuff">Free Stuff</option>
498-
<option value="lost_found">Lost & Found</option>
499-
<option value="community">Community</option>
500-
<option value="garage_sales">Garage & Yard Sales</option>
501-
<option value="rideshare">Rideshare & Carpool</option>
502-
<option value="experiences">Experiences</option>
503-
<option value="other">Other</option>
504-
</select>
505-
</div>
506-
507-
<div className="space-y-1">
508-
<label className="text-sm opacity-80">Tags (commas separated)</label>
509-
<TagsInput value={tags} onChange={setTags} placeholder="e.g. iphone, mint, boxed" />
468+
<label className="text-sm opacity-80">Tags</label>
469+
<TagsInput value={tags} onChange={setTags} placeholder="e.g. iphone mint boxed (space separated)" />
510470
</div>
511471
<div className="flex items-center gap-3">
512472
<label className="label cursor-pointer justify-start gap-2 m-0 p-0">

packages/nextjs/app/location/[id]/page.tsx

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const LocationPage = () => {
1212
const [loadingListings, setLoadingListings] = useState(true);
1313
const [location, setLocation] = useState<any | null>(null);
1414
const [cachedName, setCachedName] = useState<string | null>(null);
15-
const [categoryFilter, setCategoryFilter] = useState<string>("");
15+
const [tagFilter, setTagFilter] = useState<string>("");
1616

1717
const refreshDelay = process.env.NEXT_PUBLIC_REFRESH_DELAY ? Number(process.env.NEXT_PUBLIC_REFRESH_DELAY) : 4000;
1818
// Future: we could store geo/radius for map previews
@@ -113,7 +113,6 @@ const LocationPage = () => {
113113
priceWei
114114
tokenSymbol
115115
tokenDecimals
116-
category
117116
}
118117
}
119118
}`,
@@ -142,7 +141,6 @@ const LocationPage = () => {
142141
priceWei: it?.priceWei ?? null,
143142
tokenSymbol: it?.tokenSymbol ?? null,
144143
tokenDecimals: it?.tokenDecimals ?? null,
145-
category: it?.category ?? null,
146144
};
147145
});
148146
setListings(items);
@@ -193,16 +191,33 @@ const LocationPage = () => {
193191
return () => clearTimeout(timeout);
194192
}, [fetchListings, params?.id, refreshDelay]);
195193

194+
const availableTags = useMemo(() => {
195+
const tagSet = listings.reduce((acc: Set<string>, listing) => {
196+
if (Array.isArray(listing.tags)) {
197+
listing.tags.forEach((tag: string) => {
198+
if (tag && typeof tag === "string") {
199+
acc.add(tag.toLowerCase());
200+
}
201+
});
202+
}
203+
return acc;
204+
}, new Set<string>());
205+
return Array.from(tagSet).sort();
206+
}, [listings]);
207+
196208
const filtered = useMemo(() => {
197209
const q = (query || "").toLowerCase();
198-
const cat = (categoryFilter || "").toLowerCase();
210+
const selectedTag = (tagFilter || "").toLowerCase();
199211

200212
return listings.filter(l => {
201213
if (q && !(l.title || "").toLowerCase().includes(q)) return false;
202-
if (cat && String(l.category || "").toLowerCase() !== cat) return false;
214+
if (selectedTag) {
215+
const listingTags = Array.isArray(l.tags) ? l.tags.map((t: string) => String(t).toLowerCase()) : [];
216+
if (!listingTags.includes(selectedTag)) return false;
217+
}
203218
return true;
204219
});
205-
}, [listings, query, categoryFilter]);
220+
}, [listings, query, tagFilter]);
206221

207222
return (
208223
<div className="p-4 space-y-4">
@@ -222,56 +237,39 @@ const LocationPage = () => {
222237
<details className="dropdown">
223238
<summary className="btn btn-sm relative">
224239
Filters
225-
{categoryFilter ? <span className="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-red-500" /> : null}
240+
{tagFilter ? <span className="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-red-500" /> : null}
226241
</summary>
227242
<div className="menu dropdown-content bg-base-100 rounded-box shadow p-3 mt-2 w-80 space-y-2">
228243
<div className="space-y-1">
229-
<div className="text-sm opacity-80">Category</div>
230-
<select
231-
className="select select-bordered w-full"
232-
value={categoryFilter}
233-
onChange={e => {
234-
setCategoryFilter(e.target.value);
235-
try {
236-
const parent = (e.target as HTMLSelectElement).closest("details") as HTMLDetailsElement | null;
237-
if (parent) parent.open = false; // auto close
238-
} catch {}
239-
}}
240-
>
241-
<option value="">All categories</option>
242-
<option value="vehicles">Vehicles</option>
243-
<option value="housing">Housing & Rooms</option>
244-
<option value="furniture">Furniture</option>
245-
<option value="appliances">Appliances</option>
246-
<option value="electronics">Electronics</option>
247-
<option value="tools">Tools & Equipment</option>
248-
<option value="garden_outdoor">Garden & Outdoor</option>
249-
<option value="home_improvement">Home Improvement</option>
250-
<option value="clothing_accessories">Clothing & Accessories</option>
251-
<option value="baby_kids">Baby & Kids</option>
252-
<option value="sports_fitness">Sports & Fitness</option>
253-
<option value="bikes">Bikes</option>
254-
<option value="pets">Pets & Supplies</option>
255-
<option value="farm_garden">Farm & Garden</option>
256-
<option value="business_industrial">Business & Industrial</option>
257-
<option value="services">Services</option>
258-
<option value="jobs">Jobs</option>
259-
<option value="classes">Classes & Lessons</option>
260-
<option value="events">Local Events</option>
261-
<option value="free_stuff">Free Stuff</option>
262-
<option value="lost_found">Lost & Found</option>
263-
<option value="community">Community</option>
264-
<option value="garage_sales">Garage & Yard Sales</option>
265-
<option value="rideshare">Rideshare & Carpool</option>
266-
<option value="experiences">Experiences</option>
267-
<option value="other">Other</option>
268-
</select>
244+
<div className="text-sm opacity-80">Tag</div>
245+
{availableTags.length > 0 ? (
246+
<select
247+
className="select select-bordered w-full"
248+
value={tagFilter}
249+
onChange={e => {
250+
setTagFilter(e.target.value);
251+
try {
252+
const parent = (e.target as HTMLSelectElement).closest("details") as HTMLDetailsElement | null;
253+
if (parent) parent.open = false; // auto close
254+
} catch {}
255+
}}
256+
>
257+
<option value="">All tags</option>
258+
{availableTags.map(tag => (
259+
<option key={tag} value={tag}>
260+
{tag}
261+
</option>
262+
))}
263+
</select>
264+
) : (
265+
<div className="text-sm opacity-60 py-2">No tags available</div>
266+
)}
269267
<div className="flex justify-end pt-1">
270268
<button
271269
className="btn btn-ghost btn-sm"
272270
onClick={e => {
273271
e.preventDefault();
274-
setCategoryFilter("");
272+
setTagFilter("");
275273
try {
276274
const parent = (e.currentTarget as HTMLButtonElement).closest(
277275
"details",

packages/nextjs/components/marketplace/TagsInput.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const TagsInput = ({ value, onChange, placeholder, className }: TagsInput
1919
const commitDraftTokens = useCallback(
2020
(text: string) => {
2121
const tokens = text
22-
.split(",")
22+
.split(/\s+/)
2323
.map(t => t.trim())
2424
.filter(Boolean)
2525
.map(t => t.toLowerCase());
@@ -76,17 +76,14 @@ export const TagsInput = ({ value, onChange, placeholder, className }: TagsInput
7676
value={draft}
7777
style={{ caretColor: "var(--bc)" }}
7878
onChange={e => {
79-
const text = e.target.value;
80-
if (text.includes(",")) {
81-
commitDraftTokens(text);
82-
return;
83-
}
84-
setDraft(text);
79+
setDraft(e.target.value);
8580
}}
8681
onKeyDown={e => {
87-
if (e.key === "Enter") {
82+
if (e.key === "Enter" || e.key === " ") {
8883
e.preventDefault();
89-
commitDraftTokens(draft);
84+
if (draft.trim()) {
85+
commitDraftTokens(draft);
86+
}
9087
} else if (e.key === "Backspace" && draft.length === 0 && value.length > 0) {
9188
e.preventDefault();
9289
removeTag(value[value.length - 1]);

packages/nextjs/services/marketplace/graphql.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export interface ListingData {
44
id: string;
55
title: string;
66
description: string;
7-
category: string;
87
tags: string[] | string | null;
98
price: string;
109
currency: string;
@@ -29,7 +28,6 @@ export async function fetchListingById(id: string): Promise<ListingData | null>
2928
id
3029
title
3130
description
32-
category
3331
tags
3432
price
3533
currency

0 commit comments

Comments
 (0)