article detail page

db error fixed
This commit is contained in:
echo 2026-01-10 20:16:08 +01:00
parent 71dfd86187
commit c1b5865feb
2 changed files with 124 additions and 6 deletions

View File

@ -6,8 +6,22 @@ import {
UpdateDateColumn,
ManyToOne,
JoinColumn,
ValueTransformer,
} from 'typeorm';
class ArrayTransformer implements ValueTransformer {
to(value: string[]): string {
return JSON.stringify(value ?? []);
}
from(value: string): string[] {
try {
return value ? JSON.parse(value) : [];
} catch {
return [];
}
}
}
export enum ArticleStatus {
DRAFT = 'draft',
PUBLISHED = 'published',
@ -92,7 +106,11 @@ export class Article {
@Column({ default: '' })
featuredImage: string;
@Column({ type: 'text', default: '[]' })
@Column({
type: 'text',
default: '[]',
transformer: new ArrayTransformer(),
})
tags: string[];
@Column({

View File

@ -17,7 +17,7 @@ const rootRoute = createRootRoute({
<header className="border-b">
<div className="container mx-auto max-w-6xl px-4 py-4">
<h1 className="text-3xl font-bold">
<Link to="/">Placebo.mk</Link>
<Link to="/" className="hover:underline">Placebo.mk</Link>
</h1>
<nav className="flex gap-4">
<Link to="/" className="text-sm font-medium hover:underline">
@ -162,9 +162,10 @@ const articlesRoute = createRoute({
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{data?.data.map((article) => (
<div
<Link
key={article.id}
className="p-6 rounded-xl border bg-card hover:shadow-lg transition-shadow"
to={`/articles/${article.id}` as any}
className="p-6 rounded-xl border bg-card hover:shadow-lg transition-shadow cursor-pointer block"
>
<h2 className="text-xl font-semibold mb-2 line-clamp-2">
{article.title}
@ -184,7 +185,7 @@ const articlesRoute = createRoute({
</span>
<span>{article.views} views</span>
</div>
</div>
</Link>
))}
</div>
@ -200,7 +201,106 @@ const articlesRoute = createRoute({
},
})
const routeTree = rootRoute.addChildren([indexRoute, articlesRoute])
const articleDetailRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/articles/$id',
component: () => {
const { id } = articleDetailRoute.useParams()
const { data, isLoading, error } = useQuery({
queryKey: ['article', id],
queryFn: () => api.fetchArticleById(id),
})
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-lg">Loading article...</div>
</div>
)
}
if (error) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-lg text-red-500">Error loading article</div>
</div>
)
}
if (!data) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-lg">Article not found</div>
</div>
)
}
return (
<article className="max-w-3xl mx-auto">
<Link
to="/articles"
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground mb-8"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="m15 18-6-6 6-6" />
<path d="M19 6H5" />
</svg>
Back to articles
</Link>
<h1 className="text-4xl font-bold mb-6">{data.title}</h1>
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-8">
<span>
{new Date(data.createdAt).toLocaleDateString('mk-MK', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</span>
<span></span>
<span>{data.views} views</span>
{data.author && (
<>
<span></span>
<span>By {data.author.name}</span>
</>
)}
</div>
{data.featuredImage && (
<img
src={data.featuredImage}
alt={data.title}
className="w-full h-64 md:h-96 object-cover rounded-xl mb-8"
/>
)}
<div className="prose prose-slate max-w-none">
<p className="text-lg leading-relaxed mb-6">{data.content}</p>
</div>
{data.tags && Array.isArray(data.tags) && data.tags.length > 0 && (
<div className="mt-8 pt-8 border-t">
<h3 className="text-sm font-semibold mb-4 text-muted-foreground">Tags</h3>
<div className="flex flex-wrap gap-2">
{data.tags.map((tag) => (
<span
key={tag}
className="px-3 py-1 text-sm rounded-full bg-secondary text-secondary-foreground"
>
{tag}
</span>
))}
</div>
</div>
)}
</article>
)
},
})
const routeTree = rootRoute.addChildren([indexRoute, articlesRoute, articleDetailRoute])
export const router = createRouter({ routeTree })