← back to Costa Rica App

app/listing/[slug].tsx

121 lines

import { useEffect, useState } from 'react';
import { View, Text, ScrollView, TouchableOpacity, StyleSheet, ActivityIndicator, Linking, Alert, TextInput, Modal } from 'react-native';
import { useLocalSearchParams, router } from 'expo-router';
import { api, money, isAuthed } from '../../src/api';

export default function ListingDetail() {
  const { slug } = useLocalSearchParams<{ slug: string }>();
  const [l, setL] = useState<any>(null);
  const [contact, setContact] = useState<any>(null);
  const [msgOpen, setMsgOpen] = useState(false);
  const [msg, setMsg] = useState('');
  useEffect(() => {
    api.listing(slug!).then(r => setL(r.listing)).catch(() => setL(false));
    api.contact(slug!).then(r => setContact(r.contact)).catch(() => {});
  }, [slug]);

  async function sendMessage() {
    if (!isAuthed()) { router.push('/login'); return; }
    if (!msg.trim()) return;
    try { await api.message(slug!, msg.trim()); setMsgOpen(false); setMsg(''); Alert.alert('Sent ✅', 'Your message was delivered. Replies show in your inbox.'); }
    catch (e: any) { Alert.alert('Could not send', e.message); }
  }
  if (l === null) return <ActivityIndicator style={{ marginTop: 40 }} />;
  if (l === false) return <Text style={s.err}>Not available.</Text>;
  return (
    <View style={{ flex: 1 }}>
      <ScrollView contentContainerStyle={{ padding: 16 }}>
        <Text style={s.name}>{l.name}</Text>
        <Text style={s.region}>{l.region} · {String(l.vertical).replace(/_/g, ' ')}</Text>
        {l.rating ? <Text style={s.rating}>★ {l.rating}</Text> : null}
        {l.description ? <Text style={s.desc}>{l.description}</Text> : null}
        <View style={s.box}>
          <Text style={s.price}>{money(l.base_price, l.currency)} <Text style={s.per}>/ {l.booking_type === 'nightly' ? 'night' : 'person'}</Text></Text>
          {l.cleaning_fee ? <Text style={s.fee}>+ {money(l.cleaning_fee, l.currency)} cleaning</Text> : null}
          <Text style={s.small}>Up to {l.max_guests} guests · {l.instant_book ? 'Instant book' : 'Request to book'} · {l.cancellation}</Text>
        </View>
        {l.address ? <Text style={s.small}>📍 {l.address}</Text> : null}

        {/* BEST WAY TO CONTACT — big, prominent */}
        <Text style={s.contactHead}>BEST WAY TO CONTACT</Text>
        <View style={s.contactGrid}>
          {contact?.whatsapp && (
            <TouchableOpacity style={[s.cbtn, s.wa]} onPress={() => Linking.openURL(contact.whatsapp)}>
              <Text style={s.cemoji}>💬</Text><Text style={s.cbtnTxt}>WhatsApp</Text>
            </TouchableOpacity>
          )}
          {contact?.tel && (
            <TouchableOpacity style={[s.cbtn, s.call]} onPress={() => Linking.openURL(contact.tel)}>
              <Text style={s.cemoji}>📞</Text><Text style={s.cbtnTxt}>Call</Text>
            </TouchableOpacity>
          )}
          {contact?.mailto && (
            <TouchableOpacity style={[s.cbtn, s.email]} onPress={() => Linking.openURL(contact.mailto)}>
              <Text style={s.cemoji}>✉️</Text><Text style={s.cbtnTxt}>Email</Text>
            </TouchableOpacity>
          )}
          <TouchableOpacity style={[s.cbtn, s.msg]} onPress={() => (isAuthed() ? setMsgOpen(true) : router.push('/login'))}>
            <Text style={s.cemoji}>📨</Text><Text style={s.cbtnTxt}>Message</Text>
          </TouchableOpacity>
          {contact?.website && (
            <TouchableOpacity style={[s.cbtn, s.other]} onPress={() => Linking.openURL(contact.website)}>
              <Text style={s.cemoji}>🌐</Text><Text style={s.cbtnTxt}>Other</Text>
            </TouchableOpacity>
          )}
        </View>
      </ScrollView>

      {/* In-app messenger composer */}
      <Modal visible={msgOpen} animationType="slide" transparent onRequestClose={() => setMsgOpen(false)}>
        <View style={s.modalWrap}>
          <View style={s.modalCard}>
            <Text style={s.modalH}>Message {l.name}</Text>
            <TextInput style={s.msgInput} placeholder="Ask about availability, price, anything…" value={msg}
              onChangeText={setMsg} multiline autoFocus />
            <View style={{ flexDirection: 'row', gap: 8, marginTop: 12 }}>
              <TouchableOpacity style={[s.cta, { flex: 1, backgroundColor: '#eee', marginTop: 0 }]} onPress={() => setMsgOpen(false)}>
                <Text style={[s.ctaTxt, { color: '#333' }]}>Cancel</Text>
              </TouchableOpacity>
              <TouchableOpacity style={[s.cta, { flex: 1, marginTop: 0 }]} onPress={sendMessage}>
                <Text style={s.ctaTxt}>Send</Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      </Modal>
      <TouchableOpacity style={s.cta} onPress={() => router.push(`/book/${slug}`)}>
        <Text style={s.ctaTxt}>Book now</Text>
      </TouchableOpacity>
    </View>
  );
}
const s = StyleSheet.create({
  name: { fontSize: 22, fontWeight: '800', color: '#1a2b22' },
  region: { color: '#6b756e', marginTop: 4 },
  rating: { color: '#b8860b', marginTop: 6 },
  desc: { marginTop: 12, lineHeight: 21, color: '#33413a' },
  box: { marginTop: 16, backgroundColor: '#eef5f1', borderRadius: 12, padding: 16 },
  price: { fontSize: 20, fontWeight: '800', color: '#0a7d55' },
  per: { fontWeight: '400', color: '#6b756e', fontSize: 15 },
  fee: { color: '#33413a', marginTop: 4 },
  small: { color: '#6b756e', marginTop: 8 },
  cta: { backgroundColor: '#0a7d55', margin: 16, height: 52, borderRadius: 12, alignItems: 'center', justifyContent: 'center' },
  ctaTxt: { color: '#fff', fontSize: 17, fontWeight: '700' },
  err: { textAlign: 'center', marginTop: 40, color: '#a33' },
  // BEST WAY TO CONTACT
  contactHead: { fontSize: 22, fontWeight: '900', letterSpacing: 1, color: '#1a2b22', marginTop: 26, marginBottom: 12, textAlign: 'center' },
  contactGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, justifyContent: 'center' },
  cbtn: { width: '47%', minHeight: 74, borderRadius: 14, alignItems: 'center', justifyContent: 'center', paddingVertical: 12 },
  cemoji: { fontSize: 26 },
  cbtnTxt: { color: '#fff', fontWeight: '800', fontSize: 16, marginTop: 4 },
  wa: { backgroundColor: '#25D366' },
  call: { backgroundColor: '#0a7d55' },
  email: { backgroundColor: '#2b6cb0' },
  msg: { backgroundColor: '#7a3cff' },
  other: { backgroundColor: '#555' },
  modalWrap: { flex: 1, backgroundColor: 'rgba(0,0,0,.4)', justifyContent: 'flex-end' },
  modalCard: { backgroundColor: '#fff', borderTopLeftRadius: 20, borderTopRightRadius: 20, padding: 20 },
  modalH: { fontSize: 18, fontWeight: '800', color: '#1a2b22', marginBottom: 12 },
  msgInput: { minHeight: 90, borderWidth: 1, borderColor: '#e2e5e0', borderRadius: 12, padding: 12, textAlignVertical: 'top' },
});