Deploy 2026-06-01T21:59:00.799Z

This commit is contained in:
2026-06-01 22:59:00 +01:00
commit 8c6e1e9af7
15 changed files with 2786 additions and 0 deletions

98
src/App.jsx Normal file
View File

@@ -0,0 +1,98 @@
import { useState, useRef, useEffect } from 'react'
import './App.css'
const API_URL = window.location.hostname === 'localhost'
? 'http://localhost:4003'
: 'https://diet-api.psbailey.uk'
export default function App() {
const [messages, setMessages] = useState([])
const [input, setInput] = useState('')
const [loading, setLoading] = useState(false)
const messagesEnd = useRef(null)
const inputRef = useRef(null)
useEffect(() => {
messagesEnd.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
async function send(e) {
e.preventDefault()
const text = input.trim()
if (!text || loading) return
setInput('')
setMessages(prev => [...prev, { role: 'user', content: text }])
setLoading(true)
try {
const res = await fetch(`${API_URL}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: text })
})
if (!res.ok) throw new Error('Failed')
const reader = res.body.getReader()
const decoder = new TextDecoder()
let assistantMsg = ''
setMessages(prev => [...prev, { role: 'assistant', content: '' }])
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
// Parse SSE lines
for (const line of chunk.split('\n')) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') break
assistantMsg += data
setMessages(prev => {
const updated = [...prev]
updated[updated.length - 1] = { role: 'assistant', content: assistantMsg }
return updated
})
}
}
}
} catch (err) {
setMessages(prev => [...prev, { role: 'assistant', content: '⚠️ Connection error. Try again.' }])
} finally {
setLoading(false)
inputRef.current?.focus()
}
}
return (
<div className="app">
<header>
<h1>🥗 Diet Coach</h1>
</header>
<div className="messages">
{messages.length === 0 && (
<div className="empty">Ask me about your diet, log food, or get meal suggestions.</div>
)}
{messages.map((msg, i) => (
<div key={i} className={`msg ${msg.role}`}>
<div className="bubble">{msg.content || '...'}</div>
</div>
))}
<div ref={messagesEnd} />
</div>
<form onSubmit={send} className="input-bar">
<input
ref={inputRef}
value={input}
onChange={e => setInput(e.target.value)}
placeholder="What did you eat today?"
disabled={loading}
autoFocus
/>
<button type="submit" disabled={loading || !input.trim()}>Send</button>
</form>
</div>
)
}