Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/sync-content.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ on:
- blog
- legacy-revival
schedule:
- cron: "17 */6 * * *"
- cron: "17 * * * *"
repository_dispatch:
types:
- sync-projects
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ tetapi tidak ditampilkan di indeks blog, tidak masuk sitemap, dan memakai
Workflow `.github/workflows/sync-content.yml` memperbarui data secara otomatis.

- Manual: jalankan workflow `Sync content data`.
- Schedule: berjalan setiap 6 jam.
- Schedule: berjalan setiap jam pada menit ke-17.
- Dispatch dari repo lain:
- `sync-projects`
- `sync-blog`
Expand Down
9 changes: 5 additions & 4 deletions src/components/PageHeader.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
interface Props {
eyebrow: string;
title: string;
centered?: boolean;
}

const { eyebrow, title } = Astro.props;
const { eyebrow, title, centered = false } = Astro.props;
---

<section class="relative overflow-hidden border-b border-line bg-paper py-14 md:py-20">
<div class="mx-auto w-[min(1120px,calc(100%-32px))]">
<div class="mb-5 flex items-center gap-3">
<div class:list={["mb-5 flex items-center gap-3", centered && "justify-center"]}>
<span class="sticker">{eyebrow}</span>
</div>
<h1 class="display-lg m-0 max-w-5xl text-ink">{title}</h1>
<div class="mt-6 max-w-3xl text-xl leading-8 text-muted">
<h1 class:list={["display-lg m-0 max-w-5xl text-ink", centered && "mx-auto text-center"]}>{title}</h1>
<div class:list={["mt-6 max-w-3xl text-xl leading-8 text-muted", centered && "mx-auto text-center"]}>
<slot />
</div>
</div>
Expand Down
46 changes: 42 additions & 4 deletions src/data/blog-posts.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/data/legacy.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"description": "List of All Programming Telegram Group",
"url": "https://github.com/hendisantika/List-All-Programming-Telegram-Group",
"homepage": "",
"stars": 1484,
"stars": 1483,
"forks": 364,
"language": "Crystal",
"topics": [
Expand All @@ -44,7 +44,7 @@
],
"license": "CC0-1.0",
"createdAt": "2017-04-06T23:39:58Z",
"updatedAt": "2026-07-13T09:56:32Z",
"updatedAt": "2026-07-20T18:42:02Z",
"pushedAt": "2025-08-03T03:05:42Z",
"archived": false,
"disabled": false
Expand Down
152 changes: 76 additions & 76 deletions src/data/projects.json

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions src/pages/404.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
import BaseButton from '../components/BaseButton.astro';
import BaseLayout from '../layouts/BaseLayout.astro';
import PageHeader from '../components/PageHeader.astro';
---

<BaseLayout
title="Halaman tidak ditemukan - IndopenSource"
description="Halaman yang Anda cari tidak ditemukan."
robots="noindex, nofollow"
>
<PageHeader eyebrow="404" title="Halaman tidak ditemukan." centered>
<p>Alamatnya mungkin berubah, salah ketik, atau halaman tersebut sudah tidak tersedia.</p>
<div class="mt-8 flex flex-wrap justify-center gap-3">
<BaseButton href="/">Kembali ke beranda</BaseButton>
<BaseButton href="/projects/" variant="secondary">Lihat proyek</BaseButton>
</div>
</PageHeader>
</BaseLayout>
110 changes: 46 additions & 64 deletions src/pages/blog.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,7 @@ import blogPosts from '../data/blog-posts.json';
import { withBase } from '../lib/urls';
import { isPublished, type BlogPost } from '../lib/content';

// Feature the first *published* post, never a draft (index 0 may be a draft).
const publishedPosts = (blogPosts as BlogPost[]).filter(isPublished);
const featuredPost = publishedPosts[0];
const otherPosts = publishedPosts.slice(1);
const featuredThumbnail = featuredPost?.thumbnail || '/brand/indopensource-hero.jpg';
const featuredThumbnailUrl = /^https?:\/\//.test(featuredThumbnail) ? featuredThumbnail : withBase(featuredThumbnail);
const featuredAlt = featuredPost
? `Thumbnail artikel: ${featuredPost.title}`
: 'Ilustrasi sampul Blog IndopenSource';
const featuredDate = featuredPost?.releasedAt
? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long' }).format(new Date(featuredPost.releasedAt))
: '';

const contributionSteps = [
{
Expand Down Expand Up @@ -68,69 +57,62 @@ const statusLegend = [
</div>
</PageHeader>

{featuredPost && (
{publishedPosts.length > 0 && (
<section class="border-b-[3px] border-ink bg-canvas py-16 md:py-20">
<div class="mx-auto w-[min(1120px,calc(100%-32px))]">
<div class="reveal reveal-1 mb-7 flex items-center gap-3">
<span class="section-number" aria-hidden="true">01</span>
<div class="min-w-0">
<span class="sticker">Artikel utama</span>
<p class="meta-row mt-2 font-semibold text-brand-dark uppercase tracking-[0.06em]">Artikel terbaru</p>
<span class="sticker">Artikel</span>
<p class="meta-row mt-2 font-semibold text-brand-dark uppercase tracking-[0.06em]">Terbaru lebih dulu</p>
</div>
<span class="rule grow"></span>
</div>

<article class="riso-hover riso-shadow grid overflow-hidden border-2 border-ink bg-paper md:grid-cols-[minmax(0,1fr)_minmax(280px,400px)]">
<div class="reveal reveal-2 flex flex-col p-6 md:p-8">
<div class="meta-row flex flex-wrap items-center gap-x-3 gap-y-1 font-semibold text-muted uppercase tracking-[0.06em]">
<span class="text-brand-dark">/blog/{featuredPost.slug}</span>
{featuredDate && <><span aria-hidden="true">·</span><span>{featuredDate}</span></>}
{featuredPost.author?.name && <><span aria-hidden="true">·</span><span class="text-brand-dark">oleh {featuredPost.author.name}</span></>}
</div>
<h2 class="display-md mt-4 max-w-2xl text-ink">{featuredPost.title}</h2>
<p class="mt-4 max-w-xl font-body text-lg leading-8 text-muted">{featuredPost.description}</p>
{featuredPost.tags?.length > 0 && (
<div class="mt-5 flex flex-wrap gap-2">
{featuredPost.tags.map((tag) => <span class="stamp">{tag}</span>)}
</div>
)}
<div class="mt-7">
<BaseButton href={`/blog/${featuredPost.slug}/`} variant="secondary">
Baca artikel
</BaseButton>
</div>
</div>
<div class="relative min-h-64 border-ink bg-ink max-md:border-t-2 md:border-l-2">
<img
class="h-full min-h-64 w-full object-cover mix-blend-luminosity opacity-95"
src={featuredThumbnailUrl}
alt={featuredAlt}
width="1280"
height="720"
loading="lazy"
decoding="async"
/>
</div>
</article>
</div>
</section>
)}
<div class="grid gap-6">
{publishedPosts.map((post) => {
const articleUrl = `/blog/${post.slug}/`;
const thumbnail = post.thumbnail || '/brand/indopensource-hero.jpg';
const thumbnailUrl = /^https?:\/\//.test(thumbnail) ? thumbnail : withBase(thumbnail);
const publishedDate = post.releasedAt
? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long', timeZone: 'UTC' }).format(new Date(post.releasedAt))
: '';
Comment thread
wauputr4 marked this conversation as resolved.

{otherPosts.length > 0 && (
<section class="border-b-[3px] border-ink bg-paper py-12 md:py-16">
<div class="mx-auto w-[min(1120px,calc(100%-32px))]">
<div class="mb-6 flex items-center gap-3"><span class="sticker">Artikel lainnya</span><span class="rule grow"></span></div>
<div class="grid gap-3 md:grid-cols-2">
{otherPosts.map((post) => (
<a class="riso-hover riso-shadow-sm block border-2 border-ink bg-canvas p-5" href={`/blog/${post.slug}/`}>
<span class="meta-row text-brand-dark">/blog/{post.slug}</span>
<h2 class="display-sm mt-3 text-ink">{post.title}</h2>
<p class="mt-2 font-body leading-7 text-muted">{post.description}</p>
{post.author?.name && (
<p class="meta-row mt-3 text-brand-dark">oleh {post.author.name}</p>
)}
</a>
))}
return (
<article class="riso-hover riso-shadow grid overflow-hidden border-2 border-ink bg-paper md:grid-cols-[minmax(0,1fr)_minmax(280px,400px)]">
<div class="flex flex-col p-6 md:p-8">
<div class="meta-row flex flex-wrap items-center gap-x-3 gap-y-1 font-semibold text-muted uppercase tracking-[0.06em]">
<span class="text-brand-dark">/blog/{post.slug}</span>
{publishedDate && <><span aria-hidden="true">·</span><span>{publishedDate}</span></>}
{post.author?.name && <><span aria-hidden="true">·</span><span class="text-brand-dark">oleh {post.author.name}</span></>}
</div>
<h2 class="display-md mt-4 max-w-2xl text-ink">
<a class="hover:text-brand" href={articleUrl}>{post.title}</a>
</h2>
<p class="mt-4 max-w-xl font-body text-lg leading-8 text-muted">{post.description}</p>
{post.tags?.length > 0 && (
<div class="mt-5 flex flex-wrap gap-2">
{post.tags.map((tag) => <span class="stamp">{tag}</span>)}
</div>
)}
<div class="mt-7">
<BaseButton href={articleUrl} variant="secondary">Baca artikel</BaseButton>
</div>
</div>
<a class="relative min-h-64 border-ink bg-ink max-md:border-t-2 md:border-l-2" href={articleUrl} aria-label={`Baca ${post.title}`}>
<img
class="h-full min-h-64 w-full object-cover mix-blend-luminosity opacity-95"
src={thumbnailUrl}
alt={`Thumbnail artikel: ${post.title}`}
width="1280"
height="720"
loading="lazy"
decoding="async"
/>
</a>
</article>
);
})}
</div>
</div>
</section>
Expand Down
8 changes: 4 additions & 4 deletions src/pages/blog/[...slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const seoTitle = post.slug === 'bagaimana-open-source-mampu-bertahan'
? 'Mengapa Open Source Bertahan Puluhan Tahun - IndopenSource'
: `${post.title} - IndopenSource`;
const releaseDate = post.releasedAt
? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long' }).format(new Date(post.releasedAt))
? new Intl.DateTimeFormat('id-ID', { dateStyle: 'long', timeZone: 'UTC' }).format(new Date(post.releasedAt))
: '';
const thumbnail = post.thumbnail || '/brand/indopensource-hero.jpg';
const thumbnailUrl = /^https?:\/\//.test(thumbnail) ? thumbnail : withBase(thumbnail);
Expand Down Expand Up @@ -58,19 +58,19 @@ const jsonLd = {
---

<BaseLayout title={seoTitle} description={post.description} image={thumbnailUrl} type="article" robots={published ? undefined : 'noindex, nofollow, noarchive'} publishedAt={published ? post.releasedAt : undefined} modifiedAt={post.lastModifiedAt} jsonLd={jsonLd}>
<PageHeader eyebrow="Blog" title={post.title}>
<PageHeader eyebrow="Blog" title={post.title} centered>
{!published && (
<div class="mb-6 border-2 border-ink bg-sun p-4 font-body text-ink riso-shadow-sm" role="status">
<strong class="font-display">Preview draf.</strong> Artikel ini masih ditinjau, belum diterbitkan, dan tidak diindeks mesin pencari.
</div>
)}
<p>{post.description}</p>
<div class="meta-row mt-5 flex flex-wrap items-center gap-x-3 gap-y-2 font-semibold text-ink uppercase tracking-[0.05em]">
<div class="meta-row mt-5 flex flex-wrap items-center justify-center gap-x-3 gap-y-2 font-semibold text-ink uppercase tracking-[0.05em]">
<span class="text-brand-dark">{articlePath}</span>
{releaseDate && <><span aria-hidden="true">·</span><span>{published ? 'Rilis' : 'Tanggal draf'} {releaseDate}</span></>}
{authors.length > 0 && <><span aria-hidden="true">·</span><span class="text-brand-dark">oleh {authors.map((author) => author.name).join(' & ')}</span></>}
</div>
<div class="mt-5 flex flex-wrap items-center gap-2">
<div class="mt-5 flex flex-wrap items-center justify-center gap-2">
<span class="sticker">{post.status}</span>
{post.tags.map((tag) => <span class="stamp">{tag}</span>)}
</div>
Expand Down
3 changes: 2 additions & 1 deletion wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "indopensource-org",
"compatibility_date": "2026-07-20",
"assets": {
"directory": "./dist"
"directory": "./dist",
"not_found_handling": "404-page"
}
}