diff --git a/app/Http/Controllers/BlogController.php b/app/Http/Controllers/BlogController.php index 68eb947..9ee6bab 100644 --- a/app/Http/Controllers/BlogController.php +++ b/app/Http/Controllers/BlogController.php @@ -20,7 +20,7 @@ public function index() // 'message' => 'Blog list fetched successfully', // 'datas' => $blogs, // ]); - return inertia('Blogs/Index', [ + return inertia('Blog/Index', [ 'blogs' => $blogs, ]); } @@ -47,13 +47,14 @@ public function store(Request $request) public function show(string $slug) { $blog = Blog::where('slug', $slug)->firstOrFail(); - // return response()->json([ - // 'success' => true, - // 'message' => 'Blog detail fetched successfully', - // 'datas' => $blog, - // ]); - return inertia('Blogs/Show', [ + $blogs = Blog::select('title', 'slug', 'cover') + ->where('id', '!=', $blog->id) + ->latest() + ->take(5) + ->get(); + return inertia('Blog/Show', [ 'blog' => $blog, + 'blogs' => $blogs, ]); } diff --git a/resources/js/Components/Cards/HorizontalArticle.tsx b/resources/js/Components/Cards/HorizontalArticle.tsx index f58b9ee..40226db 100644 --- a/resources/js/Components/Cards/HorizontalArticle.tsx +++ b/resources/js/Components/Cards/HorizontalArticle.tsx @@ -1,4 +1,5 @@ import { Article } from '@/models/Article'; +import { Blog } from '@/models/Blog'; import { formatDateWithTime } from '@/tools/formatDate'; import { Link } from '@inertiajs/react'; import { IconRocketFill } from 'justd-icons'; @@ -6,28 +7,35 @@ import { IconRocketFill } from 'justd-icons'; export default function HorizontalArticleCardComponent({ props, }: { - props: Article; + props: Article | Blog; }) { + // Cek apakah props berasal dari Article (punya course) + const isArticle = (props as Article).course !== undefined; + + // Tentukan URL dinamis berdasarkan tipe + const url = isArticle ? `/articles/${props.slug}` : `/blogs/${props.slug}`; + return ( article cover
- {props.course ? ( + {/* Jika article, tampilkan informasi course */} + {isArticle && (props as Article).course ? (
- {props.course.visibility == 'private' && ( + {(props as Article).course.visibility === 'private' && ( )}

- {props.course.title} + {(props as Article).course.title}

🞄

@@ -45,18 +53,18 @@ export default function HorizontalArticleCardComponent({

) : ( - '' - )} -

- {props.title} -

- {!props.course ? ( + // Jika Blog (tidak punya course)

{formatDateWithTime(props.created_at || '')}

- ) : ( - '' )} + + {/* Judul */} +

+ {props.title} +

+ + {/* Deskripsi */}

{props.description}

diff --git a/resources/js/Components/Cards/SimpleBlog.tsx b/resources/js/Components/Cards/SimpleBlog.tsx new file mode 100644 index 0000000..0c75a51 --- /dev/null +++ b/resources/js/Components/Cards/SimpleBlog.tsx @@ -0,0 +1,34 @@ +import { Blog } from '@/models/Blog'; +import { Link } from '@inertiajs/react'; + +export default function SimpleBlogCardComponent({ + props, + withoutBorder = false, +}: { + props: Blog; + withoutBorder?: boolean; +}) { + return ( + + blog cover +
+

{props.title}

+
+ + ); +} diff --git a/resources/js/Components/Navbar.tsx b/resources/js/Components/Navbar.tsx index fa7183e..3736b58 100644 --- a/resources/js/Components/Navbar.tsx +++ b/resources/js/Components/Navbar.tsx @@ -16,6 +16,7 @@ export default function NavbarComponent() { { name: 'Home', href: '/' }, { name: 'Course', href: '/courses' }, { name: 'Article', href: '/articles' }, + { name: 'Blog', href: '/blogs' }, { name: 'Video', href: '/videos/pendahuluan' }, ]; diff --git a/resources/js/Pages/Blog/Index.tsx b/resources/js/Pages/Blog/Index.tsx new file mode 100644 index 0000000..549d628 --- /dev/null +++ b/resources/js/Pages/Blog/Index.tsx @@ -0,0 +1,134 @@ +import BannerHorizontalComponent from '@/Components/Banners/Horizontal'; +import BreadcrumbComponent from '@/Components/Breadcrumb'; +import HorizontalArticleCardComponent from '@/Components/Cards/HorizontalArticle'; +import FooterComponent from '@/Components/Footer'; +import NavbarComponent from '@/Components/Navbar'; +import { Blog } from '@/models/Blog'; +import SideBlogsSection from '@/Sections/SideBlogs'; +import { PaginatedResponse } from '@/types/PaginateResponse'; +import { Inertia, Method } from '@inertiajs/inertia'; +import { Head, router, usePage } from '@inertiajs/react'; + +import 'glider-js/glider.min.css'; +import { IconChevronLeft, IconChevronRight } from 'justd-icons'; +import { useEffect, useState } from 'react'; + +export default function BlogIndexPage({ + blogs, +}: { + blogs: PaginatedResponse; +}) { + const { props } = usePage(); + const searchQuery = (props as any).search || ''; + const [query, setQuery] = useState(searchQuery); + + const handleSearch = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + Inertia.visit(route('blogs.index'), { + method: Method.GET, + data: { q: query }, + preserveState: true, + preserveScroll: true, + replace: true, + only: ['blogs'], + }); + } + }; + + useEffect(() => { + setQuery(searchQuery); + console.log('search q', searchQuery); + }, [searchQuery]); + + const goToPage = (url: string | null) => { + if (url) { + router.visit(url); + } + }; + + const goToPageNumber = (page: number) => { + router.visit(`${blogs.path}?page=${page}`); + }; + + return ( + <> + + + + + {/* Background shapes */} + blueBlur + purpleBlur + + + +
+
+ + + {/* Daftar Blog */} +
+ {blogs.data.map((blog, index) => ( + + ))} +
+ + {/* Pagination */} +
+ + + {Array.from({ length: blogs.last_page }, (_, i) => i + 1).map( + (page) => ( + + ), + )} + + +
+
+ + {/* Sidebar */} + +
+ + + + ); +} diff --git a/resources/js/Pages/Blog/Show.tsx b/resources/js/Pages/Blog/Show.tsx new file mode 100644 index 0000000..309677a --- /dev/null +++ b/resources/js/Pages/Blog/Show.tsx @@ -0,0 +1,74 @@ +import FooterComponent from '@/Components/Footer'; +import NavbarComponent from '@/Components/Navbar'; +import { Head } from '@inertiajs/react'; + +import BreadcrumbComponent from '@/Components/Breadcrumb'; +import { Blog } from '@/models/Blog'; +import SideBlogsSection from '@/Sections/SideBlogs'; +import strLimit from '@/tools/strLimit'; +import '../../../css/bodyContent.css'; +import { useEffect } from 'react'; + +export default function BlogShowPage({ + blog, + blogs, +}: { + blog: Blog; + blogs: Blog[]; +}) { + + useEffect(() => { + console.log('blogs', blogs); + }, [blogs]); + + return ( + <> + + + + + + +
+
+ +
+
+
+

+ {blog.title} +

+ + Cover Blog +
+ +
+
+ + +
+
+ + + + ); +} diff --git a/resources/js/Pages/Course/Index.tsx b/resources/js/Pages/Course/Index.tsx index 5ec69b0..c365d0f 100644 --- a/resources/js/Pages/Course/Index.tsx +++ b/resources/js/Pages/Course/Index.tsx @@ -58,7 +58,7 @@ export default function CourseIndexPage({ /> ))}
- +

{course.title}

diff --git a/resources/js/Pages/TooltipDetail/Show.tsx b/resources/js/Pages/TooltipDetail/Show.tsx index 721f7db..7e1bda5 100644 --- a/resources/js/Pages/TooltipDetail/Show.tsx +++ b/resources/js/Pages/TooltipDetail/Show.tsx @@ -35,7 +35,7 @@ export default function Show({ glosary }: GlosaryDetailProps) { +

+ // Blog Terbaru +

+ + {blogs.map( + (blog, index) => + index < 5 && ( +
+ + {blog.title} + +
+ ), + )} + +
+

+ // Sorotan +

+ {blogs.map( + (blog, index) => + index < 2 && ( + + ), + )} +
+ + ); +} diff --git a/resources/js/models/Blog.ts b/resources/js/models/Blog.ts new file mode 100644 index 0000000..9805555 --- /dev/null +++ b/resources/js/models/Blog.ts @@ -0,0 +1,9 @@ +export type Blog = { + id: string; + slug: string; + title: string; + description: string; + cover: string; + body: string; + created_at?: string; +}; diff --git a/routes/web.php b/routes/web.php index f3175a1..465714c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -27,6 +27,8 @@ Route::resource('/courses', CourseController::class); Route::resource('/videos', VideoController::class); Route::get('/glosarium/{slug}', [GlossaryController::class, 'show'])->name('glosarium.show'); +Route::get('/blogs', [BlogController::class, 'index'])->name('blogs.index'); +Route::get('/blogs/{slug}', [BlogController::class, 'show'])->name('blogs.show'); Route::resource('/blogs', BlogController::class); // Route::get('/glosarium', [GlossaryController::class, 'index'])->name('glosarium.index');