Skip to content

Commit

Permalink
feat: App router and tailwind (#2032)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ackuq authored Sep 22, 2023
1 parent c963c35 commit 05022b3
Show file tree
Hide file tree
Showing 59 changed files with 2,195 additions and 1,867 deletions.
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"plugins": ["prettier-plugin-tailwindcss"],
"trailingComma": "all"
}
20 changes: 20 additions & 0 deletions apps/web/app/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { githubProfile, linkedIn } from "../src/lib/socials";
import GithubIcon from "@components/icons/github";
import LinkedInIcon from "@components/icons/linkedIn";

export default function Footer() {
return (
<footer className="bg-primary-elevated-light dark:bg-primary-elevated-dark mt-auto flex flex-col gap-3 py-6 text-center text-white">
<span>© Axel Pettersson 2023</span>
<a href="mailto:[email protected]">[email protected]</a>
<span className="flex items-center justify-center gap-3">
<a href={linkedIn} target="_blank" rel="noopener noreferrer">
<LinkedInIcon />
</a>
<a href={githubProfile} target="_blank" rel="noopener noreferrer">
<GithubIcon />
</a>
</span>
</footer>
);
}
16 changes: 16 additions & 0 deletions apps/web/app/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer utilities {
/* For Webkit-based browsers (Chrome, Safari and Opera) */
.scrollbar-hide::-webkit-scrollbar {
display: none;
}

/* For IE, Edge and Firefox */
.scrollbar-hide {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
}
126 changes: 126 additions & 0 deletions apps/web/app/head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import Script from "next/script";
import * as gtag from "../src/lib/gtag";
import { ADSENSE_CLIENT_ID } from "../src/lib/adsense";

export default function Head() {
return (
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/static/icons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
href="/static/icons/favicon-32x32.png"
sizes="32x32"
/>
<link
rel="icon"
type="image/png"
href="/static/icons/favicon-16x16.png"
sizes="16x16"
/>
<link
rel="mask-icon"
href="/static/icons/safari-pinned-tab.svg"
color="#5bbad5"
/>
<link rel="manifest" href="/manifest.webmanifest" />

<meta property="og:image" content="/static/images/partiguiden_logo.png" />
<meta
name="theme-color"
content="#339388"
media="(prefers-color-scheme: light)"
/>
<meta
name="theme-color"
content="#1e293b"
media="(prefers-color-scheme: dark)"
/>
<style>
{`
#__next {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
min-height: 100%;
}
a:not(:hover) {
text-decoration: none;
}
`}
</style>
{process.env.NEXT_PUBLIC_VERCEL_ENV !== "production" && (
<>
{/* Disable indexing of all non-production sites */}
<meta name="robots" content="noindex" />
</>
)}
<Script
id="twttr-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.twttr = (function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0],
t = window.twttr || {};
if (d.getElementById(id)) return t;
js = d.createElement(s);
js.id = id;
js.src = "https://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
t._e = [];
t.ready = function(f) {
t._e.push(f);
};
return t;
}(document, "script", "twitter-wjs"));
`,
}}
/>
{process.env.NODE_ENV === "production" && (
<>
{/* Google Ads */}
<Script
id="ads-init"
async
strategy="afterInteractive"
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ADSENSE_CLIENT_ID}`}
crossOrigin="anonymous"
/>
{/* Global site tag (gtag.js) - Google Analytics */}
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
/>
<Script
id="gtag-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gtag.GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</>
)}
</head>
);
}
31 changes: 31 additions & 0 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import "./global.css";
import { Roboto } from "next/font/google";
import { Suspense, type PropsWithChildren } from "react";
import Head from "./head";
import Footer from "./footer";
import Header from "@components/header/header";
import { ThemeProvider } from "@components/providers/theme-provider";

const roboto = Roboto({
weight: ["300", "400", "500", "700"],
subsets: ["latin"],
display: "swap",
fallback: ["Helvetica", "Arial", "sans-serif"],
});

export default function RootLayout({ children }: PropsWithChildren) {
return (
<html lang="sv" suppressHydrationWarning>
<Head />
<body
className={`${roboto.className} bg-background-light dark:bg-background-dark text-font-light dark:text-font-dark flex min-h-screen flex-col shadow-sm`}
>
<ThemeProvider attribute="class">
<Header />
<Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
<Footer />
</ThemeProvider>
</body>
</html>
);
}
25 changes: 25 additions & 0 deletions apps/web/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { BaseCard } from "@components/card";
import PageTitle from "@components/page-title";
import { ExclamationCircleIcon } from "@heroicons/react/24/solid";

export const metadata = {
title: "404 | Sidan hittades inte | Partiguiden",
};

export default function Error404() {
return (
<main>
<PageTitle Icon={ExclamationCircleIcon}>
404 - Sidan hittades inte
</PageTitle>
<div className="container mt-4">
<BaseCard>
<p className="text-center text-lg">
Sidan du letade har kanske blivit borttagen, eller skrev du in en
felaktig URL.
</p>
</BaseCard>
</div>
</main>
);
}
69 changes: 69 additions & 0 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { BaseCard } from "@components/card";
import Typed from "../src/components/Typed";
import Link from "next/link";

export const metadata = {
title: "Partiguiden | Rösta rätt",
description:
"Vad tar Sveriges partier för ståndpunkter i sakfrågor? På Partiguiden kan du hitta och jämföra vad partierns åsikter för att hitta det parti du sympatiserar mest med.",
};

function PageTitleContainer() {
return (
<h2 className="bg-primary dark:bg-primary-elevated-dark mb-4 py-6 text-center text-xl font-light leading-10 text-white shadow-sm sm:text-3xl">
Hur vill Sveriges partier förbättra
<br />
<Typed
strings={["miljön?", "jämlikheten?", "vården?", "Sverige?"]}
typeSpeed={100}
backSpeed={50}
showCursor={false}
/>
&nbsp;
</h2>
);
}

// TODO: Fetch this from google
const featured = [
{ id: "ekonomi-och-skatter", name: "Ekonomi och Skatter" },
{ id: "lag-och-ratt", name: "Lag och rätt" },
{ id: "migration-och-integration", name: "Migration och Integration" },
{ id: "miljo-och-klimat", name: "Miljö och klimat" },
];

export default function IndexPage() {
return (
<main>
<PageTitleContainer />
<div className="container grid gap-4">
<BaseCard className="w-full">
<h3 className="pb-4 text-center text-2xl sm:text-3xl">
Vilket parti ska man rösta på?
</h3>
<p>
Vilket parti ska man rösta på? Och vad tycker partierna egentligen?
På Partiguiden kan du läsa om vad partierna tycker enligt sina
partiprogram och samt se hur de röstar i riksdagsvoteringar.
</p>
</BaseCard>
<BaseCard>
<h3 className="pb-4 text-center text-2xl sm:text-3xl">
Mest besökta ämnen de senaste 30 dagarna
</h3>
<div className="grid grid-cols-2 gap-6 text-center">
{featured.map((subject) => (
<Link
key={subject.id}
href={`/standpoints/${subject.id}`}
className="bg-background-elevated-light dark:bg-background-elevated-dark-200 rounded py-3 shadow-md transition-opacity hover:opacity-70"
>
{subject.name}
</Link>
))}
</div>
</BaseCard>
</div>
</main>
);
}
56 changes: 56 additions & 0 deletions apps/web/app/standpunkter/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { notFound } from "next/navigation";
import type { Party } from "@partiguiden/party-data/types";
import {
getStandpointsForSubject,
getSubject,
} from "@partiguiden/party-data/reader";
import { ERROR_404_TITLE } from "@lib/constants";
import PageTitle from "@components/page-title";
import PartyStandpoints from "./party-standpoints";

interface PageProps {
params: {
id: string;
};
}

export async function generateMetadata({ params: { id } }: PageProps) {
const subject = getSubject(id);

if (!subject) {
return {
title: ERROR_404_TITLE,
};
}

return {
title: `${subject.name} | Ämne | Partiguiden`,
description: `Vad tar Sveriges partier för ståndpunkter inom sakområdet ${subject.name}? Här hittar du informationen du behöver för att kunna jämföra och hitta det parti du sympatiserar med mest!`,
};
}

export default function Standpoints({ params: { id } }: PageProps) {
const subject = getSubject(id);
if (!subject) {
return notFound();
}

const standpoints = getStandpointsForSubject(subject.id);

return (
<main>
<PageTitle>{subject.name}</PageTitle>
<div className="container">
<div className="grid gap-4">
{Object.entries(standpoints).map(([party, standpoints]) => (
<PartyStandpoints
key={party}
party={party as Party}
standpoints={standpoints}
/>
))}
</div>
</div>
</main>
);
}
Loading

0 comments on commit 05022b3

Please sign in to comment.