Corp/frontend/src/app/page.tsx

132 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { Heart, MessageSquare } from "lucide-react";
import { AppShell } from "../components/AppShell";
import { apiClient } from "@/lib/api";
import { useEffect, useState } from "react";
import type { Post } from "@/types/api";
import { useUser } from "@/contexts/UserContext";
export default function Home() {
const { user } = useUser();
const [posts, setPosts] = useState<Post[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const userInitials = user
? `${user.last_name?.[0] || ''}${user.first_name?.[0] || ''}`
: 'Г';
useEffect(() => {
async function loadPosts() {
try {
const response = await apiClient.getPosts();
setPosts(response.results);
} catch (err: any) {
console.error('Ошибка загрузки ленты:', err);
setError('Не удалось загрузить ленту');
} finally {
setLoading(false);
}
}
loadPosts();
}, []);
if (loading) {
return (
<AppShell>
<div className="flex items-center justify-center py-12">
<div className="text-center">
<div className="mb-4 inline-block h-8 w-8 animate-spin rounded-full border-4 border-sky-400 border-t-transparent"></div>
<p className="text-sm text-gray-500">Загрузка ленты...</p>
</div>
</div>
</AppShell>
);
}
if (error) {
return (
<AppShell>
<div className="rounded-2xl bg-red-50 p-6 text-center">
<p className="text-sm text-red-800">{error}</p>
</div>
</AppShell>
);
}
return (
<AppShell>
<div className="space-y-4">
{posts.length === 0 ? (
<div className="rounded-2xl bg-gray-50 p-8 text-center">
<p className="text-sm text-gray-500">Пока нет постов в ленте</p>
</div>
) : (
posts.map((post) => {
const authorName = post.author
? `${post.author.last_name} ${post.author.first_name}`.trim()
: 'Аноним';
const authorInitials = post.author
? `${post.author.last_name?.[0] || ''}${post.author.first_name?.[0] || ''}`
: 'А';
const isAuthorOnline =
typeof post.author?.is_active === 'boolean'
? post.author.is_active
: Boolean(post.author?.id && user?.id && post.author.id === user.id && user.is_active);
// Форматируем дату
const postDate = new Date(post.created_at);
const now = new Date();
const diffMs = now.getTime() - postDate.getTime();
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const timeAgo = diffHours < 1
? 'только что'
: diffHours < 24
? `${diffHours} ч. назад`
: `${Math.floor(diffHours / 24)} дн. назад`;
return (
<article key={post.id} className="rounded-2xl bg-white p-5 shadow-sm ring-1 ring-gray-100">
<header className="mb-3 flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="relative h-10 w-10">
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-sky-400 text-sm font-semibold text-white">
{post.author?.avatar ? (
<img src={post.author.avatar} alt={authorName} className="h-full w-full object-cover" />
) : (
authorInitials
)}
</div>
{isAuthorOnline ? (
<span className="absolute -bottom-0.5 -right-0.5 z-10 h-3 w-3 rounded-full bg-sky-400 ring-2 ring-white" />
) : null}
</div>
<div>
<p className="text-sm font-semibold text-gray-900">{authorName}</p>
<p className="text-xs text-gray-500">{timeAgo}</p>
</div>
</div>
</header>
<p className="text-sm leading-6 text-gray-800">{post.content}</p>
{post.image && (
<div className="mt-3 overflow-hidden rounded-lg">
<img src={post.image} alt="" className="w-full" />
</div>
)}
<div className="mt-4 flex items-center gap-4 text-sm text-gray-600">
<button className="flex items-center gap-2 rounded-lg px-3 py-2 hover:bg-gray-50">
<Heart size={16} className="text-gray-400" /> {post.likes_count || 0}
</button>
<button className="flex items-center gap-2 rounded-lg px-3 py-2 hover:bg-gray-50">
<MessageSquare size={16} className="text-gray-400" /> {post.comments_count || 0}
</button>
</div>
</article>
);
})
)}
</div>
</AppShell>
);
}