Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | "use client";
import { useEffect, useState } from "react";
import {
Container,
Typography,
Card,
CardContent,
CardActions,
Button,
Chip,
Box,
CircularProgress,
Alert,
} from "@mui/material";
import Link from "next/link";
import Navbar from "@/components/Navbar";
import { useSession } from "next-auth/react";
interface Post {
_id: string;
title: string;
summary: string;
authorId: {
name: string;
image?: string;
};
categoryId: {
name: string;
slug: string;
};
createdAt: string;
tags?: string[];
}
export default function Home() {
const { status } = useSession();
const [posts, setPosts] = useState<Post[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (status === "authenticated") {
fetchPosts();
}
}, [status]);
const fetchPosts = async () => {
try {
setLoading(true);
const response = await fetch("/anotoki/api/posts?limit=20");
if (!response.ok) {
throw new Error("Failed to fetch posts");
}
const result = await response.json();
setPosts(result.data);
} catch (err) {
setError(err instanceof Error ? err.message : "An error occurred");
} finally {
setLoading(false);
}
};
if (status === "loading") {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="100vh">
<CircularProgress />
</Box>
);
}
return (
<>
<Navbar />
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
最近の投稿
</Typography>
<Typography variant="body1" color="text.secondary" paragraph>
みんなの「あの時こうしておけばよかった」を共有しましょう
</Typography>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{loading ? (
<Box display="flex" justifyContent="center" my={4}>
<CircularProgress />
</Box>
) : posts.length === 0 ? (
<Alert severity="info">
まだ投稿がありません。最初の投稿を作成してみましょう!
</Alert>
) : (
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", sm: "repeat(2, 1fr)", md: "repeat(3, 1fr)" }, gap: 3 }}>
{posts.map((post) => (
<Box key={post._id}>
<Card sx={{ height: "100%", display: "flex", flexDirection: "column" }}>
<CardContent sx={{ flexGrow: 1 }}>
<Chip
label={post.categoryId.name}
size="small"
color="primary"
sx={{ mb: 1 }}
/>
<Typography variant="h6" component="h2" gutterBottom>
{post.title}
</Typography>
<Typography variant="body2" color="text.secondary" paragraph>
{post.summary}
</Typography>
<Typography variant="caption" color="text.secondary">
投稿者: {post.authorId.name}
</Typography>
<br />
<Typography variant="caption" color="text.secondary">
{new Date(post.createdAt).toLocaleDateString("ja-JP")}
</Typography>
{post.tags && post.tags.length > 0 && (
<Box sx={{ mt: 1 }}>
{post.tags.map((tag, index) => (
<Chip key={index} label={tag} size="small" sx={{ mr: 0.5, mt: 0.5 }} />
))}
</Box>
)}
</CardContent>
<CardActions>
<Button
size="small"
component={Link}
href={`/posts/${post._id}`}
fullWidth
>
詳細を見る
</Button>
</CardActions>
</Card>
</Box>
))}
</Box>
)}
</Container>
</>
);
}
|