diff --git a/src/App.tsx b/src/App.tsx index d7a72df3..3993faf4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,40 +1,299 @@ -import { useEffect, useState } from "react"; -import type { Schema } from "../amplify/data/resource"; -import { generateClient } from "aws-amplify/data"; +// @ts-nocheck +import { useState, useEffect, useRef } from 'react'; +import { Amplify } from 'aws-amplify'; +import { signIn, signUp, signOut, getCurrentUser } from 'aws-amplify/auth'; +import { generateClient } from 'aws-amplify/api'; +import './App.css'; -const client = generateClient(); +// Setup AWS Connection +import outputs from '../amplify_outputs.json'; +Amplify.configure(outputs); +const client = generateClient(); -function App() { - const [todos, setTodos] = useState>([]); +export default function App() { + // --- STATE --- + const [view, setView] = useState('LOGIN'); + const [user, setUser] = useState(null); + + // Auth Form State + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [fullName, setFullName] = useState(''); + const [phone, setPhone] = useState(''); - useEffect(() => { - client.models.Todo.observeQuery().subscribe({ - next: (data) => setTodos([...data.items]), - }); - }, []); + // App State + const [activeOrders, setActiveOrders] = useState([]); + const [pendingOrders, setPendingOrders] = useState([]); + const [walletBalance, setWalletBalance] = useState(0); + const [showProfile, setShowProfile] = useState(false); + const [chatOrderId, setChatOrderId] = useState(null); + + const gpsWatchId = useRef(null); - function createTodo() { - client.models.Todo.create({ content: window.prompt("Todo content") }); - } + // --- LOGIC --- + const handleAuth = async (type) => { + try { + if (type === 'SIGNUP') { + const { userId } = await signUp({ + username: email, + password, + options: { userAttributes: { email, name: fullName, phone_number: phone } } + }); + await client.models.Driver.create({ + id: userId, email, fullName, phoneNumber: phone, status: 'pending', walletBalance: 0 + }); + setView('PENDING'); + } else { + await signIn({ username: email, password }); + checkDriverStatus(); + } + } catch (e) { alert("Auth Error: " + e.message); } + }; + const checkDriverStatus = async () => { + try { + const currentUser = await getCurrentUser(); + setUser(currentUser); + const { data: driverProfile } = await client.models.Driver.get({ id: currentUser.userId }); + + if (driverProfile?.status === 'pending') { + setView('PENDING'); + } else if (driverProfile?.status === 'suspended') { + setView('SUSPENDED'); + } else { + setWalletBalance(driverProfile?.walletBalance || 0); + setView('DASHBOARD'); + startOrderListeners(currentUser.userId); + } + } catch (e) { setView('LOGIN'); } + }; + + const handleLogout = async () => { + stopTracking(); + await signOut(); + setUser(null); + setView('LOGIN'); + }; + + const requestWithdrawal = async () => { + alert("Processing withdrawal request to M-PESA..."); + setWalletBalance(0); + }; + + // --- ORDER MANAGERS --- + const startOrderListeners = (driverId) => { + client.models.Order.observeQuery({ + filter: { status: { eq: 'Pending' } } + }).subscribe(({ items }) => setPendingOrders(items)); + + client.models.Order.observeQuery({ + filter: { status: { eq: 'Accepted' }, driverId: { eq: driverId } } + }).subscribe(({ items }) => { + setActiveOrders(items); + if (items.length > 0) startTracking(items[0].id); + else stopTracking(); + }); + }; + + const acceptOrder = async (orderId) => { + await client.models.Order.update({ + id: orderId, + status: 'Accepted', + driverId: user.userId + }); + }; + + const completeOrder = async (orderId, baseFee) => { + const pin = prompt("Enter 4-digit customer PIN:"); + if (!pin) return; + + await client.models.Order.update({ id: orderId, status: 'Completed' }); + + const payout = baseFee * 0.95; + setWalletBalance(prev => prev + payout); + await client.models.Driver.update({ id: user.userId, walletBalance: walletBalance + payout }); + + alert(`Success! M${payout.toFixed(2)} added to Wallet.`); + }; + + // --- TRACKING MANAGER --- + const startTracking = (orderId) => { + if (gpsWatchId.current) return; + if (!navigator.geolocation) return; + + gpsWatchId.current = navigator.geolocation.watchPosition( + (position) => { + client.models.Order.update({ + id: orderId, + driverLat: position.coords.latitude, + driverLng: position.coords.longitude + }); + }, + (error) => console.warn(error), + { enableHighAccuracy: true, timeout: 5000 } + ); + }; + + const stopTracking = () => { + if (gpsWatchId.current) { + navigator.geolocation.clearWatch(gpsWatchId.current); + gpsWatchId.current = null; + } + }; + + // --- RENDERS --- + + if (view === 'LOGIN') return ( +
+
+

Driver Portal

+ setEmail(e.target.value)} className="login-input" placeholder="Driver Email" /> + setPassword(e.target.value)} className="login-input" placeholder="Password" /> + + +
+
+ ); + + if (view === 'SIGNUP') return ( +
+

Driver Registration

+

Provide your full details for background verification.

+ +
Personal Details
+ setFullName(e.target.value)} /> + setPhone(e.target.value)} /> + +
Account Security
+ setEmail(e.target.value)} /> + setPassword(e.target.value)} /> + + + +
+ ); + + if (view === 'PENDING') return ( +
+ + + +

Account Under Review

+

Your registration has been received. Our admin team is currently reviewing your documents. This page will automatically update once you are approved.

+ +
+ ); + + if (view === 'SUSPENDED') return ( +
+ + + +

Account Suspended

+

Your account has been suspended by the administration. Please contact support for more details.

+ +
+ ); + + // DASHBOARD return ( -
-

My todos

- -
    - {todos.map((todo) => ( -
  • {todo.content}
  • - ))} -
-
- 🥳 App successfully hosted. Try creating a new todo. -
- - Review next step of this tutorial. - +
+
+

Bofelo Driver

+
+ +
Online
+
-
+ +
+ {/* ACTIVE ORDERS SECTION */} +
Active Deliveries
+ {activeOrders.length === 0 ? ( +

No active orders

+ ) : ( + activeOrders.map(o => ( +
+
+
M {o.fee || '0.00'}
+
IN PROGRESS
+
+
+
+
PICKUP
{o.pickup || 'Store'}
+
DROPOFF
{o.dropoff || 'Customer'}
+
+
+ 🗺 Navigate + +
+ +
+ )) + )} + + {/* PENDING ORDERS SECTION */} +
New Requests
+ {activeOrders.length > 0 ? ( +

Complete your active delivery to see new requests.

+ ) : pendingOrders.length === 0 ? ( +

Waiting for orders...

+ ) : ( + pendingOrders.map(o => ( +
+
+
M {o.fee || '0.00'}
+
NEW
+
+
+
+
PICKUP
{o.pickup || 'Store'}
+
DROPOFF
{o.dropoff || 'Customer'}
+
+
+ +
+
+ )) + )} +
+ + {/* PROFILE MODAL */} + {showProfile && ( +
+
+

Profile & Analytics

+ +
+
+
+
+
Available to Withdraw
+
M-PESA
+
+
M {walletBalance.toFixed(2)}
+ +
+ +
+
+ )} + + {/* CHAT MODAL */} + {chatOrderId && ( +
+
+
+ Chat & Call + +
+
+

Chat interface active for Order {chatOrderId}

+

(Full Chat Component injection point)

+
+
+
+ )} + ); } - -export default App;