import React, { useState, useEffect, useMemo, useRef } from 'react'; import { Truck, X, Check, Phone, ChevronRight, ChevronLeft, Loader2, Monitor, Wind, Tv, Zap, Layout, Archive, Bike, Laptop, Music, Sofa, Bed, Search, Calendar, History, AlertCircle } from 'lucide-react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, addDoc, query, getDocs, where } from 'firebase/firestore'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; // --- Configuration & Constants --- const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const db = getFirestore(app); const auth = getAuth(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'kamitsudo-app'; const CITIES = ['下関市', '宇部市', '山陽小野田市', '美祢市', '長門市']; const TIME_SLOTS = ['10:00~12:00', '12:00~14:00', '14:00~16:00', '16:00~18:00']; const ITEM_CATEGORIES = [ { name: '主要家電', items: [ { id: 'ref_l', label: '冷蔵庫(大型)', icon: }, { id: 'ref_s', label: '冷蔵庫(小型)', icon: }, { id: 'wm', label: '洗濯機', icon: }, { id: 'tv', label: 'テレビ', icon: }, { id: 'ac', label: 'エアコン', icon: }, ] }, { name: '家具', items: [ { id: 'table', label: 'テーブル', icon: }, { id: 'sofa', label: 'ソファー', icon: }, { id: 'bed', label: 'ベッドフレーム', icon: }, { id: 'cabinet', label: '棚・キャビネット', icon: }, ] }, { name: 'その他', items: [ { id: 'bicycle', label: '自転車', icon: }, { id: 'pc', label: 'パソコン', icon: }, { id: 'instrument', label: '楽器', icon: }, ] } ]; // --- Utilities --- const formatDate = (date) => { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; }; const getDaysInMonth = (year, month) => new Date(year, month + 1, 0).getDate(); // --- Main App Component --- export default function App() { const [user, setUser] = useState(null); const [activeView, setActiveView] = useState('booking'); const [loading, setLoading] = useState(true); useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (err) { console.error("Auth error:", err); } finally { setLoading(false); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, setUser); return () => unsubscribe(); }, []); if (loading) { return (

Loading 神集...

); } return (
{activeView === 'booking' ? : }
{/* Mobile Nav */}
); } function BookingView({ user }) { const [step, setStep] = useState(1); const [items, setItems] = useState({}); const [bookingDate, setBookingDate] = useState(null); const [bookingTime, setBookingTime] = useState(null); const [formData, setFormData] = useState({ city: '下関市', address: '', phone: '', name: '' }); const [submitting, setSubmitting] = useState(false); const [completedId, setCompletedId] = useState(null); const totalItems = Object.values(items).reduce((a, b) => a + b, 0); const handleBooking = async () => { if (!user) return; setSubmitting(true); try { const bookingData = { userId: user.uid, items, date: formatDate(bookingDate), time: bookingTime, ...formData, status: 'pending', createdAt: new Date().toISOString() }; const docRef = await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'bookings'), bookingData); setCompletedId(docRef.id); setStep(5); } catch (err) { console.error(err); } finally { setSubmitting(false); } }; if (step === 5) { return (

ご予約を受け付けました!

担当者より内容確認のお電話を差し上げます。

Reservation ID

{completedId}

); } return (

{step === 1 && "買取品目の選択"} {step === 2 && "訪問日時の選択"} {step === 3 && "連絡先情報の入力"} {step === 4 && "ご予約内容の確認"}

Step {step} / 4
{step === 1 && (
{ITEM_CATEGORIES.map(cat => (

{cat.name}

{cat.items.map(item => (
{item.icon}
{item.label}
{items[item.id] || 0}
))}
))}
)} {step === 2 && (
{bookingDate && (

希望の時間帯を選択

{TIME_SLOTS.map(slot => ( ))}
)}
)} {step === 3 && (
{CITIES.map(c => ( ))}
setFormData(p => ({...p, name: e.target.value}))} placeholder="神集 太郎" className="w-full bg-gray-50 border-none rounded-2xl px-5 py-4 text-sm focus:ring-2 focus:ring-yellow-500 transition-all outline-none" />
setFormData(p => ({...p, address: e.target.value}))} placeholder="町名、番地、建物名など" className="w-full bg-gray-50 border-none rounded-2xl px-5 py-4 text-sm focus:ring-2 focus:ring-yellow-500 transition-all outline-none" />
setFormData(p => ({...p, phone: e.target.value}))} placeholder="083-XXX-XXXX" className="w-full bg-gray-50 border-none rounded-2xl px-5 py-4 text-sm focus:ring-2 focus:ring-yellow-500 transition-all outline-none" />
)} {step === 4 && (

訪問予定日時

{formatDate(bookingDate)}

{bookingTime}

エリア

{formData.city}

買取品目リスト

{Object.entries(items).map(([id, qty]) => { if (qty === 0) return null; const label = ITEM_CATEGORIES.flatMap(c => c.items).find(i => i.id === id)?.label; return ( {label} x{qty} ); })}

お客様名

{formData.name} 様

連絡先電話番号

{formData.phone}

ご住所

{formData.city} {formData.address}

※予約後、買取スタッフよりお電話にてご連絡をさせていただきます。品目の状態によっては買取ができない場合もございますので、あらかじめご了承ください。

)}
); } function CalendarComponent({ onSelect, selected }) { const [currentDate, setCurrentDate] = useState(new Date()); const year = currentDate.getFullYear(); const month = currentDate.getMonth(); const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = getDaysInMonth(year, month); const today = new Date(); today.setHours(0, 0, 0, 0); const prevMonth = () => setCurrentDate(new Date(year, month - 1, 1)); const nextMonth = () => setCurrentDate(new Date(year, month + 1, 1)); const days = []; for (let i = 0; i < firstDay; i++) days.push(null); for (let i = 1; i <= daysInMonth; i++) days.push(new Date(year, month, i)); return (

{year}年 {month + 1}月

{['日', '月', '火', '水', '木', '金', '土'].map((d, i) => (
{d}
))}
{days.map((date, i) => { if (!date) return
; const isSelected = selected && date.getTime() === selected.getTime(); const isPast = date < today; const isSunday = date.getDay() === 0; return ( ); })}
); } function LookupView({ user }) { const [phone, setPhone] = useState(''); const [bookings, setBookings] = useState([]); const [searching, setSearching] = useState(false); const [hasSearched, setHasSearched] = useState(false); const handleSearch = async () => { if (!phone || !user) return; setSearching(true); try { const q = query( collection(db, 'artifacts', appId, 'public', 'data', 'bookings'), where('phone', '==', phone) ); const snapshot = await getDocs(q); const data = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); setBookings(data); setHasSearched(true); } catch (err) { console.error(err); } finally { setSearching(false); } }; return (

予約情報の確認

登録した電話番号で予約状況を確認できます。

setPhone(e.target.value)} placeholder="09012345678" className="w-full bg-gray-100 border-none rounded-2xl px-6 py-5 text-lg font-bold tracking-widest focus:ring-2 focus:ring-yellow-500 transition-all outline-none" />
{hasSearched && (

検索結果: {bookings.length}件

{bookings.length === 0 ? (

該当する予約が見つかりませんでした。

) : ( bookings.map(b => (
{b.status === 'pending' ? 'Confirmation Pending' : 'Confirmed'}

{b.date}

{b.time}

City

{b.city}

{Object.entries(b.items).map(([id, qty]) => { if (qty === 0) return null; const label = ITEM_CATEGORIES.flatMap(c => c.items).find(i => i.id === id)?.label; return ( {label} x{qty} ); })}

Reservation Key

{b.id}

)) )}
)}
); }