Moves prettier and lint to root of the project (#143)

This commit is contained in:
Michael Novotny
2023-09-06 12:57:56 -05:00
committed by GitHub
parent a657dd215b
commit 1725e58866
135 changed files with 6730 additions and 765 deletions

10
.eslintrc.js Normal file
View File

@@ -0,0 +1,10 @@
module.exports = {
extends: ['next/core-web-vitals', 'prettier'],
ignorePatterns: ['**/.next/**', '**/node_modules/**'],
root: true,
settings: {
next: {
rootDir: ['basics/*/', 'dashboard/*/', 'seo/'],
},
},
};

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'

30
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: test
on: pull_request
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Cancel running workflows
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- name: Checkout repo
uses: actions/checkout@v3
- name: Setup pnpm
uses: pnpm/action-setup@v2
- name: Set node version
uses: actions/setup-node@v3
with:
cache: 'pnpm'
node-version-file: '.nvmrc'
- name: Cache node_modules
id: node-modules-cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: node-modules-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
if: steps.node-modules-cache.outputs.cache-hit != 'true'
run: pnpm install
- name: Run tests
run: pnpm test

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.log
.DS_Store
node_modules

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
**/.next
**/node_modules
**/package-lock.json
**/pnpm-lock.yaml

5
basics/.gitignore vendored
View File

@@ -1,5 +0,0 @@
.next
node_modules
*.log
yarn.lock
package-lock.json

View File

@@ -1,2 +0,0 @@
.next
node_modules

View File

@@ -1,7 +0,0 @@
{
"singleQuote": true,
"semi": false,
"trailingComma": "none",
"arrowParens": "avoid",
"endOfLine": "auto"
}

View File

@@ -1,6 +1,6 @@
import { parseISO, format } from 'date-fns' import { parseISO, format } from 'date-fns';
export default function Date({ dateString }) { export default function Date({ dateString }) {
const date = parseISO(dateString) const date = parseISO(dateString);
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time> return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>;
} }

View File

@@ -1,11 +1,11 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = '[Your Name]' const name = '[Your Name]';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) { export default function Layout({ children, home }) {
return ( return (
@@ -19,7 +19,7 @@ export default function Layout({ children, home }) {
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -65,5 +65,5 @@ export default function Layout({ children, home }) {
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,69 +1,69 @@
import fs from 'fs' import fs from 'fs';
import path from 'path' import path from 'path';
import matter from 'gray-matter' import matter from 'gray-matter';
import { remark } from 'remark' import { remark } from 'remark';
import html from 'remark-html' import html from 'remark-html';
const postsDirectory = path.join(process.cwd(), 'posts') const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() { export function getSortedPostsData() {
// Get file names under /posts // Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => { const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id // Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '') const id = fileName.replace(/\.md$/, '');
// Read markdown file as string // Read markdown file as string
const fullPath = path.join(postsDirectory, fileName) const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...matterResult.data ...matterResult.data,
} };
}) });
// Sort posts by date // Sort posts by date
return allPostsData.sort((a, b) => { return allPostsData.sort((a, b) => {
if (a.date < b.date) { if (a.date < b.date) {
return 1 return 1;
} else { } else {
return -1 return -1;
} }
}) });
} }
export function getAllPostIds() { export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => { return fileNames.map((fileName) => {
return { return {
params: { params: {
id: fileName.replace(/\.md$/, '') id: fileName.replace(/\.md$/, ''),
} },
} };
}) });
} }
export async function getPostData(id) { export async function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`) const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Use remark to convert markdown into HTML string // Use remark to convert markdown into HTML string
const processedContent = await remark() const processedContent = await remark()
.use(html) .use(html)
.process(matterResult.content) .process(matterResult.content);
const contentHtml = processedContent.toString() const contentHtml = processedContent.toString();
// Combine the data with the id and contentHtml // Combine the data with the id and contentHtml
return { return {
id, id,
contentHtml, contentHtml,
...matterResult.data ...matterResult.data,
} };
} }

View File

@@ -1,11 +1,8 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
@@ -16,5 +13,8 @@
"react-dom": "18.2.0", "react-dom": "18.2.0",
"remark": "^14.0.2", "remark": "^14.0.2",
"remark-html": "^15.0.1" "remark-html": "^15.0.1"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import '../styles/global.css' import '../styles/global.css';
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,9 +1,9 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts' import { getSortedPostsData } from '../lib/posts';
import Link from 'next/link' import Link from 'next/link';
import Date from '../components/date' import Date from '../components/date';
export default function Home({ allPostsData }) { export default function Home({ allPostsData }) {
return ( return (
@@ -33,14 +33,14 @@ export default function Home({ allPostsData }) {
</ul> </ul>
</section> </section>
</Layout> </Layout>
) );
} }
export async function getStaticProps() { export async function getStaticProps() {
const allPostsData = getSortedPostsData() const allPostsData = getSortedPostsData();
return { return {
props: { props: {
allPostsData allPostsData,
} },
} };
} }

View File

@@ -1,8 +1,8 @@
import Layout from '../../components/layout' import Layout from '../../components/layout';
import { getAllPostIds, getPostData } from '../../lib/posts' import { getAllPostIds, getPostData } from '../../lib/posts';
import Head from 'next/head' import Head from 'next/head';
import Date from '../../components/date' import Date from '../../components/date';
import utilStyles from '../../styles/utils.module.css' import utilStyles from '../../styles/utils.module.css';
export default function Post({ postData }) { export default function Post({ postData }) {
return ( return (
@@ -18,22 +18,22 @@ export default function Post({ postData }) {
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} /> <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</article> </article>
</Layout> </Layout>
) );
} }
export async function getStaticPaths() { export async function getStaticPaths() {
const paths = getAllPostIds() const paths = getAllPostIds();
return { return {
paths, paths,
fallback: false fallback: false,
} };
} }
export async function getStaticProps({ params }) { export async function getStaticProps({ params }) {
const postData = await getPostData(params.id) const postData = await getPostData(params.id);
return { return {
props: { props: {
postData postData,
} },
} };
} }

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,16 +1,16 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
"next": "latest", "next": "latest",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import Link from 'next/link'; import Link from 'next/link';
import Head from 'next/head' import Head from 'next/head';
import styles from '../styles/Home.module.css'; import styles from '../styles/Home.module.css';
export default function Home() { export default function Home() {
@@ -12,7 +12,7 @@ export default function Home() {
<main> <main>
<h1 className={styles.title}> <h1 className={styles.title}>
Read <Link href='/posts/first-post'>this page!</Link> Read <Link href="/posts/first-post">this page!</Link>
</h1> </h1>
<p className={styles.description}> <p className={styles.description}>
@@ -93,8 +93,15 @@ export default function Home() {
border-radius: 5px; border-radius: 5px;
padding: 0.75rem; padding: 0.75rem;
font-size: 1.1rem; font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, font-family:
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
} }
`}</style> `}</style>
@@ -103,8 +110,17 @@ export default function Home() {
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, font-family:
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif; sans-serif;
} }
* { * {
@@ -112,5 +128,5 @@ export default function Home() {
} }
`}</style> `}</style>
</div> </div>
) );
} }

View File

@@ -1,4 +1,4 @@
import Link from 'next/link' import Link from 'next/link';
export default function FirstPost() { export default function FirstPost() {
return ( return (
@@ -8,5 +8,5 @@ export default function FirstPost() {
<Link href="/">Back to home</Link> <Link href="/">Back to home</Link>
</h2> </h2>
</> </>
) );
} }

View File

@@ -33,7 +33,6 @@
text-align: center; text-align: center;
} }
.description { .description {
line-height: 1.5; line-height: 1.5;
font-size: 1.5rem; font-size: 1.5rem;
@@ -58,7 +57,9 @@
text-decoration: none; text-decoration: none;
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
border-radius: 10px; border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease; transition:
color 0.15s ease,
border-color 0.15s ease;
} }
.card:hover, .card:hover,

View File

@@ -2,8 +2,19 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, font-family:
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; Inter,
-apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
} }
a { a {

View File

@@ -1,6 +1,6 @@
import { parseISO, format } from 'date-fns' import { parseISO, format } from 'date-fns';
export default function Date({ dateString }) { export default function Date({ dateString }) {
const date = parseISO(dateString) const date = parseISO(dateString);
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time> return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>;
} }

View File

@@ -1,13 +1,13 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import Script from 'next/script' import Script from 'next/script';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = '[Your Name]' const name = '[Your Name]';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) { export default function Layout({ children, home }) {
return ( return (
@@ -21,7 +21,7 @@ export default function Layout({ children, home }) {
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -74,5 +74,5 @@ export default function Layout({ children, home }) {
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,69 +1,69 @@
import fs from 'fs' import fs from 'fs';
import path from 'path' import path from 'path';
import matter from 'gray-matter' import matter from 'gray-matter';
import { remark } from 'remark' import { remark } from 'remark';
import html from 'remark-html' import html from 'remark-html';
const postsDirectory = path.join(process.cwd(), 'posts') const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() { export function getSortedPostsData() {
// Get file names under /posts // Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => { const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id // Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '') const id = fileName.replace(/\.md$/, '');
// Read markdown file as string // Read markdown file as string
const fullPath = path.join(postsDirectory, fileName) const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...matterResult.data ...matterResult.data,
} };
}) });
// Sort posts by date // Sort posts by date
return allPostsData.sort((a, b) => { return allPostsData.sort((a, b) => {
if (a.date < b.date) { if (a.date < b.date) {
return 1 return 1;
} else { } else {
return -1 return -1;
} }
}) });
} }
export function getAllPostIds() { export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => { return fileNames.map((fileName) => {
return { return {
params: { params: {
id: fileName.replace(/\.md$/, '') id: fileName.replace(/\.md$/, ''),
} },
} };
}) });
} }
export async function getPostData(id) { export async function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`) const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Use remark to convert markdown into HTML string // Use remark to convert markdown into HTML string
const processedContent = await remark() const processedContent = await remark()
.use(html) .use(html)
.process(matterResult.content) .process(matterResult.content);
const contentHtml = processedContent.toString() const contentHtml = processedContent.toString();
// Combine the data with the id and contentHtml // Combine the data with the id and contentHtml
return { return {
id, id,
contentHtml, contentHtml,
...matterResult.data ...matterResult.data,
} };
} }

View File

@@ -1,11 +1,8 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
@@ -16,5 +13,8 @@
"react-dom": "18.2.0", "react-dom": "18.2.0",
"remark": "^14.0.2", "remark": "^14.0.2",
"remark-html": "^15.0.1" "remark-html": "^15.0.1"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import '../styles/global.css' import '../styles/global.css';
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,3 +1,3 @@
export default (req, res) => { export default (req, res) => {
res.status(200).json({ text: 'Hello' }) res.status(200).json({ text: 'Hello' });
} };

View File

@@ -1,9 +1,9 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts' import { getSortedPostsData } from '../lib/posts';
import Link from 'next/link' import Link from 'next/link';
import Date from '../components/date' import Date from '../components/date';
export default function Home({ allPostsData }) { export default function Home({ allPostsData }) {
return ( return (
@@ -33,14 +33,14 @@ export default function Home({ allPostsData }) {
</ul> </ul>
</section> </section>
</Layout> </Layout>
) );
} }
export async function getStaticProps() { export async function getStaticProps() {
const allPostsData = getSortedPostsData() const allPostsData = getSortedPostsData();
return { return {
props: { props: {
allPostsData allPostsData,
} },
} };
} }

View File

@@ -1,8 +1,8 @@
import Layout from '../../components/layout' import Layout from '../../components/layout';
import { getAllPostIds, getPostData } from '../../lib/posts' import { getAllPostIds, getPostData } from '../../lib/posts';
import Head from 'next/head' import Head from 'next/head';
import Date from '../../components/date' import Date from '../../components/date';
import utilStyles from '../../styles/utils.module.css' import utilStyles from '../../styles/utils.module.css';
export default function Post({ postData }) { export default function Post({ postData }) {
return ( return (
@@ -18,22 +18,22 @@ export default function Post({ postData }) {
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} /> <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</article> </article>
</Layout> </Layout>
) );
} }
export async function getStaticPaths() { export async function getStaticPaths() {
const paths = getAllPostIds() const paths = getAllPostIds();
return { return {
paths, paths,
fallback: false fallback: false,
} };
} }
export async function getStaticProps({ params }) { export async function getStaticProps({ params }) {
const postData = await getPostData(params.id) const postData = await getPostData(params.id);
return { return {
props: { props: {
postData postData,
} },
} };
} }

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,11 +1,11 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = '[Your Name]' const name = '[Your Name]';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) { export default function Layout({ children, home }) {
return ( return (
@@ -19,7 +19,7 @@ export default function Layout({ children, home }) {
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -65,5 +65,5 @@ export default function Layout({ children, home }) {
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,16 +1,16 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
"next": "latest", "next": "latest",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import '../styles/global.css' import '../styles/global.css';
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,6 +1,6 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
export default function Home() { export default function Home() {
return ( return (
@@ -16,5 +16,5 @@ export default function Home() {
</p> </p>
</section> </section>
</Layout> </Layout>
) );
} }

View File

@@ -1,6 +1,6 @@
import Head from 'next/head' import Head from 'next/head';
import Link from 'next/link' import Link from 'next/link';
import Layout from '../../components/layout' import Layout from '../../components/layout';
export default function FirstPost() { export default function FirstPost() {
return ( return (
@@ -13,5 +13,5 @@ export default function FirstPost() {
<Link href="/">Back to home</Link> <Link href="/">Back to home</Link>
</h2> </h2>
</Layout> </Layout>
) );
} }

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,6 +1,6 @@
import { parseISO, format } from 'date-fns' import { parseISO, format } from 'date-fns';
export default function Date({ dateString }) { export default function Date({ dateString }) {
const date = parseISO(dateString) const date = parseISO(dateString);
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time> return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>;
} }

View File

@@ -1,11 +1,11 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = 'Shu Uesugi' const name = 'Shu Uesugi';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) { export default function Layout({ children, home }) {
return ( return (
@@ -19,7 +19,7 @@ export default function Layout({ children, home }) {
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -65,5 +65,5 @@ export default function Layout({ children, home }) {
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,69 +1,69 @@
import fs from 'fs' import fs from 'fs';
import path from 'path' import path from 'path';
import matter from 'gray-matter' import matter from 'gray-matter';
import { remark } from 'remark' import { remark } from 'remark';
import html from 'remark-html' import html from 'remark-html';
const postsDirectory = path.join(process.cwd(), 'posts') const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() { export function getSortedPostsData() {
// Get file names under /posts // Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => { const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id // Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '') const id = fileName.replace(/\.md$/, '');
// Read markdown file as string // Read markdown file as string
const fullPath = path.join(postsDirectory, fileName) const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...matterResult.data ...matterResult.data,
} };
}) });
// Sort posts by date // Sort posts by date
return allPostsData.sort((a, b) => { return allPostsData.sort((a, b) => {
if (a.date < b.date) { if (a.date < b.date) {
return 1 return 1;
} else { } else {
return -1 return -1;
} }
}) });
} }
export function getAllPostIds() { export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => { return fileNames.map((fileName) => {
return { return {
params: { params: {
id: fileName.replace(/\.md$/, '') id: fileName.replace(/\.md$/, ''),
} },
} };
}) });
} }
export async function getPostData(id) { export async function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`) const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Use remark to convert markdown into HTML string // Use remark to convert markdown into HTML string
const processedContent = await remark() const processedContent = await remark()
.use(html) .use(html)
.process(matterResult.content) .process(matterResult.content);
const contentHtml = processedContent.toString() const contentHtml = processedContent.toString();
// Combine the data with the id and contentHtml // Combine the data with the id and contentHtml
return { return {
id, id,
contentHtml, contentHtml,
...matterResult.data ...matterResult.data,
} };
} }

View File

@@ -1,11 +1,8 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
@@ -16,5 +13,8 @@
"react-dom": "18.2.0", "react-dom": "18.2.0",
"remark": "^14.0.2", "remark": "^14.0.2",
"remark-html": "^15.0.1" "remark-html": "^15.0.1"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import '../styles/global.css' import '../styles/global.css';
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,3 +1,3 @@
export default (req, res) => { export default (req, res) => {
res.status(200).json({ text: 'Hello' }) res.status(200).json({ text: 'Hello' });
} };

View File

@@ -1,9 +1,9 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts' import { getSortedPostsData } from '../lib/posts';
import Link from 'next/link' import Link from 'next/link';
import Date from '../components/date' import Date from '../components/date';
export default function Home({ allPostsData }) { export default function Home({ allPostsData }) {
return ( return (
@@ -37,14 +37,14 @@ export default function Home({ allPostsData }) {
</ul> </ul>
</section> </section>
</Layout> </Layout>
) );
} }
export async function getStaticProps() { export async function getStaticProps() {
const allPostsData = getSortedPostsData() const allPostsData = getSortedPostsData();
return { return {
props: { props: {
allPostsData allPostsData,
} },
} };
} }

View File

@@ -1,8 +1,8 @@
import Layout from '../../components/layout' import Layout from '../../components/layout';
import { getAllPostIds, getPostData } from '../../lib/posts' import { getAllPostIds, getPostData } from '../../lib/posts';
import Head from 'next/head' import Head from 'next/head';
import Date from '../../components/date' import Date from '../../components/date';
import utilStyles from '../../styles/utils.module.css' import utilStyles from '../../styles/utils.module.css';
export default function Post({ postData }) { export default function Post({ postData }) {
return ( return (
@@ -18,22 +18,22 @@ export default function Post({ postData }) {
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} /> <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</article> </article>
</Layout> </Layout>
) );
} }
export async function getStaticPaths() { export async function getStaticPaths() {
const paths = getAllPostIds() const paths = getAllPostIds();
return { return {
paths, paths,
fallback: false fallback: false,
} };
} }
export async function getStaticProps({ params }) { export async function getStaticProps({ params }) {
const postData = await getPostData(params.id) const postData = await getPostData(params.id);
return { return {
props: { props: {
postData postData,
} },
} };
} }

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,11 +1,11 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = '[Your Name]' const name = '[Your Name]';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) { export default function Layout({ children, home }) {
return ( return (
@@ -19,7 +19,7 @@ export default function Layout({ children, home }) {
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -65,5 +65,5 @@ export default function Layout({ children, home }) {
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,35 +1,35 @@
import fs from 'fs' import fs from 'fs';
import path from 'path' import path from 'path';
import matter from 'gray-matter' import matter from 'gray-matter';
const postsDirectory = path.join(process.cwd(), 'posts') const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() { export function getSortedPostsData() {
// Get file names under /posts // Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => { const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id // Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '') const id = fileName.replace(/\.md$/, '');
// Read markdown file as string // Read markdown file as string
const fullPath = path.join(postsDirectory, fileName) const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...matterResult.data ...matterResult.data,
} };
}) });
// Sort posts by date // Sort posts by date
return allPostsData.sort((a, b) => { return allPostsData.sort((a, b) => {
if (a.date < b.date) { if (a.date < b.date) {
return 1 return 1;
} else { } else {
return -1 return -1;
} }
}) });
} }

View File

@@ -1,11 +1,8 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
@@ -13,5 +10,8 @@
"next": "latest", "next": "latest",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import '../styles/global.css' import '../styles/global.css';
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,7 +1,7 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts' import { getSortedPostsData } from '../lib/posts';
export default function Home({ allPostsData }) { export default function Home({ allPostsData }) {
return ( return (
@@ -27,14 +27,14 @@ export default function Home({ allPostsData }) {
</ul> </ul>
</section> </section>
</Layout> </Layout>
) );
} }
export async function getStaticProps() { export async function getStaticProps() {
const allPostsData = getSortedPostsData() const allPostsData = getSortedPostsData();
return { return {
props: { props: {
allPostsData allPostsData,
} },
} };
} }

View File

@@ -1,6 +1,6 @@
import Head from 'next/head' import Head from 'next/head';
import Link from 'next/link' import Link from 'next/link';
import Layout from '../../components/layout' import Layout from '../../components/layout';
export default function FirstPost() { export default function FirstPost() {
return ( return (
@@ -13,5 +13,5 @@ export default function FirstPost() {
<Link href="/">Back to home</Link> <Link href="/">Back to home</Link>
</h2> </h2>
</Layout> </Layout>
) );
} }

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,11 +1,11 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = '[Your Name]' const name = '[Your Name]';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) { export default function Layout({ children, home }) {
return ( return (
@@ -19,7 +19,7 @@ export default function Layout({ children, home }) {
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -65,5 +65,5 @@ export default function Layout({ children, home }) {
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,60 +1,60 @@
import fs from 'fs' import fs from 'fs';
import path from 'path' import path from 'path';
import matter from 'gray-matter' import matter from 'gray-matter';
const postsDirectory = path.join(process.cwd(), 'posts') const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() { export function getSortedPostsData() {
// Get file names under /posts // Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => { const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id // Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '') const id = fileName.replace(/\.md$/, '');
// Read markdown file as string // Read markdown file as string
const fullPath = path.join(postsDirectory, fileName) const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...matterResult.data ...matterResult.data,
} };
}) });
// Sort posts by date // Sort posts by date
return allPostsData.sort((a, b) => { return allPostsData.sort((a, b) => {
if (a.date < b.date) { if (a.date < b.date) {
return 1 return 1;
} else { } else {
return -1 return -1;
} }
}) });
} }
export function getAllPostIds() { export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => { return fileNames.map((fileName) => {
return { return {
params: { params: {
id: fileName.replace(/\.md$/, '') id: fileName.replace(/\.md$/, ''),
} },
} };
}) });
} }
export function getPostData(id) { export function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`) const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...matterResult.data ...matterResult.data,
} };
} }

View File

@@ -1,11 +1,8 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
@@ -13,5 +10,8 @@
"next": "latest", "next": "latest",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,5 +1,5 @@
import '../styles/global.css' import '../styles/global.css';
export default function App({ Component, pageProps }) { export default function App({ Component, pageProps }) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,7 +1,7 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts' import { getSortedPostsData } from '../lib/posts';
export default function Home({ allPostsData }) { export default function Home({ allPostsData }) {
return ( return (
@@ -27,14 +27,14 @@ export default function Home({ allPostsData }) {
</ul> </ul>
</section> </section>
</Layout> </Layout>
) );
} }
export async function getStaticProps() { export async function getStaticProps() {
const allPostsData = getSortedPostsData() const allPostsData = getSortedPostsData();
return { return {
props: { props: {
allPostsData allPostsData,
} },
} };
} }

View File

@@ -1,5 +1,5 @@
import Layout from '../../components/layout' import Layout from '../../components/layout';
import { getAllPostIds, getPostData } from '../../lib/posts' import { getAllPostIds, getPostData } from '../../lib/posts';
export default function Post({ postData }) { export default function Post({ postData }) {
return ( return (
@@ -10,22 +10,22 @@ export default function Post({ postData }) {
<br /> <br />
{postData.date} {postData.date}
</Layout> </Layout>
) );
} }
export async function getStaticPaths() { export async function getStaticPaths() {
const paths = getAllPostIds() const paths = getAllPostIds();
return { return {
paths, paths,
fallback: false fallback: false,
} };
} }
export async function getStaticProps({ params }) { export async function getStaticProps({ params }) {
const postData = getPostData(params.id) const postData = getPostData(params.id);
return { return {
props: { props: {
postData postData,
} },
} };
} }

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,16 +1,16 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
"next": "latest", "next": "latest",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -92,8 +92,15 @@ export default function Home() {
border-radius: 5px; border-radius: 5px;
padding: 0.75rem; padding: 0.75rem;
font-size: 1.1rem; font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, font-family:
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
} }
`}</style> `}</style>
@@ -102,8 +109,17 @@ export default function Home() {
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, font-family:
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif; sans-serif;
} }
* { * {
@@ -111,5 +127,5 @@ export default function Home() {
} }
`}</style> `}</style>
</div> </div>
) );
} }

View File

@@ -33,7 +33,6 @@
text-align: center; text-align: center;
} }
.description { .description {
line-height: 1.5; line-height: 1.5;
font-size: 1.5rem; font-size: 1.5rem;
@@ -58,7 +57,9 @@
text-decoration: none; text-decoration: none;
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
border-radius: 10px; border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease; transition:
color 0.15s ease,
border-color 0.15s ease;
} }
.card:hover, .card:hover,

View File

@@ -2,8 +2,19 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, font-family:
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; Inter,
-apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
} }
a { a {

View File

@@ -1,16 +1,16 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
"next": "latest", "next": "latest",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,4 +1,4 @@
import Head from 'next/head' import Head from 'next/head';
import styles from '../styles/Home.module.css'; import styles from '../styles/Home.module.css';
export default function Home() { export default function Home() {
@@ -92,8 +92,15 @@ export default function Home() {
border-radius: 5px; border-radius: 5px;
padding: 0.75rem; padding: 0.75rem;
font-size: 1.1rem; font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, font-family:
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
} }
`}</style> `}</style>
@@ -102,8 +109,17 @@ export default function Home() {
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, font-family:
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif; sans-serif;
} }
* { * {
@@ -111,5 +127,5 @@ export default function Home() {
} }
`}</style> `}</style>
</div> </div>
) );
} }

View File

@@ -33,7 +33,6 @@
text-align: center; text-align: center;
} }
.description { .description {
line-height: 1.5; line-height: 1.5;
font-size: 1.5rem; font-size: 1.5rem;
@@ -58,7 +57,9 @@
text-decoration: none; text-decoration: none;
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
border-radius: 10px; border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease; transition:
color 0.15s ease,
border-color 0.15s ease;
} }
.card:hover, .card:hover,

View File

@@ -2,8 +2,19 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, font-family:
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; Inter,
-apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
} }
a { a {

View File

@@ -1,17 +0,0 @@
{
"private": true,
"engines": {
"node": ">=18"
},
"license": "MIT",
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"devDependencies": {
"prettier": "^2.0.2",
"pretty-quick": "2.0.1",
"husky": "4.2.3"
}
}

View File

@@ -1,12 +1,12 @@
// Example: Adding className with <Link> // Example: Adding className with <Link>
import Link from 'next/link' import Link from 'next/link';
export default function LinkClassnameExample() { export default function LinkClassnameExample() {
return ( return (
<Link href="/" className="foo" target="_blank" rel="noopener noreferrer"> <Link href="/" className="foo" target="_blank" rel="noopener noreferrer">
Hello World Hello World
</Link> </Link>
) );
} }
// Take a look at https://nextjs.org/docs/api-reference/next/link // Take a look at https://nextjs.org/docs/api-reference/next/link

View File

@@ -1,6 +1,6 @@
import { parseISO, format } from 'date-fns' import { parseISO, format } from 'date-fns';
export default function Date({ dateString }: { dateString: string }) { export default function Date({ dateString }: { dateString: string }) {
const date = parseISO(dateString) const date = parseISO(dateString);
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time> return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>;
} }

View File

@@ -1,18 +1,18 @@
import Head from 'next/head' import Head from 'next/head';
import Image from 'next/image' import Image from 'next/image';
import styles from './layout.module.css' import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import Link from 'next/link' import Link from 'next/link';
const name = '[Your Name]' const name = '[Your Name]';
export const siteTitle = 'Next.js Sample Website' export const siteTitle = 'Next.js Sample Website';
export default function Layout({ export default function Layout({
children, children,
home home,
}: { }: {
children: React.ReactNode children: React.ReactNode;
home?: boolean home?: boolean;
}) { }) {
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -25,7 +25,7 @@ export default function Layout({
<meta <meta
property="og:image" property="og:image"
content={`https://og-image.vercel.app/${encodeURI( content={`https://og-image.vercel.app/${encodeURI(
siteTitle siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`} )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/> />
<meta name="og:title" content={siteTitle} /> <meta name="og:title" content={siteTitle} />
@@ -71,5 +71,5 @@ export default function Layout({
</div> </div>
)} )}
</div> </div>
) );
} }

View File

@@ -1,4 +1,4 @@
declare module 'remark-html' { declare module 'remark-html' {
const html: any const html: any;
export default html export default html;
} }

View File

@@ -1,69 +1,69 @@
import fs from 'fs' import fs from 'fs';
import path from 'path' import path from 'path';
import matter from 'gray-matter' import matter from 'gray-matter';
import { remark } from 'remark' import { remark } from 'remark';
import html from 'remark-html' import html from 'remark-html';
const postsDirectory = path.join(process.cwd(), 'posts') const postsDirectory = path.join(process.cwd(), 'posts');
export function getSortedPostsData() { export function getSortedPostsData() {
// Get file names under /posts // Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => { const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id // Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '') const id = fileName.replace(/\.md$/, '');
// Read markdown file as string // Read markdown file as string
const fullPath = path.join(postsDirectory, fileName) const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Combine the data with the id // Combine the data with the id
return { return {
id, id,
...(matterResult.data as { date: string; title: string }) ...(matterResult.data as { date: string; title: string }),
} };
}) });
// Sort posts by date // Sort posts by date
return allPostsData.sort((a, b) => { return allPostsData.sort((a, b) => {
if (a.date < b.date) { if (a.date < b.date) {
return 1 return 1;
} else { } else {
return -1 return -1;
} }
}) });
} }
export function getAllPostIds() { export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory) const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => { return fileNames.map((fileName) => {
return { return {
params: { params: {
id: fileName.replace(/\.md$/, '') id: fileName.replace(/\.md$/, ''),
} },
} };
}) });
} }
export async function getPostData(id: string) { export async function getPostData(id: string) {
const fullPath = path.join(postsDirectory, `${id}.md`) const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8') const fileContents = fs.readFileSync(fullPath, 'utf8');
// Use gray-matter to parse the post metadata section // Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents) const matterResult = matter(fileContents);
// Use remark to convert markdown into HTML string // Use remark to convert markdown into HTML string
const processedContent = await remark() const processedContent = await remark()
.use(html) .use(html)
.process(matterResult.content) .process(matterResult.content);
const contentHtml = processedContent.toString() const contentHtml = processedContent.toString();
// Combine the data with the id and contentHtml // Combine the data with the id and contentHtml
return { return {
id, id,
contentHtml, contentHtml,
...(matterResult.data as { date: string; title: string }) ...(matterResult.data as { date: string; title: string }),
} };
} }

View File

@@ -1,11 +1,8 @@
{ {
"private": true, "private": true,
"engines": {
"node": ">=18"
},
"scripts": { "scripts": {
"dev": "next dev",
"build": "next build", "build": "next build",
"dev": "next dev",
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
@@ -21,5 +18,8 @@
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/react": "^18.0.25", "@types/react": "^18.0.25",
"typescript": "^4.8.4" "typescript": "^4.8.4"
},
"engines": {
"node": ">=18"
} }
} }

View File

@@ -1,6 +1,6 @@
import '../styles/global.css' import '../styles/global.css';
import { AppProps } from 'next/app' import { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) { export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} /> return <Component {...pageProps} />;
} }

View File

@@ -1,5 +1,5 @@
import { NextApiRequest, NextApiResponse } from 'next' import { NextApiRequest, NextApiResponse } from 'next';
export default (_: NextApiRequest, res: NextApiResponse) => { export default (_: NextApiRequest, res: NextApiResponse) => {
res.status(200).json({ text: 'Hello' }) res.status(200).json({ text: 'Hello' });
} };

View File

@@ -1,19 +1,19 @@
import Head from 'next/head' import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout' import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css' import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts' import { getSortedPostsData } from '../lib/posts';
import Link from 'next/link' import Link from 'next/link';
import Date from '../components/date' import Date from '../components/date';
import { GetStaticProps } from 'next' import { GetStaticProps } from 'next';
export default function Home({ export default function Home({
allPostsData allPostsData,
}: { }: {
allPostsData: { allPostsData: {
date: string date: string;
title: string title: string;
id: string id: string;
}[] }[];
}) { }) {
return ( return (
<Layout home> <Layout home>
@@ -42,14 +42,14 @@ export default function Home({
</ul> </ul>
</section> </section>
</Layout> </Layout>
) );
} }
export const getStaticProps: GetStaticProps = async () => { export const getStaticProps: GetStaticProps = async () => {
const allPostsData = getSortedPostsData() const allPostsData = getSortedPostsData();
return { return {
props: { props: {
allPostsData allPostsData,
} },
} };
} };

View File

@@ -1,18 +1,18 @@
import Layout from '../../components/layout' import Layout from '../../components/layout';
import { getAllPostIds, getPostData } from '../../lib/posts' import { getAllPostIds, getPostData } from '../../lib/posts';
import Head from 'next/head' import Head from 'next/head';
import Date from '../../components/date' import Date from '../../components/date';
import utilStyles from '../../styles/utils.module.css' import utilStyles from '../../styles/utils.module.css';
import { GetStaticProps, GetStaticPaths } from 'next' import { GetStaticProps, GetStaticPaths } from 'next';
export default function Post({ export default function Post({
postData postData,
}: { }: {
postData: { postData: {
title: string title: string;
date: string date: string;
contentHtml: string contentHtml: string;
} };
}) { }) {
return ( return (
<Layout> <Layout>
@@ -27,22 +27,22 @@ export default function Post({
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} /> <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</article> </article>
</Layout> </Layout>
) );
} }
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
const paths = getAllPostIds() const paths = getAllPostIds();
return { return {
paths, paths,
fallback: false fallback: false,
} };
} };
export const getStaticProps: GetStaticProps = async ({ params }) => { export const getStaticProps: GetStaticProps = async ({ params }) => {
const postData = await getPostData(params?.id as string) const postData = await getPostData(params?.id as string);
return { return {
props: { props: {
postData postData,
} },
} };
} };

View File

@@ -2,8 +2,18 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, font-family:
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen,
Ubuntu,
Cantarell,
Fira Sans,
Droid Sans,
Helvetica Neue,
sans-serif;
line-height: 1.6; line-height: 1.6;
font-size: 18px; font-size: 18px;
} }

View File

@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -1,9 +1,9 @@
import CustomersTable from "@/app/ui/customers/table"; import CustomersTable from '@/app/ui/customers/table';
export default function Page() { export default function Page() {
return ( return (
<div> <div>
<CustomersTable /> <CustomersTable />
</div> </div>
) );
} }

View File

@@ -1,6 +1,6 @@
import InvoiceForm from "@/app/ui/invoices/form"; import InvoiceForm from '@/app/ui/invoices/form';
import { invoices } from "@/app/lib/dummy-data"; import { invoices } from '@/app/lib/dummy-data';
import { notFound } from "next/navigation"; import { notFound } from 'next/navigation';
export default function Page({ params }: { params: { id: string } }) { export default function Page({ params }: { params: { id: string } }) {
const id = params.id ? parseInt(params.id) : null; const id = params.id ? parseInt(params.id) : null;

View File

@@ -1,4 +1,4 @@
import InvoiceForm from "@/app/ui/invoices/form"; import InvoiceForm from '@/app/ui/invoices/form';
export default function Page() { export default function Page() {
return <InvoiceForm type="new" />; return <InvoiceForm type="new" />;

View File

@@ -1,4 +1,4 @@
import InvoicesTable from "@/app/ui/invoices/table"; import InvoicesTable from '@/app/ui/invoices/table';
export default function Page() { export default function Page() {
return ( return (

View File

@@ -1,5 +1,5 @@
import TopNav from "@/app/ui/dashboard/topnav"; import TopNav from '@/app/ui/dashboard/topnav';
import SideNav from "@/app/ui/dashboard/sidenav"; import SideNav from '@/app/ui/dashboard/sidenav';
export default function Layout({ children }: { children: React.ReactNode }) { export default function Layout({ children }: { children: React.ReactNode }) {
return ( return (

View File

@@ -1,4 +1,4 @@
import DashboardOverview from "@/app/ui/dashboard/overview"; import DashboardOverview from '@/app/ui/dashboard/overview';
export default function Page() { export default function Page() {
return ( return (

View File

@@ -1,11 +1,11 @@
import "./globals.css"; import './globals.css';
import type { Metadata } from "next"; import type { Metadata } from 'next';
import { Inter } from "next/font/google"; import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ["latin"] }); const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: 'Create Next App',
description: "Generated by create next app", description: 'Generated by create next app',
}; };
export default function RootLayout({ export default function RootLayout({

View File

@@ -1,11 +1,11 @@
"use server"; 'use server';
export async function deleteInvoice(id: number) { export async function deleteInvoice(id: number) {
// TO DO: Add delete invoice logic // TO DO: Add delete invoice logic
console.log("Delete invoice", id); console.log('Delete invoice', id);
} }
export async function addOrUpdateInvoice(formData: FormData) { export async function addOrUpdateInvoice(formData: FormData) {
// TO DO: Add create/update invoice logic // TO DO: Add create/update invoice logic
console.log("Edit Invoice"); console.log('Edit Invoice');
} }

View File

@@ -1,30 +1,30 @@
import { Invoice, Revenue } from "./definitions"; import { Invoice, Revenue } from './definitions';
export const calculateAllInvoices = ( export const calculateAllInvoices = (
invoices: Invoice[], invoices: Invoice[],
status: "pending" | "paid", status: 'pending' | 'paid',
) => { ) => {
return invoices return invoices
.filter((invoice) => !status || invoice.status === status) .filter((invoice) => !status || invoice.status === status)
.reduce((total, invoice) => total + invoice.amount / 100, 0) .reduce((total, invoice) => total + invoice.amount / 100, 0)
.toLocaleString("en-US", { .toLocaleString('en-US', {
style: "currency", style: 'currency',
currency: "USD", currency: 'USD',
}); });
}; };
export const calculateCustomerInvoices = ( export const calculateCustomerInvoices = (
invoices: Invoice[], invoices: Invoice[],
status: "pending" | "paid", status: 'pending' | 'paid',
customerId: number, customerId: number,
) => { ) => {
return invoices return invoices
.filter((invoice) => invoice.customerId === customerId) .filter((invoice) => invoice.customerId === customerId)
.filter((invoice) => !status || invoice.status === status) .filter((invoice) => !status || invoice.status === status)
.reduce((total, invoice) => total + invoice.amount / 100, 0) .reduce((total, invoice) => total + invoice.amount / 100, 0)
.toLocaleString("en-US", { .toLocaleString('en-US', {
style: "currency", style: 'currency',
currency: "USD", currency: 'USD',
}); });
}; };

View File

@@ -19,7 +19,7 @@ export type Invoice = {
id: number; id: number;
customerId: number; customerId: number;
amount: number; amount: number;
status: "pending" | "paid"; // In TypeScript, this is called a string union type. status: 'pending' | 'paid'; // In TypeScript, this is called a string union type.
// It means that the "status" property can only be one of the two strings. // It means that the "status" property can only be one of the two strings.
date: string; date: string;
}; };

View File

@@ -1,39 +1,39 @@
import { User, Customer, Invoice, Revenue } from "./definitions"; import { User, Customer, Invoice, Revenue } from './definitions';
// This file contains dummy data that you'll be replacing with real data in Chapter 7. // This file contains dummy data that you'll be replacing with real data in Chapter 7.
export const users: User[] = [ export const users: User[] = [
{ {
id: 1, id: 1,
name: "User", name: 'User',
email: "user@nextmail.com", email: 'user@nextmail.com',
password: "123456", password: '123456',
}, },
]; ];
export const customers: Customer[] = [ export const customers: Customer[] = [
{ {
id: 1, id: 1,
name: "Ada Lovelace", name: 'Ada Lovelace',
email: "ada@lovelace.com", email: 'ada@lovelace.com',
imageUrl: "/customers/ada-lovelace.png", imageUrl: '/customers/ada-lovelace.png',
}, },
{ {
id: 2, id: 2,
name: "Grace Hopper", name: 'Grace Hopper',
email: "grace@hopper.com", email: 'grace@hopper.com',
imageUrl: "/customers/grace-hopper.png", imageUrl: '/customers/grace-hopper.png',
}, },
{ {
id: 3, id: 3,
name: "Hedy Lammar", name: 'Hedy Lammar',
email: "hedy@lammar.com", email: 'hedy@lammar.com',
imageUrl: "/customers/hedy-lammar.png", imageUrl: '/customers/hedy-lammar.png',
}, },
{ {
id: 4, id: 4,
name: "Margaret Hamilton", name: 'Margaret Hamilton',
email: "margaret@hamilton.com", email: 'margaret@hamilton.com',
imageUrl: "/customers/margaret-hamilton.png", imageUrl: '/customers/margaret-hamilton.png',
}, },
]; ];
@@ -42,71 +42,71 @@ export const invoices: Invoice[] = [
id: 1, id: 1,
customerId: 1, customerId: 1,
amount: 15795, amount: 15795,
status: "pending", status: 'pending',
date: "2023-12-01", date: '2023-12-01',
}, },
{ {
id: 2, id: 2,
customerId: 2, customerId: 2,
amount: 20348, amount: 20348,
status: "pending", status: 'pending',
date: "2023-11-01", date: '2023-11-01',
}, },
{ {
id: 3, id: 3,
customerId: 3, customerId: 3,
amount: 3040, amount: 3040,
status: "paid", status: 'paid',
date: "2023-10-01", date: '2023-10-01',
}, },
{ {
id: 4, id: 4,
customerId: 4, customerId: 4,
amount: 44800, amount: 44800,
status: "paid", status: 'paid',
date: "2023-09-01", date: '2023-09-01',
}, },
{ {
id: 5, id: 5,
customerId: 1, customerId: 1,
amount: 34577, amount: 34577,
status: "pending", status: 'pending',
date: "2023-08-01", date: '2023-08-01',
}, },
{ {
id: 6, id: 6,
customerId: 2, customerId: 2,
amount: 54246, amount: 54246,
status: "pending", status: 'pending',
date: "2023-07-01", date: '2023-07-01',
}, },
{ {
id: 7, id: 7,
customerId: 3, customerId: 3,
amount: 8945, amount: 8945,
status: "pending", status: 'pending',
date: "2023-06-01", date: '2023-06-01',
}, },
{ {
id: 8, id: 8,
customerId: 4, customerId: 4,
amount: 32545, amount: 32545,
status: "paid", status: 'paid',
date: "2023-06-01", date: '2023-06-01',
}, },
]; ];
export const revenue: Revenue[] = [ export const revenue: Revenue[] = [
{ month: "Jan", revenue: 2000 }, { month: 'Jan', revenue: 2000 },
{ month: "Feb", revenue: 1800 }, { month: 'Feb', revenue: 1800 },
{ month: "Mar", revenue: 2200 }, { month: 'Mar', revenue: 2200 },
{ month: "Apr", revenue: 2500 }, { month: 'Apr', revenue: 2500 },
{ month: "May", revenue: 2300 }, { month: 'May', revenue: 2300 },
{ month: "Jun", revenue: 3200 }, { month: 'Jun', revenue: 3200 },
{ month: "Jul", revenue: 3500 }, { month: 'Jul', revenue: 3500 },
{ month: "Aug", revenue: 3700 }, { month: 'Aug', revenue: 3700 },
{ month: "Sep", revenue: 2500 }, { month: 'Sep', revenue: 2500 },
{ month: "Oct", revenue: 2800 }, { month: 'Oct', revenue: 2800 },
{ month: "Nov", revenue: 3000 }, { month: 'Nov', revenue: 3000 },
{ month: "Dec", revenue: 4800 }, { month: 'Dec', revenue: 4800 },
]; ];

View File

@@ -1,4 +1,4 @@
import LoginForm from "@/app/ui/login-form"; import LoginForm from '@/app/ui/login-form';
export default function Page() { export default function Page() {
return ( return (

View File

@@ -1,4 +1,4 @@
import Hero from "@/app/ui/hero"; import Hero from '@/app/ui/hero';
export default function Page() { export default function Page() {
return ( return (

View File

@@ -1,8 +1,8 @@
import { customers, invoices } from "@/app/lib/dummy-data"; import { customers, invoices } from '@/app/lib/dummy-data';
import { import {
countCustomerInvoices, countCustomerInvoices,
calculateCustomerInvoices, calculateCustomerInvoices,
} from "@/app/lib/calculations"; } from '@/app/lib/calculations';
export default function CustomersTable() { export default function CustomersTable() {
return ( return (
@@ -60,12 +60,12 @@ export default function CustomersTable() {
<td className="whitespace-nowrap px-3 py-4 text-sm"> <td className="whitespace-nowrap px-3 py-4 text-sm">
{calculateCustomerInvoices( {calculateCustomerInvoices(
invoices, invoices,
"pending", 'pending',
customer.id, customer.id,
)} )}
</td> </td>
<td className="whitespace-nowrap px-3 py-4 text-sm"> <td className="whitespace-nowrap px-3 py-4 text-sm">
{calculateCustomerInvoices(invoices, "paid", customer.id)} {calculateCustomerInvoices(invoices, 'paid', customer.id)}
</td> </td>
</tr> </tr>
))} ))}

View File

@@ -3,7 +3,7 @@ import {
ClockIcon, ClockIcon,
UserGroupIcon, UserGroupIcon,
InboxIcon, InboxIcon,
} from "@heroicons/react/24/outline"; } from '@heroicons/react/24/outline';
const iconMap = { const iconMap = {
collected: BanknotesIcon, collected: BanknotesIcon,
@@ -19,7 +19,7 @@ export default function Card({
}: { }: {
title: string; title: string;
value: number | string; value: number | string;
type: "invoices" | "customers" | "pending" | "collected"; type: 'invoices' | 'customers' | 'pending' | 'collected';
}) { }) {
const Icon = iconMap[type]; const Icon = iconMap[type];

View File

@@ -1,6 +1,6 @@
// InvoiceList.tsx // InvoiceList.tsx
import { Customer, Invoice } from "@/app/lib/definitions"; import { Customer, Invoice } from '@/app/lib/definitions';
import { findLatestInvoices } from "@/app/lib/calculations"; import { findLatestInvoices } from '@/app/lib/calculations';
export default function LatestInvoices({ export default function LatestInvoices({
invoices, invoices,
@@ -26,8 +26,8 @@ export default function LatestInvoices({
> >
<div className="flex items-center"> <div className="flex items-center">
<img <img
src={customer?.imageUrl || ""} src={customer?.imageUrl || ''}
alt={customer?.name || ""} alt={customer?.name || ''}
className="mr-4 h-8 w-8 rounded-full" className="mr-4 h-8 w-8 rounded-full"
/> />
<div className="min-w-0"> <div className="min-w-0">
@@ -38,10 +38,10 @@ export default function LatestInvoices({
</div> </div>
</div> </div>
<p className="truncate font-medium sm:text-lg"> <p className="truncate font-medium sm:text-lg">
+{" "} +{' '}
{(invoice.amount / 100).toLocaleString("en-US", { {(invoice.amount / 100).toLocaleString('en-US', {
style: "currency", style: 'currency',
currency: "USD", currency: 'USD',
})} })}
</p> </p>
</div> </div>

View File

@@ -1,12 +1,12 @@
import Card from "@/app/ui/dashboard/card"; import Card from '@/app/ui/dashboard/card';
import { invoices, customers, revenue } from "@/app/lib/dummy-data"; import { invoices, customers, revenue } from '@/app/lib/dummy-data';
import { calculateAllInvoices } from "@/app/lib/calculations"; import { calculateAllInvoices } from '@/app/lib/calculations';
import RevenueChart from "@/app/ui/dashboard/revenue-chart"; import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import LatestInvoices from "@/app/ui/dashboard/latest-invoices"; import LatestInvoices from '@/app/ui/dashboard/latest-invoices';
export default function DashboardOverview() { export default function DashboardOverview() {
const totalPaidInvoices = calculateAllInvoices(invoices, "paid"); const totalPaidInvoices = calculateAllInvoices(invoices, 'paid');
const totalPendingInvoices = calculateAllInvoices(invoices, "pending"); const totalPendingInvoices = calculateAllInvoices(invoices, 'pending');
const numberOfInvoices = invoices.length; const numberOfInvoices = invoices.length;
const numberOfCustomers = customers.length; const numberOfCustomers = customers.length;

View File

@@ -1,5 +1,5 @@
import { Revenue } from "@/app/lib/definitions"; import { Revenue } from '@/app/lib/definitions';
import { generateYAxis } from "@/app/lib/calculations"; import { generateYAxis } from '@/app/lib/calculations';
// This component is representational only. // This component is representational only.
// For data visualization UI, check out: // For data visualization UI, check out:
@@ -17,7 +17,7 @@ export default function RevenueChart({ revenue }: { revenue: Revenue[] }) {
return ( return (
<div className="rounded-xl border p-6 shadow-sm md:col-span-5"> <div className="rounded-xl border p-6 shadow-sm md:col-span-5">
<h2 className="font-semibold">Revenue</h2> <h2 className="font-semibold">Revenue</h2>
<div className="mt-4 grid grid-cols-12 items-end gap-2 sm:grid-cols-13 md:gap-4"> <div className="sm:grid-cols-13 mt-4 grid grid-cols-12 items-end gap-2 md:gap-4">
{/* y-axis */} {/* y-axis */}
<div <div
className="mb-6 hidden flex-col justify-between text-sm text-gray-400 sm:flex" className="mb-6 hidden flex-col justify-between text-sm text-gray-400 sm:flex"

View File

@@ -1,8 +1,8 @@
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
export default function Search() { export default function Search() {
async function submitForm(formData: FormData) { async function submitForm(formData: FormData) {
"use server"; 'use server';
// TODO: Implement search // TODO: Implement search
} }
return ( return (

Some files were not shown because too many files have changed in this diff Show More