Skip to content

Commit

Permalink
Merge pull request #10 from KSET/develop
Browse files Browse the repository at this point in the history
Add news pages
  • Loading branch information
Allypost authored Aug 3, 2023
2 parents aa41caa + 1aaa05b commit 075a4da
Show file tree
Hide file tree
Showing 13 changed files with 939 additions and 311 deletions.
7 changes: 6 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ const config = {
destination: "/contact",
permanent: false,
},
{
source: "/gallery/live/:slug",
destination: "/multimedia/live/:slug",
permanent: false,
},
]);
},

Expand All @@ -69,7 +74,7 @@ const config = {
},

experimental: {
// appDir: true,
typedRoutes: true,
adjustFontFallbacks: true,
},
};
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@accessible/drawer": "^3.0.2",
"@hookform/resolvers": "^3.0.1",
"@next-auth/prisma-adapter": "^1.0.5",
"@prisma/client": "^4.11.0",
"@prisma/client": "^5.0.0",
"@radix-ui/react-aspect-ratio": "^1.0.2",
"@tanstack/react-query": "^4.28.0",
"@trpc/client": "^10.18.0",
Expand All @@ -23,14 +23,14 @@
"embla-carousel-react": "^7.1.0",
"html-to-text": "^9.0.5",
"lodash": "^4.17.21",
"next": "~13.2.4",
"next": "^13.4.10",
"next-auth": "4.20.1",
"next-seo": "^6.0.0",
"nprogress": "^0.2.0",
"rambdax": "^9.1.0",
"react": "18.2.0",
"react": "^18.2.0",
"react-cookie": "^4.1.1",
"react-dom": "18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.9",
"react-icons": "^4.8.0",
"sharp": "^0.32.0",
Expand All @@ -47,26 +47,26 @@
"@types/node": "^18.15.5",
"@types/nprogress": "^0.2.0",
"@types/prettier": "^2.7.2",
"@types/react": "^18.0.28",
"@types/react": "^18.2.8",
"@types/react-dom": "^18.0.11",
"@types/sharp": "^0.31.1",
"@typescript-eslint/eslint-plugin": "^5.56.0",
"@typescript-eslint/parser": "^5.56.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.36.0",
"eslint-config-next": "~13.2.4",
"eslint-config-next": "^13.4.9",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"postcss": "^8.4.21",
"prettier": "^2.8.6",
"prettier-plugin-tailwindcss": "^0.2.6",
"prisma": "^4.11.0",
"prisma": "^5.0.0",
"sass": "^1.60.0",
"tailwindcss": "^3.3.0",
"type-fest": "^3.8.0",
"typescript": "^5.0.2"
"typescript": "^5.1.3"
},
"ct3aMetadata": {
"initVersion": "7.10.2"
Expand Down
35 changes: 35 additions & 0 deletions src/components/base/aspect-ratio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { type FC, type HTMLProps, type PropsWithChildren } from "react";

import { cn } from "~/utils/class";

export type AspectRatioPropsStrict = {
ratio?: number;
};

export type AspectRatioProps = PropsWithChildren<
HTMLProps<HTMLDivElement> & AspectRatioPropsStrict
>;

const AspectRatio: FC<AspectRatioProps> = (props) => {
const ratio = props.ratio ?? 1;

return (
<div
className="relative w-full"
style={{
...props.style,
paddingBottom: `${100 / ratio}%`,
}}
>
<div
{...props}
className={cn(
props.className,
"absolute bottom-0 left-0 right-0 top-0",
)}
/>
</div>
);
};

export default AspectRatio;
6 changes: 2 additions & 4 deletions src/components/base/image/app-image/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {
AspectRatio,
type AspectRatioProps,
} from "@radix-ui/react-aspect-ratio";
import { type DetailedHTMLProps, type FC, type ImgHTMLAttributes } from "react";

import { cn } from "~/utils/class";
import { type Src } from "~/utils/kset-image";

import AspectRatio, { type AspectRatioProps } from "../../aspect-ratio";

export type ImgProps = DetailedHTMLProps<
ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
Expand Down
7 changes: 2 additions & 5 deletions src/components/base/image/variant-image/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type FC, useMemo } from "react";
import { type FC } from "react";

import { cn } from "~/utils/class";
import { src as imgSrc, urlVariants } from "~/utils/kset-image";
Expand All @@ -10,10 +10,7 @@ const VariantImage: FC<
aspectRatio?: number;
}
> = ({ src, alt, aspect, aspectRatio, ...props }) => {
const ratio = useMemo(
() => aspectRatio ?? aspect?.ratio ?? 3 / 2,
[aspect?.ratio, aspectRatio],
);
const ratio = aspectRatio ?? aspect?.ratio ?? 3 / 2;

if (!src) {
return <div />;
Expand Down
5 changes: 5 additions & 0 deletions src/pages/news/[slug]/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.newsContent {
p:not(:first-child) {
margin-top: 1rem;
}
}
222 changes: 215 additions & 7 deletions src/pages/news/[slug]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,225 @@
import { type NextPage } from "next";
import { useRouter } from "next/router";
import { type GetServerSidePropsContext } from "next";
import Link from "next/link";
import { NextSeo } from "next-seo";
import { RxArrowLeft as IconArrowLeft } from "react-icons/rx";

const PageNewsItem: NextPage = () => {
const slug = useRouter().query.slug as string;
import VariantImage from "~/components/base/image/variant-image";
import { MainLayout } from "~/layouts/main";
import { type NextPageWithLayout } from "~/types/layout";
import { type ServerSideProps } from "~/types/server";
import { type Maybe } from "~/types/util";
import { type RouterOutputs } from "~/utils/api";
import { cn } from "~/utils/class";
import { src } from "~/utils/kset-image";
import { createApi } from "~/utils/serverApi";

import $style from "./index.module.scss";

type NewsItem = NonNullable<RouterOutputs["news"]["getNewsItem"]>;
type NewsItemFixed = NonNullable<ReturnType<typeof fixNewsItem>>;

const fixNewsItem = (newsItem: Maybe<NewsItem>) => {
if (!newsItem) {
return null;
}

return {
...newsItem,
// eslint-disable-next-line camelcase
created_at: newsItem.created_at.toISOString(),
// eslint-disable-next-line camelcase
expire_at: newsItem.expire_at?.toISOString() ?? null,
};
};

export const getServerSideProps = async (
context: GetServerSidePropsContext,
) => {
const helpers = await createApi(context);
const slug = context.params!.slug! as string;
const [newsItem, recentNews] = await Promise.all([
helpers.news.getNewsItem.fetch({
slug,
}),
helpers.news.getNews.fetch({
count: 3,
}),
] as const);

return {
notFound: !newsItem,
props: {
newsItem: fixNewsItem(newsItem),
recentNews: recentNews.map(fixNewsItem),
},
};
};

type Props = ServerSideProps<typeof getServerSideProps>;

// quick and dirty html stripping
const dirtyStripHtml = (html: Maybe<string>) => html?.replace(/<[^>]+>/g, "");

const trimToLength = (str: Maybe<string>, length: number) => {
if (!str) {
return undefined;
}

if (str.length <= length) {
return str;
}

return `${str.slice(0, length - 1)}…`;
};

const getNewsDescription = (newsItem: Maybe<NewsItem | NewsItemFixed>) => {
const content = dirtyStripHtml(newsItem?.description ?? newsItem?.content);

return trimToLength(content, 250);
};

const PageNewsItem: NextPageWithLayout<Props> = ({ newsItem, recentNews }) => {
const description = getNewsDescription(newsItem);

if (!newsItem) {
return null;
}

const date = new Date(newsItem.created_at);
const thumbSrc = src(newsItem.thumb);

return (
<>
<NextSeo title={`Vijest - ${slug}`} />
Vijest
<pre>{slug}</pre>
<NextSeo
description={description}
title={newsItem.subject}
openGraph={
thumbSrc
? {
images: [
{
url: thumbSrc,
},
],
}
: undefined
}
/>
<div className="container mt-8 grid-cols-[1fr,4fr,1fr] justify-items-center tracking-wide br:mt-32 br:grid">
<Link
className="flex items-center gap-1 self-baseline justify-self-start font-bold leading-5 tracking-wider no-underline opacity-80 transition-opacity duration-300 hover:underline hover:opacity-100 hover:duration-0 max-br:mb-4"
href={{
pathname: "/news",
query: {
year: date.getFullYear(),
},
hash: `#news_${newsItem.slug!}`,
}}
>
<IconArrowLeft /> Povratak
</Link>

<div className="pb-12">
<time
className="mb-2 block text-sm tracking-widest"
dateTime={date.toISOString()}
>
{date.toLocaleDateString("hr-HR")}
</time>
<h1 className="text-4xl font-bold">{newsItem.subject}</h1>
<h3
dangerouslySetInnerHTML={{
__html: newsItem.description ?? "",
}}
className="mt-4 text-xl opacity-60"
/>
</div>
</div>

<article className="bg-white text-black">
<div className="bg-gradient-to-b from-off-black from-40% to-40%">
<div className="br:container">
<VariantImage
alt={newsItem.subject}
className="h-full w-full"
src={thumbSrc}
aspect={{
ratio: 16 / 8,
}}
/>
</div>
</div>
<div className="float-none clear-none w-auto py-8 br:py-16">
<div className="flex justify-center br:container">
<div
dangerouslySetInnerHTML={{
__html: newsItem.content ?? "",
}}
className={cn("w-4/5 br:w-2/3", $style.newsContent)}
/>
</div>
</div>
</article>

<div className="container py-14">
<h2 className="text-2xl font-bold uppercase tracking-widest text-white opacity-30">
Zadnje objave
</h2>

<div className="mt-8 grid grid-cols-1 items-baseline gap-8 br:grid-cols-3">
{recentNews?.filter(Boolean).map((newsItem) => {
const date = new Date(newsItem.created_at);
const thumbSrc = src(newsItem.thumb);

return (
<article key={newsItem.id}>
<Link
className="text-white no-underline opacity-90 transition-opacity duration-300 hover:opacity-100 hover:duration-0"
href={{
pathname: "/news/[slug]",
query: {
slug: newsItem.slug,
},
}}
>
<div>
<VariantImage
alt={newsItem.subject}
aspectRatio={16 / 10}
className="w-full"
src={thumbSrc}
/>
</div>

<div className="mb-2 mt-5 text-sm tracking-widest">
<span className="text-primary">Ligma</span>
<span className="mx-3 opacity-30">&mdash;</span>
<time dateTime={newsItem.created_at}>
{date.toLocaleDateString("hr-HR")}
</time>
</div>

<h4 className="text-lg font-bold tracking-wide">
{newsItem.subject}
</h4>

<p className="mt-2 line-clamp-3 tracking-wide opacity-60">
{getNewsDescription(newsItem)}
</p>
</Link>
</article>
);
})}
</div>
</div>
</>
);
};

PageNewsItem.getLayout = (page) => (
<MainLayout className="max-w-[initial] !p-0 [&>header]:container">
{page}
</MainLayout>
);

export default PageNewsItem;
Loading

0 comments on commit 075a4da

Please sign in to comment.