Blogs listing + Categories

This commit is contained in:
cdricms
2025-02-25 00:13:53 +01:00
parent ae228710e1
commit 793e3748f9
21 changed files with 695 additions and 192 deletions

View File

@@ -36,8 +36,8 @@ export default async function About() {
Nicolas GORUK
</CardTitle>
<CardDescription>
Président l'association française de Latosa
Escrima
Président de l'association française de
Latosa Escrima
</CardDescription>
</CardHeader>
<CardContent className="px-8 sm:px-10 py-14">

View File

@@ -0,0 +1,137 @@
"use client";
import { useSearchParams, useRouter, usePathname } from "next/navigation";
import { Button } from "@/components/ui/button";
import request from "@/lib/request";
import { useEffect, useState } from "react";
import { Category } from "@/types/types";
import { useIsMobile } from "@/hooks/use-mobile";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
} from "@/components/ui/select";
import { SelectValue } from "@radix-ui/react-select";
import { CircleXIcon } from "lucide-react";
export default function Categories({
selectedCategory,
}: {
selectedCategory?: string;
}) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const [categories, setCategories] = useState<Category[]>([]);
const [selected, setSelected] = useState<string | undefined>(
selectedCategory,
);
useEffect(() => {
setSelected(selectedCategory);
}, [selectedCategory]); // Sync local state when URL changes
const [isReady, setIsReady] = useState(false);
const isMobile = useIsMobile();
useEffect(() => {
setIsReady(true); // Ensures component renders only after first render
}, []);
// Fetch categories on mount
useEffect(() => {
const fetchCategories = async () => {
const response = await request<Category[]>("/blogs/categories", {
requiresAuth: false,
});
if (response.status !== "Error" && response.data) {
setCategories(response.data);
}
};
fetchCategories();
}, []);
// Function to update query params
const updateQueryParams = (category: string) => {
const params = new URLSearchParams(searchParams.toString());
if (category === selectedCategory) {
params.delete("category");
} else {
params.set("category", category);
}
router.push(`${pathname}?${params.toString()}`, { scroll: false });
};
if (!isReady) {
return null; // Prevents rendering the wrong section before isMobile is determined
}
if (isMobile) {
return (
<div
className={`p-4 flex flex-row gap-2 ${isReady ? "animate-fadeIn" : ""}`}
>
<Select
key={selected}
onValueChange={(value) => {
setSelected(value);
updateQueryParams(value);
}}
value={selected}
>
<SelectTrigger>
<SelectValue placeholder="Sélectionner une catégorie" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Catégories</SelectLabel>
{categories.map((c) => (
<SelectItem key={c.category} value={c.category}>
{c.category}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
{selected && (
<Button
onClick={() => {
setSelected(undefined);
updateQueryParams(selected);
}}
>
<CircleXIcon />
</Button>
)}
</div>
);
}
return (
<div
className={`flex gap-2 flex-col p-5 lg:p-8 w-1/5 border-r ${isReady ? "animate-fadeIn" : ""}`}
>
<h2>Catégories</h2>
{categories.map((cat) => (
<Button
className={`w-full flex justify-between ${
selectedCategory === cat.category
? "bg-blue-500 text-white"
: ""
}`}
key={cat.category}
variant="secondary"
onClick={() => updateQueryParams(cat.category)}
>
<span>{cat.category}</span>
<span>{cat.count}</span>
</Button>
))}
</div>
);
}

View File

@@ -0,0 +1,27 @@
"use client";
import { Button } from "@/components/ui/button"; // You can use your button component from your UI library
import { Card } from "@/components/ui/card";
import { CircleXIcon, RotateCwIcon } from "lucide-react"; // Optional icon
export default function NoBlogs() {
return (
<div className="flex justify-center items-center p-8">
<Card className="w-full md:w-96 p-6 bg-transparent border-transparent shadow-none text-center flex flex-col">
<h3 className="text-xl font-semibold mb-4">
Aucun blog trouvé.
</h3>
<p className="text-gray-500 mb-6">
Nous n'avons pu trouver aucun blog.
</p>
<Button
variant="outline"
className="flex items-center justify-center gap-2"
onClick={() => window.location.reload()}
>
<RotateCwIcon className="h-4 w-4" />
Réessayer
</Button>
</Card>
</div>
);
}

View File

@@ -1,9 +1,34 @@
import Blog from "@/components/blog";
"use server";
import Blogs from "@/components/blog";
import request from "@/lib/request";
import { Blog } from "@/types/types";
import Categories from "./categories";
import NoBlogs from "./no-blogs";
export default async function History({
searchParams,
}: {
searchParams: Promise<{ category?: string }>;
}) {
const { category } = await searchParams;
let url = "/blogs";
if (category) url += `?category=${category}`;
const blogs = await request<Blog[]>(url, {
requiresAuth: false,
});
if (!blogs.data || blogs.data.length < 1) {
return <NoBlogs />;
}
if (blogs?.status === "Error") {
return <p>Un problème est survenue.</p>;
}
export default function History() {
return (
<div className="flex flex-col">
<Blog />
<div className="flex flex-col md:flex-row">
<Categories selectedCategory={category} />
<Blogs blogs={blogs.data} />
</div>
);
}