diff --git a/bun.lockb b/bun.lockb index 80cd6ae..dfc21fd 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/messages/en.json b/messages/en.json index cf538fa..6ed52c3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -44,6 +44,7 @@ "news": { "title": "News", "internalArticle": "This is an internal article", - "newArticle": "New article" + "newArticle": "New article", + "readTime": "{count, plural, =0 {less than a minute} one {# minute} other {# minutes}} read" } } diff --git a/messages/no.json b/messages/no.json index 6161c00..c0274bc 100644 --- a/messages/no.json +++ b/messages/no.json @@ -44,6 +44,7 @@ "news": { "title": "Nyheter", "internalArticle": "Dette er en intern artikkel", - "newArticle": "Ny artikkel" + "newArticle": "Ny artikkel", + "readTime": "{count, plural, =0 {mindre enn ett minutt} one {# minutt} other {# minutter}} read" } } diff --git a/package.json b/package.json index b37f6a5..e060b86 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ ] }, "dependencies": { + "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-separator": "^1.0.3", @@ -35,6 +36,7 @@ "nuqs": "^1.15.4", "react": "18.2.0", "react-dom": "18.2.0", + "reading-time": "^1.5.0", "sharp": "^0.33.2", "tailwind-merge": "^2.2.0", "tailwind-scrollbar": "^3.0.5", diff --git a/public/authorMock.jpg b/public/authorMock.jpg new file mode 100644 index 0000000..c3d5590 Binary files /dev/null and b/public/authorMock.jpg differ diff --git a/src/app/[locale]/(dashboard)/news/[article]/page.tsx b/src/app/[locale]/(dashboard)/news/[article]/page.tsx index 72727f8..8f84184 100644 --- a/src/app/[locale]/(dashboard)/news/[article]/page.tsx +++ b/src/app/[locale]/(dashboard)/news/[article]/page.tsx @@ -1,6 +1,13 @@ -import { articleMockData as articleData } from '@/mock-data/article'; +import { + articleMockData as articleData, + authorMockData as authorData, +} from '@/mock-data/article'; +import { useTranslations } from 'next-intl'; import { unstable_setRequestLocale } from 'next-intl/server'; import { notFound } from 'next/navigation'; +import readingTime from 'reading-time'; + +import { AvatarIcon } from '@/components/profile/AvatarIcon'; export async function generateStaticParams() { return articleData.map((article) => ({ @@ -28,15 +35,36 @@ export default function Article({ params: { locale: string; article: string }; }) { unstable_setRequestLocale(params.locale); + const t = useTranslations('news'); + const article = articleData.find( (article) => article.id === Number(params.article), ); if (!article) { return notFound(); } + + const { minutes } = readingTime(article.content!); + const author = authorData[0]!; return ( <>

{article.title}

+
+ +
+

{author.name}

+ + {t('readTime', { count: Math.ceil(minutes) })} +   •   + {article.date} + +
+
+

{article.content}

); } diff --git a/src/components/profile/AvatarIcon.tsx b/src/components/profile/AvatarIcon.tsx new file mode 100644 index 0000000..abfefcb --- /dev/null +++ b/src/components/profile/AvatarIcon.tsx @@ -0,0 +1,29 @@ +import Image from 'next/image'; + +import { Avatar, AvatarFallback } from '@/components/ui/Avatar'; + +type AvatarIconProps = { + className?: string; + photoUrl: string; + name: string; + initials: string; +}; + +function AvatarIcon({ className, photoUrl, name, initials }: AvatarIconProps) { + return ( + + {photoUrl && ( + {name} + )} + {!photoUrl && {initials}} + + ); +} + +export { AvatarIcon }; diff --git a/src/components/ui/Avatar.tsx b/src/components/ui/Avatar.tsx new file mode 100644 index 0000000..6aa1374 --- /dev/null +++ b/src/components/ui/Avatar.tsx @@ -0,0 +1,38 @@ +'use client'; + +import * as AvatarPrimitive from '@radix-ui/react-avatar'; +import * as React from 'react'; + +import { cx } from '@/lib/utils'; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarFallback }; diff --git a/src/mock-data/article.ts b/src/mock-data/article.ts index 1cb83dc..5fa6b74 100644 --- a/src/mock-data/article.ts +++ b/src/mock-data/article.ts @@ -5,6 +5,8 @@ const articleMockData = [ title: 'Gruppe status: prosjekt spill', date: '22. oktober 2023', photoUrl: 'mock.jpg', + content: + 'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Officiis aut perferendis a, deleniti accusantium amet sunt autem eligendi repellat soluta omnis, nisi quam at vero perspiciatis. Ex repellendus saepe excepturi. Quam repellendus culpa quia facilis, exercitationem ipsa voluptatem nostrum aut libero labore quisquam est sed odio modi, eius quaerat tenetur deserunt facere officiis odit quibusdam consequuntur, rem vel similique? Nesciunt. Possimus libero ab suscipit enim quia. Error rerum architecto quidem ad voluptates distinctio minima tempore vel veniam esse ipsum officia atque, voluptatem molestias magni corrupti ducimus, placeat sint blanditiis praesentium? Cumque dignissimos totam pariatur repellat quod, vitae alias nostrum! Porro sequi mollitia blanditiis nulla accusantium fugiat explicabo! Soluta rerum debitis voluptates. Esse asperiores soluta facere fuga? Quas facilis nam inventore! Reiciendis tempora autem commodi dolor in doloremque eius a veritatis doloribus aliquid. Animi ipsam voluptatum sequi, eveniet placeat laboriosam iure, ullam ex odio reiciendis dicta enim libero, cupiditate et? Non. Quis ut eos, quod laboriosam suscipit exercitationem ratione incidunt blanditiis animi veritatis. Quos possimus exercitationem dolor inventore, esse ipsum, quod placeat provident officia et ab nihil? Modi impedit soluta eveniet. Sit qui cupiditate mollitia corrupti, sapiente neque enim vel praesentium veritatis voluptatibus? Laudantium sit nulla assumenda! Esse obcaecati sint dolores quos dolorum aliquam cum excepturi autem ad, fuga culpa veritatis! Quo nisi accusantium voluptatibus ipsam quia, ratione consectetur cupiditate adipisci sequi, nobis ab animi dolorem hic. Voluptates repellat ut molestias harum eos illo, odio sapiente doloribus, minima quidem, reprehenderit eum. Optio ut repellendus repudiandae at odit! Voluptates quidem eos perferendis amet veritatis quo excepturi fuga ipsa sunt quod facilis saepe, libero ea, neque cupiditate. Sint inventore laudantium error? Consectetur, porro. Laborum id assumenda, repellat ipsam cupiditate dolorum provident quod nostrum beatae a praesentium sequi animi corporis consequuntur. Atque inventore porro eum vitae? Architecto, officiis fugit tempora deserunt temporibus totam tenetur!', }, { id: 2, @@ -163,4 +165,12 @@ const articleMockData = [ }, ]; -export { articleMockData }; +const authorMockData = [ + { + name: 'Michael Jackson', + initials: 'MJ', + photoUrl: 'authorMock.jpg', + }, +]; + +export { articleMockData, authorMockData };