- Add seedCategories mutation (idempotent) and getBySlug query - Add ad queries: list, getByCategory, getById, search, getByHandyman - Home screen: search bar, CategoryGrid, recent ads list with real-time Convex queries and search-by-keyword - Explore screen: category filter via search param, filtered ad list, full category grid when unfiltered - AdCard component: title, category badge, location, rating stars, price range, navigation to ad detail - CategoryGrid component: 3-column emoji grid linking to filtered explore - Add CATEGORY_EMOJI map and getCategoryName() helper in constants All UI text in Macedonian. TypeScript clean, Convex functions deployed.
92 lines
3.2 KiB
TypeScript
92 lines
3.2 KiB
TypeScript
import { View, Text, Pressable, ScrollView } from "react-native";
|
||
import { useLocalSearchParams, Link } from "expo-router";
|
||
import { useTheme } from "../../../components/theme";
|
||
import { useQuery } from "convex/react";
|
||
import { api } from "../../../convex/_generated/api";
|
||
import { CategoryGrid } from "../../../components/category-grid";
|
||
import { AdCard } from "../../../components/ad-card";
|
||
import { Loading } from "../../../components/ui/loading";
|
||
import { CATEGORIES, CATEGORY_EMOJI, getCategoryName } from "../../../lib/constants";
|
||
|
||
export default function ExploreScreen() {
|
||
const theme = useTheme();
|
||
const { category } = useLocalSearchParams<{ category?: string }>();
|
||
|
||
const categories = useQuery(api.categories.list);
|
||
const categoryAds = useQuery(
|
||
api.ads.getByCategory,
|
||
category ? { category } : "skip"
|
||
);
|
||
const allAds = useQuery(api.ads.list);
|
||
|
||
const displayCategories = categories ?? CATEGORIES;
|
||
|
||
if (category) {
|
||
const catName = getCategoryName(category);
|
||
const catEmoji = CATEGORY_EMOJI[category] ?? "📋";
|
||
|
||
return (
|
||
<ScrollView
|
||
contentInsetAdjustmentBehavior="automatic"
|
||
contentContainerStyle={{ padding: theme.spacing.lg, gap: theme.spacing.md }}
|
||
>
|
||
<View style={{ flexDirection: "row", alignItems: "center", gap: theme.spacing.sm }}>
|
||
<Link href="/(tabs)/(explore)" asChild>
|
||
<Pressable style={{ marginRight: theme.spacing.xs }}>
|
||
<Text style={{ fontSize: 20, color: theme.colors.primary }}>← Сите</Text>
|
||
</Pressable>
|
||
</Link>
|
||
<Text style={{ fontSize: 28 }}>{catEmoji}</Text>
|
||
<Text style={{ fontSize: 22, fontWeight: "700", color: theme.colors.text }}>
|
||
{catName}
|
||
</Text>
|
||
</View>
|
||
|
||
{categoryAds === undefined ? (
|
||
<Loading />
|
||
) : categoryAds.length === 0 ? (
|
||
<Text style={{ color: theme.colors.textTertiary, textAlign: "center", padding: 24 }}>
|
||
Нема огласи во оваа категорија
|
||
</Text>
|
||
) : (
|
||
<View style={{ gap: theme.spacing.sm }}>
|
||
{categoryAds.map((ad) => (
|
||
<AdCard key={ad._id} ad={ad} />
|
||
))}
|
||
</View>
|
||
)}
|
||
</ScrollView>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<ScrollView
|
||
contentInsetAdjustmentBehavior="automatic"
|
||
contentContainerStyle={{ padding: theme.spacing.lg, gap: theme.spacing.md }}
|
||
>
|
||
<Text style={{ fontSize: 20, fontWeight: "700", color: theme.colors.text }}>
|
||
Пребарувај по категорија
|
||
</Text>
|
||
|
||
<CategoryGrid categories={displayCategories} />
|
||
|
||
<Text style={{ fontSize: 18, fontWeight: "600", color: theme.colors.text, marginTop: theme.spacing.sm }}>
|
||
Сите огласи
|
||
</Text>
|
||
|
||
{allAds === undefined ? (
|
||
<Loading />
|
||
) : allAds.length === 0 ? (
|
||
<Text style={{ color: theme.colors.textTertiary, textAlign: "center", padding: 24 }}>
|
||
Нема огласи
|
||
</Text>
|
||
) : (
|
||
<View style={{ gap: theme.spacing.sm }}>
|
||
{allAds.map((ad) => (
|
||
<AdCard key={ad._id} ad={ad} />
|
||
))}
|
||
</View>
|
||
)}
|
||
</ScrollView>
|
||
);
|
||
} |