mirror of
https://github.com/vercel/next-learn.git
synced 2026-06-11 09:51:47 +00:00
Moves prettier and lint to root of the project (#143)
This commit is contained in:
10
.eslintrc.js
Normal file
10
.eslintrc.js
Normal 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
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: 'github-actions'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
30
.github/workflows/test.yml
vendored
Normal file
30
.github/workflows/test.yml
vendored
Normal 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
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
**/.next
|
||||||
|
**/node_modules
|
||||||
|
**/package-lock.json
|
||||||
|
**/pnpm-lock.yaml
|
||||||
5
basics/.gitignore
vendored
5
basics/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
.next
|
|
||||||
node_modules
|
|
||||||
*.log
|
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
.next
|
|
||||||
node_modules
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"semi": false,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"endOfLine": "auto"
|
|
||||||
}
|
|
||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default (req, res) => {
|
export default (req, res) => {
|
||||||
res.status(200).json({ text: 'Hello' })
|
res.status(200).json({ text: 'Hello' });
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default (req, res) => {
|
export default (req, res) => {
|
||||||
res.status(200).json({ text: 'Hello' })
|
res.status(200).json({ text: 'Hello' });
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
4
basics/typescript-final/global.d.ts
vendored
4
basics/typescript-final/global.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
declare module 'remark-html' {
|
declare module 'remark-html' {
|
||||||
const html: any
|
const html: any;
|
||||||
export default html
|
export default html;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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' });
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "next/core-web-vitals"
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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" />;
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user