Skip to content

Commit 0078242

Browse files
changed sanity schema for translation (#119)
1 parent aeca682 commit 0078242

14 files changed

Lines changed: 4278 additions & 4428 deletions

File tree

frontend/app/[locale]/blog/[slug]/PostDetails.tsx

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Image from 'next/image';
22
import { notFound } from 'next/navigation';
33
import groq from 'groq';
4+
import { getTranslations } from 'next-intl/server';
45
import { client } from '@/client';
56

67
type SocialLink = {
@@ -41,24 +42,24 @@ function plainTextFromPortableText(value: any): string {
4142

4243
const query = groq`
4344
*[_type=="post" && slug.current==$slug][0]{
44-
title,
45+
"title": coalesce(title[$locale], title.en, title),
4546
publishedAt,
4647
"mainImage": mainImage.asset->url,
4748
"categories": categories[]->title,
4849
tags,
4950
resourceLink,
5051
5152
"author": author->{
52-
name,
53-
company,
54-
jobTitle,
55-
city,
56-
bio,
53+
"name": coalesce(name[$locale], name.en, name),
54+
"company": coalesce(company[$locale], company.en, company),
55+
"jobTitle": coalesce(jobTitle[$locale], jobTitle.en, jobTitle),
56+
"city": coalesce(city[$locale], city.en, city),
57+
"bio": coalesce(bio[$locale], bio.en, bio),
5758
"image": image.asset->url,
5859
socialMedia[]{ _key, platform, url }
5960
},
6061
61-
body[]{
62+
"body": coalesce(body[$locale], body.en, body)[]{
6263
...,
6364
_type == "image" => {
6465
...,
@@ -68,15 +69,32 @@ const query = groq`
6869
}
6970
`;
7071

71-
export default async function PostDetails({ slug }: { slug: string }) {
72+
export default async function PostDetails({
73+
slug,
74+
locale,
75+
}: {
76+
slug: string;
77+
locale: string;
78+
}) {
79+
const t = await getTranslations({ locale, namespace: 'blog' });
7280
const slugParam = String(slug || '').trim();
7381
if (!slugParam) return notFound();
7482

75-
const post: Post | null = await client.fetch(query, { slug: slugParam });
83+
const post: Post | null = await client.fetch(query, {
84+
slug: slugParam,
85+
locale,
86+
});
7687

7788
if (!post?.title) return notFound();
7889

7990
const authorBio = plainTextFromPortableText(post.author?.bio);
91+
const authorName = post.author?.name;
92+
const authorMetaParts = [
93+
post.author?.jobTitle,
94+
post.author?.company,
95+
post.author?.city,
96+
].filter(Boolean) as string[];
97+
const authorMeta = authorMetaParts.join(' · ');
8098

8199
return (
82100
<main className="max-w-3xl mx-auto px-6 py-12">
@@ -113,12 +131,12 @@ export default async function PostDetails({ slug }: { slug: string }) {
113131
)}
114132

115133
{post.mainImage && (
116-
<div className="relative w-full h-[320px] rounded-2xl overflow-hidden border border-gray-200 my-8">
134+
<div className="relative w-full h-[420px] rounded-2xl overflow-hidden border border-gray-200 my-8">
117135
<Image
118136
src={post.mainImage}
119137
alt={post.title || 'Post image'}
120138
fill
121-
className="object-cover"
139+
className="object-cover object-top scale-[1.05]"
122140
/>
123141
</div>
124142
)}
@@ -160,12 +178,37 @@ export default async function PostDetails({ slug }: { slug: string }) {
160178
</div>
161179
)}
162180

163-
{(authorBio || post.author?.jobTitle) && (
181+
{(authorBio || authorName || authorMeta) && (
164182
<section className="mt-12 p-6 rounded-2xl border border-gray-200 bg-white">
165-
<h2 className="text-lg font-semibold">About the author</h2>
166-
<p className="mt-2 text-sm text-gray-700 whitespace-pre-line">
167-
{authorBio}
168-
</p>
183+
<h2 className="text-lg font-semibold">{t('aboutAuthor')}</h2>
184+
<div className="mt-4 flex items-start gap-4">
185+
{post.author?.image && (
186+
<div className="relative w-14 h-14 shrink-0">
187+
<Image
188+
src={post.author.image}
189+
alt={authorName || 'Author'}
190+
fill
191+
className="rounded-full object-cover border border-gray-200"
192+
/>
193+
</div>
194+
)}
195+
196+
<div className="min-w-0">
197+
{authorName && (
198+
<p className="text-sm font-semibold text-gray-900">
199+
{authorName}
200+
</p>
201+
)}
202+
{authorMeta && (
203+
<p className="mt-1 text-sm text-gray-600">{authorMeta}</p>
204+
)}
205+
{authorBio && (
206+
<p className="mt-3 text-sm text-gray-700 whitespace-pre-line leading-relaxed">
207+
{authorBio}
208+
</p>
209+
)}
210+
</div>
211+
</div>
169212
</section>
170213
)}
171214
</main>

frontend/app/[locale]/blog/[slug]/page.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ export async function generateStaticParams() {
1515
export async function generateMetadata({
1616
params,
1717
}: {
18-
params: Promise<{ slug: string }>;
18+
params: Promise<{ slug: string; locale: string }>;
1919
}) {
20-
const { slug } = await params;
20+
const { slug, locale } = await params;
2121

2222
const post = await client.fetch(
23-
groq`*[_type == "post" && slug.current == $slug][0]{ title }`,
24-
{ slug }
23+
groq`*[_type == "post" && slug.current == $slug][0]{ "title": coalesce(title[$locale], title.en, title) }`,
24+
{ slug, locale }
2525
);
2626

2727
return {
@@ -32,8 +32,8 @@ export async function generateMetadata({
3232
export default async function Page({
3333
params,
3434
}: {
35-
params: Promise<{ slug: string }>;
35+
params: Promise<{ slug: string; locale: string }>;
3636
}) {
37-
const { slug } = await params;
38-
return <PostDetails slug={slug} />;
37+
const { slug, locale } = await params;
38+
return <PostDetails slug={slug} locale={locale} />;
3939
}

frontend/app/[locale]/blog/page.tsx

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,43 @@ export default async function BlogPage({
2525
const { locale } = await params;
2626
const t = await getTranslations({ locale, namespace: 'blog' });
2727

28-
const posts = await client.fetch(groq`
29-
*[_type == "post" && defined(slug.current)]
30-
| order(publishedAt desc) {
31-
_id,
32-
title,
33-
slug,
34-
publishedAt,
35-
tags,
36-
resourceLink,
28+
const posts = await client.fetch(
29+
groq`
30+
*[_type == "post" && defined(slug.current)]
31+
| order(publishedAt desc) {
32+
_id,
33+
"title": coalesce(title[$locale], title.en, title),
34+
slug,
35+
publishedAt,
36+
tags,
37+
resourceLink,
3738
38-
"categories": categories[]->title,
39+
"categories": categories[]->title,
3940
40-
body[] {
41-
...,
42-
children[]{
43-
text
44-
}
45-
},
46-
"mainImage": mainImage.asset->url,
41+
"body": coalesce(body[$locale], body.en, body)[]{
42+
...,
43+
children[]{
44+
text
45+
}
46+
},
47+
"mainImage": mainImage.asset->url,
4748
"author": author->{
48-
name,
49-
company,
50-
jobTitle,
51-
city,
52-
bio,
49+
"name": coalesce(name[$locale], name.en, name),
50+
"company": coalesce(company[$locale], company.en, company),
51+
"jobTitle": coalesce(jobTitle[$locale], jobTitle.en, jobTitle),
52+
"city": coalesce(city[$locale], city.en, city),
53+
"bio": coalesce(bio[$locale], bio.en, bio),
5354
"image": image.asset->url,
5455
socialMedia[]{
5556
_key,
56-
platform,
57-
url
57+
platform,
58+
url
59+
}
5860
}
5961
}
60-
}
61-
`);
62+
`,
63+
{ locale }
64+
);
6265

6366
return (
6467
<main className="max-w-6xl mx-auto px-6 py-12">

frontend/components/blog/AuthorModal.tsx

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ export default function AuthorModal({
110110
);
111111

112112
const formattedDate = useMemo(() => formatDateGB(publishedAt), [publishedAt]);
113+
const authorName = author?.name || 'Unknown author';
114+
const metaText = formattedDate ? `${authorName} · ${formattedDate}` : authorName;
113115

114116
useEffect(() => setMounted(true), []);
115117

@@ -139,26 +141,9 @@ export default function AuthorModal({
139141
<button
140142
type="button"
141143
onClick={() => setOpen(true)}
142-
className="mt-4 flex items-center gap-3 text-sm text-gray-700 hover:opacity-80 transition text-left"
144+
className="mt-3 text-[12px] md:text-[13px] text-gray-500 hover:text-[#ff00ff] hover:underline underline-offset-4 transition text-left"
143145
>
144-
{author?.image && (
145-
<div className="relative w-9 h-9 shrink-0">
146-
<Image
147-
src={author.image}
148-
alt={author?.name || 'Author'}
149-
fill
150-
className="rounded-full object-cover border border-gray-300"
151-
/>
152-
</div>
153-
)}
154-
155-
<div className="flex flex-col">
156-
<span className="font-medium">{author?.name || 'Unknown author'}</span>
157-
158-
{formattedDate && (
159-
<span className="text-gray-500 text-xs">{formattedDate}</span>
160-
)}
161-
</div>
146+
{metaText}
162147
</button>
163148
);
164149

@@ -193,7 +178,7 @@ export default function AuthorModal({
193178
transition
194179
"
195180
>
196-
<span className="text-xl leading-none">×</span>
181+
<span className="text-xl leading-none -mt-px">×</span>
197182
</button>
198183

199184
<div className="flex items-center gap-4">
@@ -240,7 +225,7 @@ export default function AuthorModal({
240225
)}
241226

242227
{hasSocial && (
243-
<div className="mt-5">
228+
<div className="mt-5 border-t border-gray-100 pt-4">
244229
<h3 className="text-sm font-semibold text-gray-900 mb-2">
245230
Social links
246231
</h3>

0 commit comments

Comments
 (0)