← back to Costa Rica App

app/index.tsx

79 lines

import { useEffect, useState } from 'react';
import { View, Text, FlatList, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
import { Link, router } from 'expo-router';
import { api, money, isAuthed } from '../src/api';

const VERTICALS = [
  { k: '', label: 'All' },
  { k: 'tourism_hotel', label: 'Stays' },
  { k: 'tourism_tour', label: 'Tours' },
  { k: 'tourism_restaurant', label: 'Eat' },
  { k: 'service_beauty', label: 'Beauty' },
];

export default function Directory() {
  const [items, setItems] = useState<any[]>([]);
  const [q, setQ] = useState('');
  const [vert, setVert] = useState('');
  const [loading, setLoading] = useState(true);

  async function load() {
    setLoading(true);
    try {
      const r = await api.listings({ ...(q && { q }), ...(vert && { vertical: vert }), limit: '40' });
      setItems(r.listings || []);
    } catch (e) { setItems([]); } finally { setLoading(false); }
  }
  useEffect(() => { load(); }, [vert]);

  return (
    <View style={s.wrap}>
      <View style={s.searchRow}>
        <TextInput style={s.search} placeholder="Search stays, tours, services…" value={q}
          onChangeText={setQ} onSubmitEditing={load} returnKeyType="search" />
        <TouchableOpacity style={s.mybtn} onPress={() => router.push(isAuthed() ? '/bookings' : '/login')}>
          <Text style={{ color: '#fff' }}>{isAuthed() ? 'Trips' : 'Sign in'}</Text>
        </TouchableOpacity>
      </View>
      <FlatList horizontal showsHorizontalScrollIndicator={false} data={VERTICALS} keyExtractor={x => x.k}
        style={s.chips} renderItem={({ item }) => (
          <TouchableOpacity style={[s.chip, vert === item.k && s.chipOn]} onPress={() => setVert(item.k)}>
            <Text style={vert === item.k ? { color: '#fff' } : { color: '#0a7d55' }}>{item.label}</Text>
          </TouchableOpacity>
        )} />
      {loading ? <ActivityIndicator style={{ marginTop: 40 }} /> : (
        <FlatList data={items} keyExtractor={x => x.slug} contentContainerStyle={{ padding: 12 }}
          ListEmptyComponent={<Text style={s.empty}>No bookable listings yet in this filter.</Text>}
          renderItem={({ item }) => (
            <Link href={`/listing/${item.slug}`} asChild>
              <TouchableOpacity style={s.card}>
                <Text style={s.name}>{item.name}</Text>
                <Text style={s.meta}>{item.region} · {String(item.vertical).replace(/_/g, ' ')}</Text>
                <Text style={s.price}>{money(item.base_price, item.currency)}<Text style={s.per}> / {item.booking_type === 'nightly' ? 'night' : 'person'}</Text></Text>
                {item.rating ? <Text style={s.rating}>★ {item.rating}</Text> : null}
              </TouchableOpacity>
            </Link>
          )} />
      )}
    </View>
  );
}

const s = StyleSheet.create({
  wrap: { flex: 1, backgroundColor: '#f6f7f5' },
  searchRow: { flexDirection: 'row', padding: 12, gap: 8, alignItems: 'center' },
  search: { flex: 1, backgroundColor: '#fff', borderRadius: 10, paddingHorizontal: 14, height: 44, borderWidth: 1, borderColor: '#e2e5e0' },
  iconbtn: { backgroundColor: '#fff', width: 44, height: 44, borderRadius: 10, alignItems: 'center', justifyContent: 'center', borderWidth: 1, borderColor: '#e2e5e0' },
  mybtn: { backgroundColor: '#0a7d55', paddingHorizontal: 14, height: 44, borderRadius: 10, justifyContent: 'center' },
  chips: { flexGrow: 0, paddingHorizontal: 8 },
  chip: { paddingHorizontal: 14, paddingVertical: 8, marginHorizontal: 4, borderRadius: 20, borderWidth: 1, borderColor: '#0a7d55', backgroundColor: '#fff' },
  chipOn: { backgroundColor: '#0a7d55' },
  card: { backgroundColor: '#fff', borderRadius: 12, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: '#e8ebe6' },
  name: { fontSize: 16, fontWeight: '700', color: '#1a2b22' },
  meta: { color: '#6b756e', marginTop: 2, fontSize: 13 },
  price: { marginTop: 8, fontSize: 15, fontWeight: '700', color: '#0a7d55' },
  per: { fontWeight: '400', color: '#6b756e' },
  rating: { position: 'absolute', top: 14, right: 14, color: '#b8860b' },
  empty: { textAlign: 'center', color: '#6b756e', marginTop: 40 },
});