Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions app/Http/Controllers/BlogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function index()
// 'message' => 'Blog list fetched successfully',
// 'datas' => $blogs,
// ]);
return inertia('Blogs/Index', [
return inertia('Blog/Index', [
'blogs' => $blogs,
]);
}
Expand All @@ -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,
]);
}

Expand Down
38 changes: 23 additions & 15 deletions resources/js/Components/Cards/HorizontalArticle.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
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';

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 (
<Link
href={`/articles/${props.slug}`}
href={url}
className="mb-7 flex flex-col items-center gap-5 sm:mb-5 sm:flex-row"
>
<img
src={props.cover}
alt="article cover"
alt={`${isArticle ? 'article' : 'blog'} cover`}
className="w-full rounded object-contain object-left sm:w-[400px] sm:min-w-[400px] sm:max-w-[400px] lg:w-[300px] lg:min-w-[300px] lg:max-w-[300px] xl:w-[400px] xl:min-w-[400px] xl:max-w-[400px]"
width={1280}
height={720}
/>
<div className="mb-5">
{props.course ? (
{/* Jika article, tampilkan informasi course */}
{isArticle && (props as Article).course ? (
<div className="my-2 flex gap-2 text-xs xl:text-sm">
<Link
href={`/courses/${props.course.slug}`}
href={`/courses/${(props as Article).course.slug}`}
className="flex items-center gap-2"
>
{props.course.visibility == 'private' && (
{(props as Article).course.visibility === 'private' && (
<span
title="Premium"
className="rounded bg-[#f0c322] p-0.5"
Expand All @@ -36,7 +44,7 @@ export default function HorizontalArticleCardComponent({
</span>
)}
<p className="text-xs font-semibold text-[#2276f0] md:text-sm">
{props.course.title}
{(props as Article).course.title}
</p>
</Link>
<p>&#128900;</p>
Expand All @@ -45,18 +53,18 @@ export default function HorizontalArticleCardComponent({
</p>
</div>
) : (
''
)}
<p className="font-bold sm:text-xl xl:text-2xl">
{props.title}
</p>
{!props.course ? (
// Jika Blog (tidak punya course)
<p className="mt-2 text-xs text-gray-500 md:text-sm">
{formatDateWithTime(props.created_at || '')}
</p>
) : (
''
)}

{/* Judul */}
<p className="font-bold sm:text-xl xl:text-2xl">
{props.title}
</p>

{/* Deskripsi */}
<p className="mt-3 text-sm text-gray-700 xl:text-base">
{props.description}
</p>
Expand Down
34 changes: 34 additions & 0 deletions resources/js/Components/Cards/SimpleBlog.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Link
href={`/blogs/${props.slug}`}
className={`${!withoutBorder && 'shadow'} mr-5`}
>
<img
src={props.cover}
alt="blog cover"
className="max-h-[150px] w-full rounded 2xl:max-h-[200px]"
width={1280}
height={720}
/>
<div
className={`-mt-1 rounded-b text-sm xl:text-base ${
!withoutBorder
? 'border border-t-0 border-[#2276f0] p-4 2xl:p-5'
: 'px-1 py-2 xl:py-4'
}`}
>
<p className="relative flex font-medium">{props.title}</p>
</div>
</Link>
);
}
1 change: 1 addition & 0 deletions resources/js/Components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
];

Expand Down
134 changes: 134 additions & 0 deletions resources/js/Pages/Blog/Index.tsx
Original file line number Diff line number Diff line change
@@ -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<Blog>;
}) {
const { props } = usePage();
const searchQuery = (props as any).search || '';
const [query, setQuery] = useState(searchQuery);

const handleSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
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 (
<>
<Head title="Blogs" />

<NavbarComponent />

{/* Background shapes */}
<img
src="/images/shapes/blueBlur2.svg"
alt="blueBlur"
className="absolute right-0 top-0 hidden xl:block"
loading="lazy"
/>
<img
src="/images/shapes/purpleBlur1.svg"
alt="purpleBlur"
className="absolute -top-52 left-0 hidden xl:block"
loading="lazy"
/>

<BreadcrumbComponent
links={[{ title: 'Blogs', url: '/blogs', active: true }]}
/>

<main className="container relative z-20 mx-auto mt-5 grid grid-cols-4 gap-8 px-3 md:px-10 xl:gap-10 xl:px-5 2xl:px-2">
<section className="col-span-4 lg:col-span-3">
<BannerHorizontalComponent />

{/* Daftar Blog */}
<section>
{blogs.data.map((blog, index) => (
<HorizontalArticleCardComponent
key={index}
props={blog}
/>
))}
</section>

{/* Pagination */}
<div className="mt-10 flex items-center justify-center gap-2">
<button
onClick={() => goToPage(blogs.prev_page_url)}
disabled={!blogs.prev_page_url}
className="flex items-center rounded border bg-white/50 px-3 py-1 disabled:opacity-50"
>
<IconChevronLeft className="size-5" /> Prev
</button>

{Array.from({ length: blogs.last_page }, (_, i) => i + 1).map(
(page) => (
<button
key={page}
onClick={() => goToPageNumber(page)}
className={`rounded border px-3 py-1 ${
page === blogs.current_page
? 'bg-[#4a86ef] text-white'
: 'bg-white/50 text-gray-800 backdrop-blur-md'
}`}
>
{page}
</button>
),
)}

<button
onClick={() => goToPage(blogs.next_page_url)}
disabled={!blogs.next_page_url}
className="flex items-center rounded border bg-white/50 px-3 py-1 disabled:opacity-50"
>
Next <IconChevronRight className="size-5" />
</button>
</div>
</section>

{/* Sidebar */}
<SideBlogsSection blogs={blogs.data} />
</main>

<FooterComponent />
</>
);
}
74 changes: 74 additions & 0 deletions resources/js/Pages/Blog/Show.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<Head title="Blog" />

<NavbarComponent />

<BreadcrumbComponent
links={[
{ title: 'Blogs', url: '/blogs' },
{
title: strLimit(blog?.title, 20),
url: `/blogs/${blog.slug}`,
active: true,
},
]}
/>

<section className="container mx-auto px-3 md:px-10 xl:px-5 2xl:px-2">
<section id="body" className="mt-20"></section>

<div className="mt-10 grid grid-cols-4 gap-8 xl:gap-10">
<main className="col-span-4 lg:col-span-3">
<header>
<h1 className="mb-2 text-xl font-bold sm:text-2xl xl:text-3xl">
{blog.title}
</h1>

<img
src={blog.cover}
alt="Cover Blog"
className="my-6 max-h-[300px] w-full rounded object-contain sm:max-h-[450px] lg:object-cover xl:max-h-[600px]"
/>
</header>

<section
id="body"
className="mt-5"
dangerouslySetInnerHTML={{
__html: blog.body,
}}
></section>
</main>

<SideBlogsSection blogs={blogs} />
</div>
</section>

<FooterComponent />
</>
);
}
2 changes: 1 addition & 1 deletion resources/js/Pages/Course/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default function CourseIndexPage({
/>
))}
</div>
<Link href={`/courses/${course.slug}`}>
<Link href={ `/courses/${course.slug}`}>
<h3 className="text-xl font-bold sm:text-2xl xl:text-3xl">
{course.title}
</h3>
Expand Down
2 changes: 1 addition & 1 deletion resources/js/Pages/TooltipDetail/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function Show({ glosary }: GlosaryDetailProps) {

<BreadcrumbComponent
links={[
{ title: 'Glosarium', url: '/glosarium' },
{ title: 'Glosarium', url: '/courses' },
{
title: glosary.title,
url: `/tooltip/${glosary.slug}`,
Expand Down
Loading