very basic header for landing page
continuous-integration/drone/push Build is failing Details

pull/1/head
Guus van Meerveld 2 months ago
parent 7c458c179f
commit ca9737ff0d

@ -1,2 +1,2 @@
DATABASE_URL=postgresql://portfolio:portfolio@localhost:5432/portfolio?schema=public
LANDING_JSON_LOCATION=./landing.json
DATA_DIR=./data

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@ -28,6 +28,7 @@
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^5.0.1",
"timeago.js": "^4.0.2",
"zod": "^3.20.6"
},

@ -0,0 +1,71 @@
"use client";
import { Button } from "@nextui-org/button";
import { Image } from "@nextui-org/image";
import { Spacer } from "@nextui-org/react";
import { Tooltip } from "@nextui-org/tooltip";
import { Component } from "@typings/component";
import Link from "next/link";
import { useMemo } from "react";
import { FiGithub, FiMail, FiLinkedin } from "react-icons/fi";
import Owner from "@models/owner";
export const Header: Component<{ owner: Owner }> = ({ owner }) => {
const socials = useMemo(
() => [
{
link: `mailto:${owner.contact.email}`,
name: "Email address",
icon: <FiMail />
},
{
link: owner.contact.git,
name: "Github",
icon: <FiGithub />
},
{
link: owner.contact.linkedin,
name: "LinkedIn",
icon: <FiLinkedin />
}
],
[owner.contact]
);
return (
<div className="container min-h-screen">
<div>
{owner.avatar !== undefined && (
<Image
src={owner.avatar}
width={300}
alt={`A picture of ${owner.fullName}`}
/>
)}
<h1 className="text-4xl">{owner.fullName}</h1>
<Spacer y={4} />
<h2 className="text-2xl">{owner.description}</h2>
<Spacer y={4} />
{socials.map((social) => (
<Link href={social.link}>
<Tooltip content={social.name}>
<Button
className="text-2xl mr-4"
color="primary"
isIconOnly
aria-label={social.name}
>
{social.icon}
</Button>
</Tooltip>
</Link>
))}
</div>
</div>
);
};

@ -1,15 +1,23 @@
import { Header } from "./Header";
import Landing from "@models/landing";
import { dataDirLocation } from "@utils/constants";
import { readLandingJson } from "@utils/landing";
const getLanding = async (): Promise<Landing> => {
return await readLandingJson();
// Any error will get handled by the `error.tsx` file.
return await readLandingJson(dataDirLocation);
};
export default async function Page() {
const landing = await getLanding();
return <div>{/* <Button color="primary">Click me</Button> */}</div>;
return (
<>
<Header owner={landing.owner} />
</>
);
}
export const revalidate = 3600;

@ -1,17 +1,11 @@
"use client";
import { Link, Spacer } from "@nextui-org/react";
import { Link } from "@nextui-org/react";
import { ErrorPage } from "@typings/errorPage";
// Error components must be Client Components
import { useEffect } from "react";
export default function Error({
error,
reset
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
const errorPage: ErrorPage = ({ error, reset }) => {
useEffect(() => {
console.error(error);
}, [error]);
@ -27,4 +21,6 @@ export default function Error({
</div>
</div>
);
}
};
export default errorPage;

@ -3,8 +3,8 @@ import z from "zod";
export const OwnerModel = z.object({
fullName: z.string(),
name: z.string(),
description: z.string(),
avatar: z.string().optional(),
description: z.string(),
contact: z.object({
email: z.string().email(),
linkedin: z.string().url(),

@ -3,23 +3,21 @@
@tailwind utilities;
.container {
display: flex;
min-width: 100%;
flex-direction: column;
justify-content: center;
display: flex;
min-width: 100%;
flex-direction: column;
justify-content: center;
@media screen and (width >=600px) {
padding-right: 1rem;
padding-left: 1rem;
}
padding-right: 1rem;
padding-left: 1rem;
@media screen and (width >=800px) {
padding-right: 4rem;
padding-left: 4rem;
}
@media screen and (width >=800px) {
padding-right: 4rem;
padding-left: 4rem;
}
@media screen and (width >=1200px) {
padding-right: 16rem;
padding-left: 16rem;
}
}
@media screen and (width >=1200px) {
padding-right: 16rem;
padding-left: 16rem;
}
}

@ -0,0 +1,3 @@
import { FC, PropsWithChildren } from "react";
export type Component<P> = FC<PropsWithChildren<P>>;

@ -0,0 +1,6 @@
import { Component } from "./component";
export type ErrorPage = Component<{
error: Error & { digest?: string };
reset: () => void;
}>;

@ -1,2 +1,3 @@
export const landingJsonLocation =
process.env.LANDING_JSON_LOCATION ?? "/app/landing.json";
import path from "path";
export const dataDirLocation = process.env.DATA_DIR ?? "/app/data";

@ -1,20 +1,24 @@
import { readJson } from "fs-extra";
import path from "path";
import Landing, { LandingModel } from "@models/landing";
import { landingJsonLocation } from "@utils/constants";
import exists from "@utils/fileExists";
export const readLandingJson = async (): Promise<Landing> => {
const location = landingJsonLocation;
export const readLandingJson = async (
dataDirLocation: string
): Promise<Landing> => {
const landingJsonLocation = path.join(dataDirLocation, "landing.json");
const fileExists = await exists(location);
const fileExists = await exists(landingJsonLocation);
if (!fileExists) {
throw new Error(`Could not find landing json file at: ${location}`);
throw new Error(
`Could not find landing json file at: ${landingJsonLocation}`
);
}
const rawJson: unknown = await readJson(location);
const rawJson: unknown = await readJson(landingJsonLocation);
const landingResult = LandingModel.safeParse(rawJson);
@ -23,3 +27,5 @@ export const readLandingJson = async (): Promise<Landing> => {
return landingResult.data;
};
// const readPfpFile = async (location: string): Promise<> => {};

@ -23,6 +23,9 @@
"paths": {
"@styles/*": [
"styles/*"
],
"@typings/*": [
"typings/*"
],
"@components/*": [
"components/*"

@ -4671,6 +4671,11 @@ react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"
react-icons@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.0.1.tgz#1694e11bfa2a2888cab47dcc30154ce90485feee"
integrity sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"

Loading…
Cancel
Save