mojmajstor/app/(tabs)/(explore)/index.tsx
echo ce7e400720 feat: Phase 2 - home screen, categories, explore, AdCard
- 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.
2026-05-29 18:24:01 +02:00

92 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}