Initial commit

This commit is contained in:
Micha
2025-12-24 11:52:49 +01:00
commit e1552a8c2e
66 changed files with 14380 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
import { useEffect, useState } from 'react'
import { Button, Card, Col, Container, Form, Row } from 'react-bootstrap'
import { Link, useParams } from 'react-router-dom'
import { createPost, getThread, listPostsByThread } from '../api/client'
import { useAuth } from '../context/AuthContext'
export default function ThreadView() {
const { id } = useParams()
const { token } = useAuth()
const [thread, setThread] = useState(null)
const [posts, setPosts] = useState([])
const [error, setError] = useState('')
const [loading, setLoading] = useState(true)
const [body, setBody] = useState('')
const [saving, setSaving] = useState(false)
useEffect(() => {
setLoading(true)
Promise.all([getThread(id), listPostsByThread(id)])
.then(([threadData, postData]) => {
setThread(threadData)
setPosts(postData)
})
.catch((err) => setError(err.message))
.finally(() => setLoading(false))
}, [id])
const handleSubmit = async (event) => {
event.preventDefault()
setSaving(true)
setError('')
try {
await createPost({ body, threadId: id })
setBody('')
const updated = await listPostsByThread(id)
setPosts(updated)
} catch (err) {
setError(err.message)
} finally {
setSaving(false)
}
}
return (
<Container className="py-5">
{loading && <p className="bb-muted">Loading thread...</p>}
{error && <p className="text-danger">{error}</p>}
{thread && (
<>
<div className="bb-hero mb-4">
<p className="bb-chip">Thread</p>
<h2 className="mt-3">{thread.title}</h2>
<p className="bb-muted mb-2">{thread.body}</p>
{thread.category && (
<p className="bb-muted mb-0">
Category:{' '}
<Link to={`/category/${thread.category.id || thread.category.split('/').pop()}`}>
{thread.category.name || 'Back to category'}
</Link>
</p>
)}
</div>
<Row className="g-4">
<Col lg={7}>
<h4 className="bb-section-title mb-3">Replies</h4>
{posts.length === 0 && <p className="bb-muted">Be the first to reply.</p>}
{posts.map((post) => (
<Card className="bb-card mb-3" key={post.id}>
<Card.Body>
<Card.Text>{post.body}</Card.Text>
<small className="bb-muted">
{post.author?.username || 'Anonymous'}
</small>
</Card.Body>
</Card>
))}
</Col>
<Col lg={5}>
<h4 className="bb-section-title mb-3">Reply</h4>
<div className="bb-form">
{!token && (
<p className="bb-muted mb-3">Log in to reply to this thread.</p>
)}
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3">
<Form.Label>Message</Form.Label>
<Form.Control
as="textarea"
rows={5}
placeholder="Share your reply."
value={body}
onChange={(event) => setBody(event.target.value)}
disabled={!token || saving}
required
/>
</Form.Group>
<Button type="submit" variant="dark" disabled={!token || saving}>
{saving ? 'Posting...' : 'Post reply'}
</Button>
</Form>
</div>
</Col>
</Row>
</>
)}
</Container>
)
}