- 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.
83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
import { View, Text, TextInput, ScrollView } from "react-native";
|
||
import { useState } from "react";
|
||
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 } from "../../../lib/constants";
|
||
|
||
export default function HomeScreen() {
|
||
const theme = useTheme();
|
||
const [search, setSearch] = useState("");
|
||
|
||
const categories = useQuery(api.categories.list);
|
||
const allAds = useQuery(api.ads.list);
|
||
const searchResults = useQuery(
|
||
api.ads.search,
|
||
search.trim() ? { query: search.trim() } : "skip"
|
||
);
|
||
|
||
const ads = search.trim() ? searchResults : allAds;
|
||
const displayCategories = categories ?? CATEGORIES;
|
||
|
||
return (
|
||
<ScrollView
|
||
contentInsetAdjustmentBehavior="automatic"
|
||
contentContainerStyle={{ padding: theme.spacing.lg, gap: theme.spacing.md, paddingBottom: 40 }}
|
||
>
|
||
<View style={{ marginTop: theme.spacing.md }}>
|
||
<Text style={{ fontSize: 24, fontWeight: "700", color: theme.colors.text }}>
|
||
Добредојдовте 🏠
|
||
</Text>
|
||
<Text style={{ fontSize: 16, color: theme.colors.textSecondary, marginTop: theme.spacing.sm }}>
|
||
Најдете мајстор во вашата близина
|
||
</Text>
|
||
</View>
|
||
|
||
<TextInput
|
||
placeholder="Пребарувај мајстори..."
|
||
placeholderTextColor={theme.colors.textTertiary}
|
||
value={search}
|
||
onChangeText={setSearch}
|
||
style={{
|
||
backgroundColor: theme.colors.surface,
|
||
borderRadius: theme.radius.md,
|
||
padding: theme.spacing.md,
|
||
fontSize: 16,
|
||
color: theme.colors.text,
|
||
borderWidth: 1,
|
||
borderColor: theme.colors.border,
|
||
}}
|
||
/>
|
||
|
||
{!search.trim() && (
|
||
<>
|
||
<Text style={{ fontSize: 18, fontWeight: "600", color: theme.colors.text, marginTop: theme.spacing.sm }}>
|
||
Категории
|
||
</Text>
|
||
<CategoryGrid categories={displayCategories} />
|
||
</>
|
||
)}
|
||
|
||
<Text style={{ fontSize: 18, fontWeight: "600", color: theme.colors.text, marginTop: theme.spacing.sm }}>
|
||
{search.trim() ? "Резултати од пребарување" : "Неодамна додадени"}
|
||
</Text>
|
||
|
||
{ads === undefined ? (
|
||
<Loading />
|
||
) : ads.length === 0 ? (
|
||
<Text style={{ color: theme.colors.textTertiary, textAlign: "center", padding: 24 }}>
|
||
{search.trim() ? "Не се пронајдени резултати" : "Нема огласи"}
|
||
</Text>
|
||
) : (
|
||
<View style={{ gap: theme.spacing.sm }}>
|
||
{ads.map((ad) => (
|
||
<AdCard key={ad._id} ad={ad} />
|
||
))}
|
||
</View>
|
||
)}
|
||
</ScrollView>
|
||
);
|
||
} |