Implement Astro features: React Islands, Server Endpoints, i18n, Partytown
Features added: - React integration with @astrojs/react - React components: DMRDashboard, MemberSearch - API endpoints: /api/repeaters.json, /api/dmr-contacts.json, /api/feed/[category].xml - i18n support for TR/EN locales with LanguageSwitcher component - Partytown enabled for third-party script optimization - Admin panel logo fixed (moved to public/images/) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
export async function GET() {
|
||||
const contacts = [
|
||||
{ id: '2867241', callsign: 'YM0KTC', name: 'ARC Merkez', location: 'Bölge 0 - İstanbul', type: 'repeater' },
|
||||
{ id: '2866808', callsign: 'YM1KTC', name: 'İstanbul Röle', location: 'Bölge 1 - İstanbul', type: 'repeater' },
|
||||
{ id: '2867887', callsign: 'YM2KTR', name: 'Ankara Röle', location: 'Bölge 2 - Ankara', type: 'repeater' },
|
||||
{ id: '2867234', callsign: 'YM3KTC', name: 'İzmir Röle', location: 'Bölge 3 - İzmir', type: 'repeater' },
|
||||
{ id: '2867235', callsign: 'YM4KTC', name: 'Antalya Röle', location: 'Bölge 4 - Antalya', type: 'repeater' },
|
||||
];
|
||||
|
||||
return Response.json(contacts);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import rss from '@astrojs/rss';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
// Helper to slugify Turkish categories
|
||||
function slugify(str: string): string {
|
||||
return str
|
||||
.toLowerCase()
|
||||
.replace(/ş/g, 's')
|
||||
.replace(/ı/g, 'i')
|
||||
.replace(/ğ/g, 'g')
|
||||
.replace(/ü/g, 'u')
|
||||
.replace(/ö/g, 'o')
|
||||
.replace(/ç/g, 'c')
|
||||
.replace(/[^a-z0-9]+/g, '-');
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection('post');
|
||||
|
||||
// Extract all unique categories and create slugified paths
|
||||
const categoryMap = new Map<string, string>();
|
||||
posts.forEach(post => {
|
||||
if (post.data.categories) {
|
||||
post.data.categories.forEach(cat => {
|
||||
if (!categoryMap.has(cat)) {
|
||||
categoryMap.set(cat, slugify(cat));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (post.data.category) {
|
||||
if (!categoryMap.has(post.data.category)) {
|
||||
categoryMap.set(post.data.category, slugify(post.data.category));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Return paths with both original category name and slug
|
||||
return Array.from(categoryMap.entries()).map(([category, slug]) => ({
|
||||
params: { category: slug },
|
||||
props: { originalCategory: category },
|
||||
}));
|
||||
}
|
||||
|
||||
export async function GET(context) {
|
||||
const posts = await getCollection('post');
|
||||
|
||||
// Get the original category name from props
|
||||
const originalCategory = context.props.originalCategory as string;
|
||||
|
||||
// Filter posts by category
|
||||
const filteredPosts = posts
|
||||
.filter(post =>
|
||||
post.data.categories?.includes(originalCategory) || post.data.category === originalCategory
|
||||
)
|
||||
.filter(post => post.data.publishDate) // Only include posts with publishDate
|
||||
.sort((a, b) => {
|
||||
const aDate = a.data.publishDate || new Date(0);
|
||||
const bDate = b.data.publishDate || new Date(0);
|
||||
return bDate.valueOf() - aDate.valueOf();
|
||||
})
|
||||
.slice(0, 20); // Limit to 20 most recent posts
|
||||
|
||||
return rss({
|
||||
title: `ARC - ${originalCategory} Blog Yazıları`,
|
||||
description: `Amatör Radyocular Derneği - ${originalCategory} kategorisindeki blog yazıları`,
|
||||
site: context.site?.toString() || 'https://radio.org.tr',
|
||||
items: filteredPosts.map(post => ({
|
||||
title: post.data.title,
|
||||
pubDate: post.data.publishDate!,
|
||||
description: post.data.excerpt || '',
|
||||
link: `/blog/${post.slug}/`,
|
||||
})),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
export async function GET() {
|
||||
const repeaters = [
|
||||
{
|
||||
id: 'YM1KTC',
|
||||
name: 'İstanbul Röle',
|
||||
frequency: '439.4125+',
|
||||
offset: '+7.6',
|
||||
tone: '88.5',
|
||||
colorCode: '1',
|
||||
status: 'online',
|
||||
lastheard: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: 'YM2KTR',
|
||||
name: 'Ankara Röle',
|
||||
frequency: '439.4750-',
|
||||
offset: '-5.0',
|
||||
tone: '88.5',
|
||||
colorCode: '1',
|
||||
status: 'online',
|
||||
lastheard: new Date(Date.now() - 3600000).toISOString(),
|
||||
},
|
||||
{
|
||||
id: 'YM3KTC',
|
||||
name: 'İzmir Röle',
|
||||
frequency: '439.5500+',
|
||||
offset: '+7.6',
|
||||
tone: '88.5',
|
||||
colorCode: '1',
|
||||
status: 'offline',
|
||||
lastheard: new Date(Date.now() - 86400000).toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
return Response.json(repeaters);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
import Layout from '~/layouts/PageLayout.astro';
|
||||
import Hero from '~/components/widgets/Hero.astro';
|
||||
import DMRDashboard from '~/react/DMRDashboard';
|
||||
import MemberSearch from '~/react/MemberSearch';
|
||||
|
||||
const metadata = {
|
||||
title: 'DMR Röle Durumu - ARC',
|
||||
};
|
||||
---
|
||||
|
||||
<Layout metadata={metadata}>
|
||||
<!-- Hero Widget -->
|
||||
<Hero
|
||||
title="📡 DMR Röle Durumu"
|
||||
subtitle="DMR röle sistemlerimizin anlık durumunu buradan takip edebilirsiniz."
|
||||
/>
|
||||
|
||||
<!-- Member Search -->
|
||||
<section class="mx-auto max-w-4xl px-4 py-8">
|
||||
<h2 class="text-2xl font-bold mb-4">🔍 Üye Ara</h2>
|
||||
<MemberSearch client:load />
|
||||
</section>
|
||||
|
||||
<!-- DMR Dashboard -->
|
||||
<section class="mx-auto max-w-6xl px-4 py-8">
|
||||
<h2 class="text-2xl font-bold mb-4">📻 Röle Durumları</h2>
|
||||
<DMRDashboard client:load />
|
||||
</section>
|
||||
</Layout>
|
||||
Reference in New Issue
Block a user