mojmajstor/app/(tabs)/(home)/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

83 lines
2.9 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, 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>
);
}