├── .env.example
├── .eslintrc.json
├── .gitignore
├── .nvmrc
├── README.md
├── app
├── dashboard
│ ├── (overview)
│ │ ├── loading.tsx
│ │ └── page.tsx
│ ├── customers
│ │ └── page.tsx
│ ├── invoices
│ │ ├── [id]
│ │ │ └── edit
│ │ │ │ ├── not-found.tsx
│ │ │ │ └── page.tsx
│ │ ├── create
│ │ │ └── page.tsx
│ │ ├── error.tsx
│ │ └── page.tsx
│ └── layout.tsx
├── layout.tsx
├── lib
│ ├── actions.ts
│ ├── data.ts
│ ├── definitions.ts
│ ├── pg-local.ts
│ ├── placeholder-data.js
│ ├── sql-hack.ts
│ └── utils.ts
├── login
│ └── page.tsx
├── page.tsx
└── ui
│ ├── acme-logo.tsx
│ ├── button.tsx
│ ├── customers
│ └── table.tsx
│ ├── dashboard
│ ├── cards.tsx
│ ├── latest-invoices.tsx
│ ├── nav-links.tsx
│ ├── revenue-chart.tsx
│ └── sidenav.tsx
│ ├── fonts.ts
│ ├── global.css
│ ├── home.module.css
│ ├── invoices
│ ├── breadcrumbs.tsx
│ ├── buttons.tsx
│ ├── create-form.tsx
│ ├── edit-form.tsx
│ ├── pagination.tsx
│ ├── status.tsx
│ └── table.tsx
│ ├── login-form.tsx
│ ├── search.tsx
│ └── skeletons.tsx
├── auth.config.ts
├── auth.ts
├── middleware.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── prettier.config.js
├── public
├── customers
│ ├── amy-burns.png
│ ├── balazs-orban.png
│ ├── delba-de-oliveira.png
│ ├── emil-kowalski.png
│ ├── evil-rabbit.png
│ ├── guillermo-rauch.png
│ ├── hector-simpson.png
│ ├── jared-palmer.png
│ ├── lee-robinson.png
│ ├── michael-novotny.png
│ ├── steph-dietz.png
│ └── steven-tey.png
├── favicon.ico
├── hero-desktop.png
├── hero-mobile.png
└── opengraph-image.png
├── scripts
├── pg-local.js
└── seed.js
├── tailwind.config.ts
├── tsconfig.json
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | # Copy from .env.local on the Vercel dashboard
2 | # https://nextjs.org/learn/dashboard-app/setting-up-your-database#create-a-postgres-database
3 | POSTGRES_URL=
4 | POSTGRES_PRISMA_URL=
5 | POSTGRES_URL_NON_POOLING=
6 | POSTGRES_USER=
7 | POSTGRES_HOST=
8 | POSTGRES_PASSWORD=
9 | POSTGRES_DATABASE=
10 |
11 | # `openssl rand -base64 32`
12 | AUTH_SECRET=
13 | AUTH_URL=http://localhost:3000/api/auth
14 |
15 | LOCAL_VERCEL_POSTGRES=true
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 | .env
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Next.js App Router Course - Starter
2 |
3 | 这是 Next.js 入门课程的示例代码。要了解更多信息,请参阅中文学习教程 [https://qufei1993.github.io/nextjs-learn-cn/](https://qufei1993.github.io/nextjs-learn-cn/)。
4 |
--------------------------------------------------------------------------------
/app/dashboard/(overview)/loading.tsx:
--------------------------------------------------------------------------------
1 | import DashboardSkeleton from '@/app/ui/skeletons';
2 |
3 | export default function Loading() {
4 | return
12 | 仪表板
13 |
14 |
Customers Page
; 3 | } -------------------------------------------------------------------------------- /app/dashboard/invoices/[id]/edit/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { FaceFrownIcon } from '@heroicons/react/24/outline'; 3 | 4 | export default function NotFound() { 5 | return ( 6 |Could not find the requested invoice.
10 | 14 | Go Back 15 | 16 |21 | Welcome to Acme. This is the example for the{' '} 22 | 23 | Next.js Learn Course 24 | 25 | , brought to you by Vercel. 26 |
27 | 31 | Log inAcme
11 |{customer.name}
39 |42 | {customer.email} 43 |
44 |Pending
49 |{customer.total_pending}
50 |Paid
53 |{customer.total_paid}
54 |{customer.total_invoices} invoices
58 |66 | Name 67 | | 68 |69 | Email 70 | | 71 |72 | Total Invoices 73 | | 74 |75 | Total Pending 76 | | 77 |78 | Total Paid 79 | | 80 |
---|---|---|---|---|
87 |
88 |
97 | {customer.name} 96 | |
98 | 99 | {customer.email} 100 | | 101 |102 | {customer.total_invoices} 103 | | 104 |105 | {customer.total_pending} 106 | | 107 |108 | {customer.total_paid} 109 | | 110 |
62 | {value} 63 |
64 |40 | {invoice.name} 41 |
42 |43 | {invoice.email} 44 |
45 |50 | {invoice.amount} 51 |
52 |{link.name}
44 | 45 | ); 46 | })} 47 | > 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/ui/dashboard/revenue-chart.tsx: -------------------------------------------------------------------------------- 1 | import { generateYAxis } from '@/app/lib/utils'; 2 | import { CalendarIcon } from '@heroicons/react/24/outline'; 3 | import { lusitana } from '@/app/ui/fonts'; 4 | import { fetchRevenue } from '@/app/lib/data'; 5 | 6 | // This component is representational only. 7 | // For data visualization UI, check out: 8 | // https://www.tremor.so/ 9 | // https://www.chartjs.org/ 10 | // https://airbnb.io/visx/ 11 | 12 | export default async function RevenueChart() { 13 | const revenue = await fetchRevenue(); 14 | const chartHeight = 350; 15 | // NOTE: comment in this code when you get to this point in the course 16 | 17 | const { yAxisLabels, topLabel } = generateYAxis(revenue); 18 | 19 | if (!revenue || revenue.length === 0) { 20 | returnNo data available.
; 21 | } 22 | 23 | return ( 24 |{label}
38 | ))} 39 |50 | {month.month} 51 |
52 |{invoice.name}
35 |{invoice.email}
37 |43 | {formatCurrency(invoice.amount)} 44 |
45 |{formatDateToLocal(invoice.date)}
46 |59 | Customer 60 | | 61 |62 | Email 63 | | 64 |65 | Amount 66 | | 67 |68 | Date 69 | | 70 |71 | Status 72 | | 73 |74 | Edit 75 | | 76 |
---|---|---|---|---|---|
85 |
86 |
93 | {invoice.name} 92 | |
94 | 95 | {invoice.email} 96 | | 97 |98 | {formatCurrency(invoice.amount)} 99 | | 100 |101 | {formatDateToLocal(invoice.date)} 102 | | 103 |
104 | |
106 |
107 |
108 |
111 | |
112 |
183 | Customer 184 | | 185 |186 | Email 187 | | 188 |189 | Amount 190 | | 191 |192 | Date 193 | | 194 |195 | Status 196 | | 197 |201 | Edit 202 | | 203 |
---|