article detail page
db error fixed
This commit is contained in:
parent
71dfd86187
commit
c1b5865feb
@ -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({
|
||||
|
||||
@ -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 })
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user