Compare commits
1 Commits
main
...
dependabot
Author | SHA1 | Date |
---|---|---|
dependabot[bot] | 7ea939c0d2 | 1 year ago |
@ -1,13 +1,8 @@
|
||||
**
|
||||
|
||||
!.env
|
||||
!next.config.js
|
||||
!tailwind.config.js
|
||||
!postcss.config.js
|
||||
!tsconfig.json
|
||||
!package.json
|
||||
!yarn.lock
|
||||
!public
|
||||
!prisma
|
||||
!src
|
||||
!data
|
||||
!src
|
@ -1,74 +0,0 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: test
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: Test the newest commit
|
||||
image: node:lts-alpine
|
||||
volumes:
|
||||
- name: cache
|
||||
path: /drone/src/node_modules
|
||||
commands:
|
||||
- yarn install
|
||||
- yarn run prisma:generate
|
||||
- yarn lint
|
||||
- yarn test-build
|
||||
|
||||
volumes:
|
||||
- name: cache
|
||||
host:
|
||||
path: /tmp/drone/cache/node_modules
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-linux-amd64
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: Build Dockerfile and push to Dockerhub
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: guusvanmeerveld/portfolio
|
||||
tags:
|
||||
- latest
|
||||
- latest-amd64
|
||||
platforms: linux/amd64
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
depends_on:
|
||||
- test
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build-linux-arm64
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
steps:
|
||||
- name: Build Dockerfile and push to Dockerhub
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: guusvanmeerveld/portfolio
|
||||
tags: latest-arm64
|
||||
platforms: linux/arm64
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
depends_on:
|
||||
- test
|
@ -1,2 +1 @@
|
||||
DATABASE_URL=postgresql://portfolio:portfolio@localhost:5432/portfolio?schema=public
|
||||
DATA_DIR=./data
|
||||
NEXT_PUBLIC_GITHUB_USERNAME=Guusvanmeerveld
|
@ -1,3 +1,67 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8
|
||||
},
|
||||
"plugins": [
|
||||
"prettier",
|
||||
"css-modules"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"node_modules/*",
|
||||
".next/*",
|
||||
".out/*"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:css-modules/recommended"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:jsx-a11y/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react/prop-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error"
|
||||
],
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"warn",
|
||||
{
|
||||
"allowExpressions": true,
|
||||
"allowConciseArrowFunctionExpressionsStartingWithVoid": true,
|
||||
"allowTypedFunctionExpressions": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,37 +1,4 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
/.next
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
node_modules
|
||||
.env.local
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.next
|
||||
yarn-error.log
|
@ -1,26 +1,7 @@
|
||||
{
|
||||
"trailingComma": "none",
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"printWidth": 80,
|
||||
"arrowParens": "always",
|
||||
"importOrderSeparation": true,
|
||||
"importOrder": [
|
||||
"^..?/.*",
|
||||
"^react.*",
|
||||
"^axios.*",
|
||||
"@prisma/.*",
|
||||
"^@src/.*",
|
||||
"^@models/.*",
|
||||
"^@interfaces/.*",
|
||||
"^@styles/.*",
|
||||
"^@shared/.*",
|
||||
"^@utils/.*",
|
||||
"^@components/.*",
|
||||
"^@cache/.*",
|
||||
".*scss$",
|
||||
".*css$",
|
||||
"^@svg/.*"
|
||||
],
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"]
|
||||
}
|
||||
"trailingComma": "none",
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"printWidth": 80,
|
||||
"arrowParens": "always"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-idiomatic-order"
|
||||
],
|
||||
"rules": {
|
||||
"indentation": "tab"
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 31 KiB |
@ -1,63 +0,0 @@
|
||||
{
|
||||
"fullName": "Guus van Meerveld",
|
||||
"role": "Computer programmer",
|
||||
"description": "As a computer programmer working with precision and being methodical are of incredible importance, as a simple minor oversight can change people's lives. This is exactly the way I prefer to work. Methodically approaching a problem, taking it apart step by step and finding an optimal, yet creative solution.",
|
||||
"contact": {
|
||||
"website": "https://guusvanmeerveld.dev",
|
||||
"email": "contact@guusvanmeerveld.dev",
|
||||
"linkedIn": "https://linkedin.com/in/guus-van-meerveld-038357210",
|
||||
"git": "https://github.com/guusvanmeerveld"
|
||||
},
|
||||
"skills": [
|
||||
{
|
||||
"name": "DevOps",
|
||||
"year": "1/1/2022",
|
||||
"value": 0.8
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"year": "1/1/2020",
|
||||
"value": 0.9
|
||||
},
|
||||
{
|
||||
"name": "Web development",
|
||||
"year": "1/1/2017",
|
||||
"value": 0.7
|
||||
}
|
||||
],
|
||||
"programmingLanguages": [
|
||||
{
|
||||
"name": "Rust",
|
||||
"year": "1/1/2022",
|
||||
"value": 0.8
|
||||
},
|
||||
{ "name": "Typescript & Javascript", "value": 0.9, "year": "1/1/2017" },
|
||||
{ "name": "Java", "value": 0.6, "year": "1/8/2022" },
|
||||
{ "name": "Python", "value": 0.6, "year": "1/2/2023" },
|
||||
{ "name": "Scala", "value": 0.5, "year": "1/8/2023" }
|
||||
],
|
||||
"education": [
|
||||
{
|
||||
"title": "Bachelor Artificial Intelligence",
|
||||
"timeFrame": "2022 - Present",
|
||||
"institution": "Radboud University",
|
||||
"location": "Nijmegen, Netherlands",
|
||||
"skills": ["Mathematics", "Neuroscience", "Computer science"]
|
||||
},
|
||||
{
|
||||
"title": "Highschool VWO (NT&G)",
|
||||
"timeFrame": "2016 - 2022",
|
||||
"institution": "RSG Pantarijn MHV Wageningen",
|
||||
"location": "Wageningen, Netherlands",
|
||||
"skills": ["Biology", "Physics", "Mathematics"]
|
||||
}
|
||||
],
|
||||
"experience": [
|
||||
{
|
||||
"title": "Albert Heijn",
|
||||
"timeFrame": "2020 - 2023",
|
||||
"role": "Store employee",
|
||||
"description": ""
|
||||
}
|
||||
]
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
{
|
||||
"header": {
|
||||
"fullName": "Guus van Meerveld",
|
||||
"name": "guus",
|
||||
"description": "AI student at Radboud University. Creating software as a hobby.",
|
||||
"contact": {
|
||||
"email": "contact@guusvanmeerveld.dev",
|
||||
"git": "https://github.com/Guusvanmeerveld",
|
||||
"linkedin": "https://linkedin.com/in/guus-van-meerveld-038357210"
|
||||
}
|
||||
},
|
||||
"projects": [
|
||||
{
|
||||
"name": "Dust-Mail",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/130915639?s=200&v=4",
|
||||
"description": "Dust-Mail is a free and open source project that aims to replace all desktop and web email clients by providing a fast and simple experience.",
|
||||
"url": "https://github.com/Dust-Mail/"
|
||||
},
|
||||
{
|
||||
"name": "Argo-Client",
|
||||
"avatarUrl": "https://avatars.githubusercontent.com/u/71986232?s=200&v=4",
|
||||
"description": "Argo is a modern client for Magister 6 that is available for Android and IOS.",
|
||||
"url": "https://argo-magister.nl"
|
||||
},
|
||||
{
|
||||
"name": "MaterialTube",
|
||||
"avatarUrl": "https://raw.githubusercontent.com/Guusvanmeerveld/MaterialTube/master/src/svg/logo.svg",
|
||||
"description": "MaterialTube is a beautiful and elegant web client for Invidious servers, built using Next.js and MUI.",
|
||||
"url": "https://github.com/Guusvanmeerveld/MaterialTube"
|
||||
}
|
||||
],
|
||||
"footer": {
|
||||
"columns": [
|
||||
{
|
||||
"title": "Built with",
|
||||
"links": [
|
||||
{ "url": "https://nextjs.org/", "text": "Next.js" },
|
||||
{ "url": "https://nextui.org/", "text": "NextUI" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Social",
|
||||
"links": [
|
||||
{ "url": "https://github.com/Guusvanmeerveld", "text": "Github" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"revCount": 57,
|
||||
"type": "tarball",
|
||||
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709386671,
|
||||
"narHash": "sha256-VPqfBnIJ+cfa78pd4Y5Cr6sOWVW8GYHRVucxJGmRf8Q=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fa9a51752f1b5de583ad5213eb621be071806663",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
{
|
||||
description = "Portfolio website";
|
||||
|
||||
inputs = {
|
||||
systems.url = "github:nix-systems/default";
|
||||
flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{ systems
|
||||
, nixpkgs
|
||||
, ...
|
||||
}:
|
||||
let
|
||||
eachSystem = f:
|
||||
nixpkgs.lib.genAttrs (import systems) (
|
||||
system:
|
||||
f nixpkgs.legacyPackages.${system}
|
||||
);
|
||||
in
|
||||
{
|
||||
devShells = eachSystem
|
||||
(pkgs: {
|
||||
default = pkgs.mkShell
|
||||
{
|
||||
buildInputs = with pkgs; [
|
||||
nodejs_20
|
||||
|
||||
yarn
|
||||
|
||||
nodePackages.typescript
|
||||
nodePackages.typescript-language-server
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// @ts-check
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
* @type {import('next/dist/next-server/server/config').NextConfig}
|
||||
**/
|
||||
module.exports = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
reactStrictMode: true,
|
||||
}
|
@ -1,55 +1,42 @@
|
||||
{
|
||||
"name": "portfolio-website",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"prisma:generate": "prisma generate",
|
||||
"start:migrate": "prisma migrate deploy && yarn run start",
|
||||
"export": "next build && next export",
|
||||
"format": "prettier src --write",
|
||||
"test-build": "tsc",
|
||||
"lint": "next lint",
|
||||
"lint": "eslint src",
|
||||
"stylelint": "npx stylelint **/*.scss",
|
||||
"full-test": "yarn test-build && yarn lint && yarn stylelint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextui-org/react": "^2.2.10",
|
||||
"@prisma/client": "4.10.1",
|
||||
"@tanstack/react-query": "^4.24.9",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"axios": "^1.1.2",
|
||||
"bcrypt": "^5.1.0",
|
||||
"framer-motion": "^11.0.8",
|
||||
"fs-extra": "^11.2.0",
|
||||
"humanize-duration": "^3.31.0",
|
||||
"iron-session": "^6.3.1",
|
||||
"next": "^14.1.1",
|
||||
"configcat-node": "^8.0.0",
|
||||
"next": "^12.1.0",
|
||||
"next-seo": "^4.24.0",
|
||||
"next-themes": "^0.2.1",
|
||||
"postcss": "^8.4.35",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"sharp": "^0.33.2",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"timeago.js": "^4.0.2",
|
||||
"zod": "^3.20.6"
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"spectre.css": "^0.5.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/humanize-duration": "^3.27.4",
|
||||
"@types/node": "^15.12.1",
|
||||
"@types/react": "^18.2.64",
|
||||
"@types/react-dom": "^18.2.21",
|
||||
"@types/react": "^17.0.9",
|
||||
"@types/react-dom": "^17.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^4.26.0",
|
||||
"@typescript-eslint/parser": "^4.26.0",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-next": "13.1.6",
|
||||
"eslint-plugin-css-modules": "^2.11.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"prettier": "^2.3.1",
|
||||
"prisma": "^5.8.1",
|
||||
"sass": "^1.34.1",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-idiomatic-order": "^8.1.0",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
@ -1,25 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"name" TEXT,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Post" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"content" TEXT,
|
||||
"published" BOOLEAN NOT NULL DEFAULT false,
|
||||
"authorId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `password` to the `User` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Post" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
ADD COLUMN "tags" TEXT[];
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "password" TEXT NOT NULL;
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Made the column `name` on table `User` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "admin" BOOLEAN NOT NULL DEFAULT false,
|
||||
ALTER COLUMN "name" SET NOT NULL;
|
@ -1,12 +0,0 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Link" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"remoteAddress" TEXT NOT NULL,
|
||||
"location" TEXT NOT NULL,
|
||||
"authorId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "Link_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Link" ADD CONSTRAINT "Link_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -1,3 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
@ -1,42 +0,0 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
admin Boolean @default(false)
|
||||
password String
|
||||
name String
|
||||
posts Post[]
|
||||
links Link[]
|
||||
}
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
content String?
|
||||
published Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
tags String[]
|
||||
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId Int
|
||||
}
|
||||
|
||||
model Link {
|
||||
id Int @id @default(autoincrement())
|
||||
remoteAddress String
|
||||
location String
|
||||
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId Int
|
||||
}
|
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
@ -1,10 +0,0 @@
|
||||
(import
|
||||
(
|
||||
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||
fetchTarball {
|
||||
url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||
}
|
||||
)
|
||||
{ src = ./.; }
|
||||
).shellNix
|
@ -1,97 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@nextui-org/button";
|
||||
import { Image } from "@nextui-org/image";
|
||||
import { Link } from "@nextui-org/link";
|
||||
import { Spacer } from "@nextui-org/spacer";
|
||||
import { Tooltip } from "@nextui-org/tooltip";
|
||||
import { Component } from "@typings/component";
|
||||
import NextLink from "next/link";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { FiGithub, FiMail, FiLinkedin, FiFileText } from "react-icons/fi";
|
||||
|
||||
import HeaderProps from "@models/header";
|
||||
|
||||
interface Social {
|
||||
link: string;
|
||||
name: string;
|
||||
icon: React.ReactElement;
|
||||
isExternal?: boolean;
|
||||
}
|
||||
|
||||
export const Header: Component<{ data: HeaderProps; avatar: string }> = ({
|
||||
data,
|
||||
avatar
|
||||
}) => {
|
||||
const socials = useMemo<Social[]>(
|
||||
() => [
|
||||
{
|
||||
link: `mailto:${data.contact.email}`,
|
||||
name: "Email address",
|
||||
icon: <FiMail />
|
||||
},
|
||||
{
|
||||
link: data.contact.git,
|
||||
name: "Github",
|
||||
icon: <FiGithub />
|
||||
},
|
||||
{
|
||||
link: data.contact.linkedin,
|
||||
name: "LinkedIn",
|
||||
icon: <FiLinkedin />
|
||||
},
|
||||
{
|
||||
link: "/cv",
|
||||
name: "Cv",
|
||||
icon: <FiFileText />,
|
||||
isExternal: false
|
||||
}
|
||||
],
|
||||
[data.contact]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex items-center min-h-screen">
|
||||
<div className="flex items-center">
|
||||
<Image
|
||||
isBlurred
|
||||
src={avatar}
|
||||
width={300}
|
||||
alt={`A picture of ${data.fullName}`}
|
||||
/>
|
||||
|
||||
<Spacer x={8} />
|
||||
|
||||
<div>
|
||||
<h1 className="text-4xl">{data.fullName}</h1>
|
||||
<Spacer y={4} />
|
||||
|
||||
<h2 className="text-2xl">{data.description}</h2>
|
||||
<Spacer y={4} />
|
||||
|
||||
{socials.map((social) => (
|
||||
<Link
|
||||
isExternal={social.isExternal ?? true}
|
||||
as={NextLink}
|
||||
href={social.link}
|
||||
key={social.name.toLowerCase()}
|
||||
>
|
||||
<Tooltip showArrow content={social.name}>
|
||||
<Button
|
||||
className="text-2xl mr-4"
|
||||
color="primary"
|
||||
variant="shadow"
|
||||
isIconOnly
|
||||
aria-label={social.name}
|
||||
>
|
||||
{social.icon}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,55 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Card, CardHeader, CardBody, CardFooter } from "@nextui-org/card";
|
||||
import { Divider } from "@nextui-org/divider";
|
||||
import { Image } from "@nextui-org/image";
|
||||
import { Link } from "@nextui-org/link";
|
||||
import { Spacer } from "@nextui-org/spacer";
|
||||
import { Component } from "@typings/component";
|
||||
|
||||
import ProjectProps from "@models/project";
|
||||
|
||||
export const Projects: Component<{ data: ProjectProps[] }> = ({ data }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="container mx-auto p-4 min-h-96">
|
||||
<h1 className="text-4xl text-center mb-8">Projects</h1>
|
||||
|
||||
<div className="grid gap-4 grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 ">
|
||||
{data.map((project) => {
|
||||
const url = new URL(project.url);
|
||||
|
||||
return (
|
||||
<Card key={project.name}>
|
||||
<CardHeader className="flex gap-3">
|
||||
<Image
|
||||
alt={`${project.name} logo`}
|
||||
height={40}
|
||||
radius="sm"
|
||||
src={project.avatarUrl}
|
||||
width={40}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-md">{project.name}</p>
|
||||
<p className="text-small text-default-500">{url.host}</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<Divider />
|
||||
<CardBody>
|
||||
<p>{project.description}</p>
|
||||
</CardBody>
|
||||
<Divider />
|
||||
<CardFooter>
|
||||
<Link isExternal showAnchorIcon href={project.url}>
|
||||
Visit the project.
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<Spacer y={24} />
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
import { Footer } from "../Footer";
|
||||
import { Header } from "./Header";
|
||||
import { Projects } from "./Projects";
|
||||
|
||||
import { dataDirLocation } from "@utils/constants";
|
||||
import { readAvatarFile, readLandingJson } from "@utils/landing";
|
||||
|
||||
export default async function Page() {
|
||||
// Any error will get handled by the `error.tsx` file.
|
||||
const landing = await readLandingJson(dataDirLocation);
|
||||
const avatar = await readAvatarFile(dataDirLocation);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header data={landing.header} avatar={avatar} />
|
||||
<Projects data={landing.projects} />
|
||||
<Footer data={landing.footer} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const revalidate = 3600;
|
@ -1,45 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Divider } from "@nextui-org/divider";
|
||||
import { Link } from "@nextui-org/link";
|
||||
import { Component } from "@typings/component";
|
||||
|
||||
import { ThemeSwitcher } from "./ThemeSwitcher";
|
||||
|
||||
import FooterProps from "@models/footer";
|
||||
|
||||
export const Footer: Component<{ data: FooterProps }> = ({ data }) => {
|
||||
return (
|
||||
<div className="container mx-auto grid grid-flow-col justify-stretch my-4">
|
||||
<div className="mx-4">
|
||||
<h1 className="text-xl">
|
||||
Created with{" "}
|
||||
<span role="img" aria-label="Red Heart">
|
||||
❤️
|
||||
</span>{" "}
|
||||
by Guus van Meerveld
|
||||
</h1>
|
||||
|
||||
<Divider className="my-4" />
|
||||
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
|
||||
{data.columns.map((column) => (
|
||||
<div className="mx-4" key={column.title.toLowerCase()}>
|
||||
<h1 className="text-xl">{column.title}</h1>
|
||||
|
||||
<Divider className="mt-4" />
|
||||
|
||||
{column.links.map((link) => (
|
||||
<div className="my-4" key={link.url}>
|
||||
<Link className="text-default-500" href={link.url}>
|
||||
{link.text}
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Button,
|
||||
Link,
|
||||
Navbar,
|
||||
NavbarBrand,
|
||||
NavbarContent,
|
||||
NavbarItem
|
||||
} from "@nextui-org/react";
|
||||
import { Component } from "@typings/component";
|
||||
|
||||
export const Nav: Component = () => {
|
||||
return (
|
||||
<Navbar position="sticky">
|
||||
<NavbarBrand>
|
||||
<p className="font-bold text-inherit">ACME</p>
|
||||
</NavbarBrand>
|
||||
<NavbarContent className="hidden sm:flex gap-4" justify="center">
|
||||
<NavbarItem>
|
||||
<Link color="foreground" href="#">
|
||||
Features
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem isActive>
|
||||
<Link href="#" aria-current="page">
|
||||
Customers
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Link color="foreground" href="#">
|
||||
Integrations
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
<NavbarContent justify="end">
|
||||
<NavbarItem className="hidden lg:flex">
|
||||
<Link href="#">Login</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Button as={Link} color="primary" href="#" variant="flat">
|
||||
Sign Up
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
</Navbar>
|
||||
);
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Switch } from "@nextui-org/react";
|
||||
import { Component } from "@typings/component";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { FiMoon, FiSun } from "react-icons/fi";
|
||||
|
||||
export const ThemeSwitcher: Component = () => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
return (
|
||||
<Switch
|
||||
defaultSelected
|
||||
size="lg"
|
||||
color="primary"
|
||||
onValueChange={(value) => {
|
||||
value ? setTheme("dark") : setTheme("light");
|
||||
}}
|
||||
startContent={<FiSun />}
|
||||
endContent={<FiMoon />}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,180 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Card, CardBody } from "@nextui-org/card";
|
||||
import { Image } from "@nextui-org/image";
|
||||
import { Listbox, ListboxItem } from "@nextui-org/listbox";
|
||||
import { Progress } from "@nextui-org/progress";
|
||||
import { Spacer } from "@nextui-org/spacer";
|
||||
import { Component } from "@typings/component";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import NextImage from "next/image";
|
||||
|
||||
import { Fragment } from "react";
|
||||
|
||||
import CvProps, {
|
||||
Education as EducationProps,
|
||||
Experience as ExperienceProps,
|
||||
Skill as SkillProps
|
||||
} from "@models/cv";
|
||||
|
||||
const Skill: Component<{ skill: SkillProps }> = ({ skill }) => {
|
||||
const duration = new Date().getTime() - skill.year.getTime();
|
||||
|
||||
const durationInYears = humanizeDuration(duration, {
|
||||
units: ["y"],
|
||||
round: true
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex justify-between my-4">
|
||||
<Progress
|
||||
label={skill.name}
|
||||
aria-label={`Value for ${skill.name}`}
|
||||
valueLabel={durationInYears}
|
||||
showValueLabel
|
||||
value={skill.value * 100}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Education: Component<{ education: EducationProps }> = ({ education }) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-md">{education.title}</h1>
|
||||
<h2 className="text-small text-default-500">
|
||||
{education.timeFrame}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex flex-col text-right">
|
||||
<h1 className="text-md">{education.institution}</h1>
|
||||
<h2 className="text-small text-default-500">
|
||||
{education.location}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
<Listbox aria-label="Actions">
|
||||
{education.skills.map((skill) => (
|
||||
<ListboxItem key={skill.toLowerCase()}>{skill}</ListboxItem>
|
||||
))}
|
||||
</Listbox>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const Experience: Component<{ experience: ExperienceProps }> = ({
|
||||
experience
|
||||
}) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-md">
|
||||
{experience.role} <span className="text-default-500">at</span>{" "}
|
||||
{experience.title}
|
||||
</h1>
|
||||
<h2 className="text-small text-default-500">
|
||||
{experience.timeFrame}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Spacer y={4} />
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export const Cv: Component<{ data: CvProps }> = ({ data }) => {
|
||||
return (
|
||||
<div className="px-2 container mx-auto min-h-screen py-8">
|
||||
<div className="md:flex items-center">
|
||||
<div className="flex justify-center">
|
||||
<Image
|
||||
alt={`Professional picture of ${data.fullName}`}
|
||||
as={NextImage}
|
||||
className="mx-auto md:mx-0"
|
||||
width={200}
|
||||
height={200}
|
||||
src="/cv.jpg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Spacer x={8} />
|
||||
|
||||
<div className="text-center md:text-left flex justify-between">
|
||||
<div className="w-full">
|
||||
<h1 className="text-4xl">{data.fullName}</h1>
|
||||
<Spacer y={4} />
|
||||
<h1 className="text-2xl text-default-600">{data.role}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Spacer y={8} />
|
||||
|
||||
<h2 className="text-xl">Professional profile</h2>
|
||||
<h3 className="text-md text-default-600">{data.description}</h3>
|
||||
|
||||
<Spacer y={8} />
|
||||
|
||||
<div className="lg:flex">
|
||||
<div className="w-full lg:w-1/3">
|
||||
<h1 className="text-2xl">Skills</h1>
|
||||
|
||||
{data.skills
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.map((skill) => (
|
||||
<Skill skill={skill} key={skill.name.toLowerCase()} />
|
||||
))}
|
||||
|
||||
<Spacer y={8} />
|
||||
|
||||
<h1 className="text-2xl">Programming Languages</h1>
|
||||
|
||||
{data.programmingLanguages
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.map((skill) => (
|
||||
<Skill skill={skill} key={skill.name.toLowerCase()} />
|
||||
))}
|
||||
|
||||
<Spacer y={8} />
|
||||
</div>
|
||||
<Spacer x={8} />
|
||||
<div className="w-full lg:w-2/3">
|
||||
<h1 className="text-2xl">Experience</h1>
|
||||
|
||||
{data.experience.map((experience) => {
|
||||
return (
|
||||
<Fragment key={experience.title.toLowerCase()}>
|
||||
<Spacer y={4} />
|
||||
<Experience experience={experience} />
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
<Spacer y={8} />
|
||||
|
||||
<h1 className="text-2xl">Education</h1>
|
||||
|
||||
{data.education.map((education) => {
|
||||
return (
|
||||
<Fragment key={education.title.toLowerCase()}>
|
||||
<Spacer y={4} />
|
||||
<Education education={education} />
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
import { Cv } from "./Cv";
|
||||
|
||||
import { dataDirLocation } from "@utils/constants";
|
||||
import { readCvJson } from "@utils/cv";
|
||||
|
||||
export default async function Page() {
|
||||
const cv = await readCvJson(dataDirLocation);
|
||||
|
||||
return <Cv data={cv} />;
|
||||
}
|
||||
|
||||
export const revalidate = 3600;
|
@ -1,28 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Link } from "@nextui-org/react";
|
||||
import { ErrorPage } from "@typings/errorPage";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
const MainErrorPage: ErrorPage = ({ error, reset }) => {
|
||||
useEffect(() => {
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex items-center justify-center min-h-screen text-center">
|
||||
<div>
|
||||
<p className="text-3xl">Something went loading the page!</p>
|
||||
<p className="text-xl">{error.toString()}</p>
|
||||
<div>
|
||||
<Link href="#" onClick={() => reset()}>
|
||||
Try again
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainErrorPage;
|
@ -1,30 +0,0 @@
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { Footer } from "./Footer";
|
||||
import { Providers } from "./providers";
|
||||
|
||||
import "@styles/global.scss";
|
||||
|
||||
export default function RootLayout({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Portfolio",
|
||||
template: "%s | Portfolio"
|
||||
},
|
||||
description: "Guus van Meerveld's portfolio",
|
||||
applicationName: "Portfolio",
|
||||
manifest: "/manifest.json"
|
||||
};
|
@ -1,15 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { NextUIProvider } from "@nextui-org/react";
|
||||
import { Component } from "@typings/component";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
|
||||
export const Providers: Component = ({ children }) => {
|
||||
return (
|
||||
<NextUIProvider>
|
||||
<NextThemesProvider attribute="class" defaultTheme="dark">
|
||||
{children}
|
||||
</NextThemesProvider>
|
||||
</NextUIProvider>
|
||||
);
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { FC } from "react";
|
||||
|
||||
import { BestRepository } from "@interfaces/repository";
|
||||
|
||||
const BestRepository: FC<{ repository: BestRepository }> = ({ repository }) => {
|
||||
return (
|
||||
<div className="hero bg-primary">
|
||||
<div className="container">
|
||||
<div className="columns">
|
||||
<div className="column col-8 col-md-12 col-mx-auto">
|
||||
<h3 className="text-secondary">My most popular project:</h3>
|
||||
<h1>{repository.name}</h1>
|
||||
<h3 className="text-secondary">
|
||||
{repository.stargazers_count} Star(s)
|
||||
</h3>
|
||||
<h5>{repository.description}</h5>
|
||||
<p className="text-secondary">
|
||||
Written in {repository.language}, has{" "}
|
||||
{repository.open_issues_count} issue(s) and{" "}
|
||||
{repository.forks_count} fork(s).
|
||||
</p>
|
||||
|
||||
<Link href={repository.url}>
|
||||
<a className="btn mr-2">Github</a>
|
||||
</Link>
|
||||
{repository.homepage && (
|
||||
<Link href={repository.homepage}>
|
||||
<a className="btn">Website</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BestRepository;
|
@ -0,0 +1,46 @@
|
||||
import { FC } from "react";
|
||||
|
||||
import styles from "./intro.module.scss";
|
||||
|
||||
const Intro: FC<{ isAvailable: boolean }> = ({ isAvailable }) => {
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<div className="container">
|
||||
<div className="columns">
|
||||
<div className="column col-8 col-md-12 col-mx-auto text-center">
|
||||
<h1>Guus van Meerveld</h1>
|
||||
|
||||
<h3>
|
||||
Open source <u>web developer</u>
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={`https://github.com/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}`}
|
||||
className="btn btn-primary mr-2"
|
||||
>
|
||||
Github
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="mailto:contact@guusvanmeerveld.dev"
|
||||
className="btn btn-primary"
|
||||
>
|
||||
Contact
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p className="text-gray">
|
||||
Availibility: {isAvailable && "Available"}
|
||||
{!isAvailable && "Not available"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Intro;
|
@ -0,0 +1,16 @@
|
||||
import { FC } from "react";
|
||||
|
||||
import Footer from "@components/Footer";
|
||||
import ThemeChanger from "@components/ThemeChanger";
|
||||
|
||||
const Layout: FC = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<ThemeChanger />
|
||||
{children}
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
@ -0,0 +1,66 @@
|
||||
import { FC } from "react";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
import { RecentRepository } from "@interfaces/repository";
|
||||
|
||||
import multipleClassNames from "@utils/multipleClassNames";
|
||||
|
||||
import styles from "./repositories.module.scss";
|
||||
|
||||
const RecentRepositories: FC<{ repositories: RecentRepository[] }> = ({
|
||||
repositories
|
||||
}) => {
|
||||
return (
|
||||
<div className={multipleClassNames("container", styles.main)}>
|
||||
<div className="columns">
|
||||
<div className="column col-6 col-mx-auto text-center">
|
||||
<h3>Some of my recent projects:</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column col-9 col-mx-auto">
|
||||
<div className="columns">
|
||||
{repositories.map((repository) => {
|
||||
return (
|
||||
<div
|
||||
key={repository.name}
|
||||
className="column col-3 col-md-12 col-mx-auto mb-2"
|
||||
>
|
||||
<div
|
||||
className={multipleClassNames(
|
||||
"card",
|
||||
"text-center",
|
||||
styles.card
|
||||
)}
|
||||
>
|
||||
<div className="card-header text-primary">
|
||||
<div className="card-title h5">{repository.name}</div>
|
||||
<div className="card-subtitle text-gray">
|
||||
{repository.stargazers_count} Star(s)
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body">{repository.description}</div>
|
||||
<div className="card-footer">
|
||||
<Link href={repository.url}>
|
||||
<a className="btn btn-primary">Github</a>
|
||||
</Link>
|
||||
|
||||
{repository.homepage && (
|
||||
<Link href={repository.homepage}>
|
||||
<a className="btn btn-primary ml-2">Website</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecentRepositories;
|
@ -0,0 +1,30 @@
|
||||
import { FC, useEffect, useState } from "react";
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import styles from "./themeChanger.module.scss";
|
||||
|
||||
const ThemeChanger: FC = () => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!mounted) return <></>;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
||||
<a
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.main}
|
||||
onClick={() => setTheme(theme == "light" ? "dark" : "light")}
|
||||
>
|
||||
{theme}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeChanger;
|
@ -0,0 +1,6 @@
|
||||
$margin: 1rem;
|
||||
|
||||
.main {
|
||||
margin-top: $margin;
|
||||
margin-bottom: $margin;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
.main {
|
||||
padding-top: 10rem;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
.card {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-bottom: 3rem;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
.main {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
export interface GithubAPIRepository {
|
||||
id: number;
|
||||
node_id: string;
|
||||
name: string;
|
||||
full_name: string;
|
||||
private: boolean;
|
||||
owner: Owner;
|
||||
html_url: string;
|
||||
description: null | string;
|
||||
fork: boolean;
|
||||
url: string;
|
||||
forks_url: string;
|
||||
keys_url: string;
|
||||
collaborators_url: string;
|
||||
teams_url: string;
|
||||
hooks_url: string;
|
||||
issue_events_url: string;
|
||||
events_url: string;
|
||||
assignees_url: string;
|
||||
branches_url: string;
|
||||
tags_url: string;
|
||||
blobs_url: string;
|
||||
git_tags_url: string;
|
||||
git_refs_url: string;
|
||||
trees_url: string;
|
||||
statuses_url: string;
|
||||
languages_url: string;
|
||||
stargazers_url: string;
|
||||
contributors_url: string;
|
||||
subscribers_url: string;
|
||||
subscription_url: string;
|
||||
commits_url: string;
|
||||
git_commits_url: string;
|
||||
comments_url: string;
|
||||
issue_comment_url: string;
|
||||
contents_url: string;
|
||||
compare_url: string;
|
||||
merges_url: string;
|
||||
archive_url: string;
|
||||
downloads_url: string;
|
||||
issues_url: string;
|
||||
pulls_url: string;
|
||||
milestones_url: string;
|
||||
notifications_url: string;
|
||||
labels_url: string;
|
||||
releases_url: string;
|
||||
deployments_url: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
pushed_at: Date;
|
||||
git_url: string;
|
||||
ssh_url: string;
|
||||
clone_url: string;
|
||||
svn_url: string;
|
||||
homepage: null | string;
|
||||
size: number;
|
||||
stargazers_count: number;
|
||||
watchers_count: number;
|
||||
language: null | string;
|
||||
has_issues: boolean;
|
||||
has_projects: boolean;
|
||||
has_downloads: boolean;
|
||||
has_wiki: boolean;
|
||||
has_pages: boolean;
|
||||
forks_count: number;
|
||||
mirror_url: null;
|
||||
archived: boolean;
|
||||
disabled: boolean;
|
||||
open_issues_count: number;
|
||||
license: License | null;
|
||||
allow_forking: boolean;
|
||||
is_template: boolean;
|
||||
web_commit_signoff_required: boolean;
|
||||
topics: string[];
|
||||
visibility: string;
|
||||
forks: number;
|
||||
open_issues: number;
|
||||
watchers: number;
|
||||
default_branch: string;
|
||||
}
|
||||
|
||||
export interface License {
|
||||
key: string;
|
||||
name: string;
|
||||
spdx_id: string;
|
||||
url: string;
|
||||
node_id: string;
|
||||
}
|
||||
|
||||
export interface Owner {
|
||||
login: string;
|
||||
id: number;
|
||||
node_id: string;
|
||||
avatar_url: string;
|
||||
gravatar_id: string;
|
||||
url: string;
|
||||
html_url: string;
|
||||
followers_url: string;
|
||||
following_url: string;
|
||||
gists_url: string;
|
||||
starred_url: string;
|
||||
subscriptions_url: string;
|
||||
organizations_url: string;
|
||||
repos_url: string;
|
||||
events_url: string;
|
||||
received_events_url: string;
|
||||
type: string;
|
||||
site_admin: boolean;
|
||||
}
|
||||
|
||||
export interface RecentRepository {
|
||||
name: string;
|
||||
description: string;
|
||||
url: string;
|
||||
homepage?: string;
|
||||
stargazers_count: number;
|
||||
}
|
||||
|
||||
export interface BestRepository extends RecentRepository {
|
||||
forks_count: number;
|
||||
language: string;
|
||||
open_issues_count: number;
|
||||
pushed_at: Date;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
const SkillModel = z.object({
|
||||
name: z.string(),
|
||||
year: z.coerce.date(),
|
||||
value: z.number().min(0).max(1)
|
||||
});
|
||||
|
||||
export type Skill = z.infer<typeof SkillModel>;
|
||||
|
||||
const EducationModel = z.object({
|
||||
title: z.string(),
|
||||
timeFrame: z.string(),
|
||||
institution: z.string(),
|
||||
location: z.string(),
|
||||
skills: z.string().array()
|
||||
});
|
||||
|
||||
export type Education = z.infer<typeof EducationModel>;
|
||||
|
||||
const ExperienceModel = z.object({
|
||||
title: z.string(),
|
||||
timeFrame: z.string(),
|
||||
role: z.string(),
|
||||
description: z.string()
|
||||
});
|
||||
|
||||
export type Experience = z.infer<typeof ExperienceModel>;
|
||||
|
||||
export const CvPropsModel = z.object({
|
||||
fullName: z.string(),
|
||||
role: z.string(),
|
||||
description: z.string(),
|
||||
contact: z.object({
|
||||
website: z.string(),
|
||||
email: z.string().email(),
|
||||
linkedIn: z.string().url(),
|
||||
git: z.string().url()
|
||||
}),
|
||||
skills: SkillModel.array(),
|
||||
programmingLanguages: SkillModel.array(),
|
||||
education: EducationModel.array(),
|
||||
experience: ExperienceModel.array()
|
||||
});
|
||||
|
||||
export type CvProps = z.infer<typeof CvPropsModel>;
|
||||
|
||||
export default CvProps;
|
@ -1,14 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
const FooterColumnModel = z.object({
|
||||
title: z.string(),
|
||||
links: z.object({ url: z.string().url(), text: z.string() }).array()
|
||||
});
|
||||
|
||||
export const FooterPropsModel = z.object({
|
||||
columns: FooterColumnModel.array()
|
||||
});
|
||||
|
||||
export type FooterProps = z.infer<typeof FooterPropsModel>;
|
||||
|
||||
export default FooterProps;
|
@ -1,16 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
export const HeaderPropsModel = z.object({
|
||||
fullName: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
contact: z.object({
|
||||
email: z.string().email(),
|
||||
linkedin: z.string().url(),
|
||||
git: z.string().url()
|
||||
})
|
||||
});
|
||||
|
||||
export type HeaderProps = z.infer<typeof HeaderPropsModel>;
|
||||
|
||||
export default HeaderProps;
|
@ -1,15 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
import { FooterPropsModel } from "./footer";
|
||||
import { HeaderPropsModel } from "./header";
|
||||
import { ProjectPropsModel } from "./project";
|
||||
|
||||
export const LandingModel = z.object({
|
||||
header: HeaderPropsModel,
|
||||
projects: ProjectPropsModel.array(),
|
||||
footer: FooterPropsModel
|
||||
});
|
||||
|
||||
export type Landing = z.infer<typeof LandingModel>;
|
||||
|
||||
export default Landing;
|
@ -1,12 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
export const ProjectPropsModel = z.object({
|
||||
name: z.string(),
|
||||
avatarUrl: z.string().url(),
|
||||
description: z.string(),
|
||||
url: z.string().url()
|
||||
});
|
||||
|
||||
export type ProjectProps = z.infer<typeof ProjectPropsModel>;
|
||||
|
||||
export default ProjectProps;
|
@ -0,0 +1,9 @@
|
||||
import type { DefaultSeoProps } from "next-seo";
|
||||
|
||||
const SEO: DefaultSeoProps = {
|
||||
titleTemplate: "%s | Guus van Meerveld",
|
||||
defaultTitle: "Guus van Meerveld",
|
||||
description: "Guus van Meerveld's portfolio"
|
||||
};
|
||||
|
||||
export default SEO;
|
@ -0,0 +1,8 @@
|
||||
.main {
|
||||
height: 100vh;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import Layout from "@components/Layout";
|
||||
|
||||
import multipleClassNames from "@utils/multipleClassNames";
|
||||
|
||||
import styles from "./404.module.scss";
|
||||
|
||||
const NotFound: NextPage = () => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className={multipleClassNames("empty", styles.main)}>
|
||||
<div>
|
||||
<div className="empty-icon">
|
||||
<i className="icon icon-stop"></i>
|
||||
</div>
|
||||
<p className="empty-title h5">Page not found</p>
|
||||
<p className="empty-subtitle">
|
||||
The page has either been deleted or moved
|
||||
</p>
|
||||
<div className="empty-action">
|
||||
<button onClick={() => router.back()} className="btn btn-primary">
|
||||
Go back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
@ -0,0 +1,19 @@
|
||||
import "@styles/globals.scss";
|
||||
|
||||
import SEO from "../next-seo.config";
|
||||
|
||||
import { DefaultSeo } from "next-seo";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps): JSX.Element => (
|
||||
<>
|
||||
<DefaultSeo {...SEO} />
|
||||
<ThemeProvider>
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
@ -0,0 +1,75 @@
|
||||
import { NextSeo } from "next-seo";
|
||||
|
||||
import { GetStaticProps, InferGetStaticPropsType, NextPage } from "next";
|
||||
|
||||
import axios from "axios";
|
||||
|
||||
import Intro from "@components/Intro";
|
||||
import Layout from "@components/Layout";
|
||||
import RecentRepositories from "@components/RecentRepositories";
|
||||
import BestRepository from "@components/BestRepository";
|
||||
|
||||
import { GithubAPIRepository } from "@interfaces/repository";
|
||||
|
||||
import createConfigCatClient from "@utils/createConfigCatClient";
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
const { data } = await axios.get<GithubAPIRepository[]>(
|
||||
`https://api.github.com/users/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}/repos`
|
||||
);
|
||||
|
||||
const configCatClient = createConfigCatClient();
|
||||
|
||||
const isAvailable: boolean =
|
||||
(await configCatClient?.getValueAsync("amiavailable", true)) ?? true;
|
||||
|
||||
const bestRepository = data.sort(
|
||||
(a, b) => b.stargazers_count - a.stargazers_count
|
||||
)[0];
|
||||
|
||||
return {
|
||||
props: {
|
||||
isAvailable,
|
||||
repositories: data
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
||||
)
|
||||
.map(({ name, description, html_url, stargazers_count, homepage }) => ({
|
||||
name,
|
||||
url: html_url,
|
||||
stargazers_count,
|
||||
homepage,
|
||||
description
|
||||
}))
|
||||
.slice(0, 3),
|
||||
bestRepository: {
|
||||
name: bestRepository.name,
|
||||
description: bestRepository.description,
|
||||
url: bestRepository.html_url,
|
||||
homepage: bestRepository.homepage,
|
||||
stargazers_count: bestRepository.stargazers_count,
|
||||
forks_count: bestRepository.forks_count,
|
||||
language: bestRepository.language,
|
||||
open_issues_count: bestRepository.open_issues_count,
|
||||
pushed_at: bestRepository.pushed_at
|
||||
}
|
||||
},
|
||||
revalidate: 60 * 5
|
||||
};
|
||||
};
|
||||
|
||||
const Index: NextPage = ({
|
||||
repositories,
|
||||
isAvailable,
|
||||
bestRepository
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) => (
|
||||
<Layout>
|
||||
<NextSeo title="Home" />
|
||||
<Intro isAvailable={isAvailable} />
|
||||
<RecentRepositories repositories={repositories} />
|
||||
<BestRepository repository={bestRepository} />
|
||||
</Layout>
|
||||
);
|
||||
|
||||
export default Index;
|
@ -0,0 +1,66 @@
|
||||
[data-theme="dark"] {
|
||||
|
||||
body {
|
||||
background-color: $bg-dark;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
.empty {
|
||||
background-color: $dark-color-secondary;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: $bg-dark;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $primary-color-dark;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: $primary-color !important;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
background-color: $primary-color !important;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: $bg-dark-secondary;
|
||||
color: $primary-color;
|
||||
|
||||
border-color: $primary-color;
|
||||
|
||||
&:hover {
|
||||
border-color: $primary-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: $primary-color;
|
||||
border-color: $primary-color;
|
||||
|
||||
color: $text-primary;
|
||||
|
||||
&:hover {
|
||||
background-color: $primary-color-dark;
|
||||
border-color: $primary-color-dark;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: $primary-color-dark;
|
||||
border-color: $primary-color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
$light-color: #eee;
|
||||
$dark-color: #212121;
|
||||
$primary-color: #7e57c2;
|
||||
|
||||
$dark-color-secondary: lighten($dark-color, 2%);
|
||||
|
||||
$primary-color-dark: darken($primary-color, 10%);
|
||||
|
||||
$bg-dark: $dark-color;
|
||||
$bg-dark-secondary: $dark-color-secondary;
|
||||
|
||||
$text-primary: $light-color;
|
||||
$text-secondary: darken($light-color, 10%);
|
@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
@ -0,0 +1,6 @@
|
||||
@use "spectre.css/src/spectre.scss";
|
||||
@use "spectre.css/src/spectre-icons.scss";
|
||||
|
||||
@import "variables";
|
||||
|
||||
@import "dark";
|
@ -1,3 +0,0 @@
|
||||
import { FC, PropsWithChildren } from "react";
|
||||
|
||||
export type Component<P = unknown> = FC<PropsWithChildren<P>>;
|
@ -1,6 +0,0 @@
|
||||
import { Component } from "./component";
|
||||
|
||||
export type ErrorPage = Component<{
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}>;
|
@ -1,3 +0,0 @@
|
||||
export const dataDirLocation = process.env.DATA_DIR ?? "/app/data";
|
||||
|
||||
export const avatarFileFormat = process.env.AVATAR_FILE_FORMAT ?? "jpg";
|
@ -0,0 +1,21 @@
|
||||
import * as configcat from "configcat-node";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
const createConfigCatClient = () => {
|
||||
if (!process.env.CONFIG_CAT_SDK_KEY) return;
|
||||
|
||||
const logger = configcat.createConsoleLogger(
|
||||
process.env.NODE_ENV == "production" ? 0 : 3
|
||||
);
|
||||
|
||||
const configCatClient = configcat.createClient(
|
||||
process.env.CONFIG_CAT_SDK_KEY,
|
||||
{
|
||||
logger
|
||||
}
|
||||
);
|
||||
|
||||
return configCatClient;
|
||||
};
|
||||
|
||||
export default createConfigCatClient;
|
@ -1,15 +0,0 @@
|
||||
import path from "path";
|
||||
|
||||
import { readAndParseJsonFile } from "./json";
|
||||
|
||||
import { cache } from "react";
|
||||
|
||||
import CvProps, { CvPropsModel } from "@models/cv";
|
||||
|
||||
export const readCvJson = cache(
|
||||
async (dataDirLocation: string): Promise<CvProps> => {
|
||||
const cvJsonLocation = path.join(dataDirLocation, "cv.json");
|
||||
|
||||
return await readAndParseJsonFile(cvJsonLocation, CvPropsModel);
|
||||
}
|
||||
);
|
@ -1,9 +0,0 @@
|
||||
import { stat } from "fs-extra";
|
||||
|
||||
const fileExists = async (fileName: string): Promise<boolean> => {
|
||||
return await stat(fileName)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
};
|
||||
|
||||
export default fileExists;
|
@ -1,23 +0,0 @@
|
||||
import { readJson } from "fs-extra";
|
||||
import z from "zod";
|
||||
|
||||
import exists from "@utils/fileExists";
|
||||
|
||||
export const readAndParseJsonFile = async <T>(
|
||||
location: string,
|
||||
model: z.ZodType<T>
|
||||
): Promise<T> => {
|
||||
const fileExists = await exists(location);
|
||||
|
||||
if (!fileExists) {
|
||||
throw new Error(`Could not find json file at: ${location}`);
|
||||
}
|
||||
|
||||
const rawJson: unknown = await readJson(location);
|
||||
|
||||
const result = model.safeParse(rawJson);
|
||||
|
||||
if (!result.success) throw new Error(`Failed to parse json: ${result.error}`);
|
||||
|
||||
return result.data;
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
import { readFile, readJson } from "fs-extra";
|
||||
import path from "path";
|
||||
|
||||
import { avatarFileFormat } from "./constants";
|
||||
import { readAndParseJsonFile } from "./json";
|
||||
|
||||
import { cache } from "react";
|
||||
|
||||
import Landing, { LandingModel } from "@models/landing";
|
||||
|
||||
export const readLandingJson = cache(
|
||||
async (dataDirLocation: string): Promise<Landing> => {
|
||||
const landingJsonLocation = path.join(dataDirLocation, "landing.json");
|
||||
|
||||
return await readAndParseJsonFile(landingJsonLocation, LandingModel);
|
||||
}
|
||||
);
|
||||
|
||||
export const readAvatarFile = cache(
|
||||
async (dataDirLocation: string): Promise<string> => {
|
||||
const avatarFileLocation = path.join(
|
||||
dataDirLocation,
|
||||
`avatar.${avatarFileFormat}`
|
||||
);
|
||||
|
||||
const imageData = await readFile(avatarFileLocation);
|
||||
|
||||
const base64Image = Buffer.from(imageData).toString("base64");
|
||||
|
||||
return `data:image/${avatarFileFormat};base64,${base64Image}`;
|
||||
}
|
||||
);
|
@ -0,0 +1,5 @@
|
||||
const multipleClassNames = (...classNames: string[]): string => {
|
||||
return classNames.join(" ");
|
||||
};
|
||||
|
||||
export default multipleClassNames;
|
@ -1,16 +0,0 @@
|
||||
import { nextui } from "@nextui-org/react";
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const config = {
|
||||
content: [
|
||||
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}"
|
||||
],
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [nextui()]
|
||||
};
|
||||
|
||||
export default config;
|
@ -1,61 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": ".next/tsbuildinfo.json",
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@styles/*": [
|
||||
"styles/*"
|
||||
],
|
||||
"@typings/*": [
|
||||
"typings/*"
|
||||
],
|
||||
"@components/*": [
|
||||
"components/*"
|
||||
],
|
||||
"@interfaces/*": [
|
||||
"interfaces/*"
|
||||
],
|
||||
"@models/*": [
|
||||
"models/*"
|
||||
],
|
||||
"@utils/*": [
|
||||
"utils/*"
|
||||
],
|
||||
"@src/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": ".next/tsbuildinfo.json",
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@styles/*": ["styles/*"],
|
||||
"@components/*": ["components/*"],
|
||||
"@interfaces/*": ["interfaces/*"],
|
||||
"@utils/*": ["utils/*"],
|
||||
"@src/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
Loading…
Reference in new issue