init
This commit is contained in:
commit
688c77d90a
4
.env
Normal file
4
.env
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
NEXTAUTH_SECRET=changeit
|
||||||
|
RUN_ADMIN_USER_SEED=true
|
||||||
|
RUN_DB_MIGRATIONS=true
|
3
.eslintrc.json
Normal file
3
.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
.next
|
||||||
|
.env.local
|
||||||
|
!.vscode
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
*.ico
|
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
28
.vscode/launch.json
vendored
Normal file
28
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug server-side",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "npm run dev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug client-side",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug full stack",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "npm run dev",
|
||||||
|
"serverReadyAction": {
|
||||||
|
"pattern": "- Local:.+(https?://.+)",
|
||||||
|
"uriFormat": "%s",
|
||||||
|
"action": "debugWithChrome"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
36
README.md
Normal file
36
README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
20
jest.config.json
Normal file
20
jest.config.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"testPathIgnorePatterns": ["node_modules", "tests"],
|
||||||
|
"collectCoverage": true,
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.ts",
|
||||||
|
"!**/cli-tools/*",
|
||||||
|
"!**/*.config.js",
|
||||||
|
"!**/*.spec.ts",
|
||||||
|
"!**/lcov-report/*",
|
||||||
|
"!**/node_modules/**",
|
||||||
|
"!**/tests/*"
|
||||||
|
],
|
||||||
|
"coverageThreshold": {
|
||||||
|
"global": {
|
||||||
|
"branches": 50,
|
||||||
|
"functions": 40,
|
||||||
|
"lines": 70
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
next-env.d.ts
vendored
Normal file
5
next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
25
next.config.js
Normal file
25
next.config.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
swcMinify: true,
|
||||||
|
experimental: {
|
||||||
|
instrumentationHook: true
|
||||||
|
},
|
||||||
|
modularizeImports: {
|
||||||
|
'@mui/icons-material': {
|
||||||
|
transform: '@mui/icons-material/{{member}}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'source.unsplash.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/random'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
9843
package-lock.json
generated
Normal file
9843
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
package.json
Normal file
41
package.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "nextjs-mui-starter",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint && prettier .",
|
||||||
|
"lint:fix": "next lint --fix && prettier --write .",
|
||||||
|
"generate:migrations": "drizzle-kit generate:sqlite --config=./src/db/drizzle.config.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@casl/ability": "^6.5.0",
|
||||||
|
"@emotion/react": "^11.11.1",
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@fontsource/roboto": "^5.0.8",
|
||||||
|
"@mui/icons-material": "^6.0.0",
|
||||||
|
"@mui/material": "^6.0.0",
|
||||||
|
"argon2": "^0.41.0",
|
||||||
|
"better-sqlite3": "^11.0.0",
|
||||||
|
"drizzle-orm": "^0.39.0",
|
||||||
|
"next-auth": "^4.24.5",
|
||||||
|
"pino": "^9.0.0",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18",
|
||||||
|
"zod": "^3.22.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/better-sqlite3": "^7.6.8",
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"drizzle-kit": "^0.30.0",
|
||||||
|
"eslint": "^9.0.0",
|
||||||
|
"eslint-config-next": "15.1.7",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prettier": "^3.1.0",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
5
renovate.json
Normal file
5
renovate.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": ["config:recommended"],
|
||||||
|
"automerge": true
|
||||||
|
}
|
BIN
src/app/favicon.ico
Normal file
BIN
src/app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
45
src/app/layout.tsx
Normal file
45
src/app/layout.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import AppBar from '@mui/material/AppBar'
|
||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import Drawer from '@mui/material/Drawer'
|
||||||
|
import Toolbar from '@mui/material/Toolbar'
|
||||||
|
import Typography from '@mui/material/Typography'
|
||||||
|
import Divider from '@mui/material/Divider'
|
||||||
|
import List from '@mui/material/List'
|
||||||
|
import ListItem from '@mui/material/ListItem'
|
||||||
|
import ListItemButton from '@mui/material/ListItemButton'
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon'
|
||||||
|
import ListItemText from '@mui/material/ListItemText'
|
||||||
|
import DashboardIcon from '@mui/icons-material/Dashboard'
|
||||||
|
import HomeIcon from '@mui/icons-material/Home'
|
||||||
|
import UsersIcon from '@mui/icons-material/People'
|
||||||
|
import ThemeRegistry from '@/components/ThemeRegistry/ThemeRegistry'
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: 'Next.js MUI Starter Template',
|
||||||
|
description: 'Next.js App Router + Material UI v5 Starter Template'
|
||||||
|
}
|
||||||
|
|
||||||
|
const DRAWER_WIDTH = 240
|
||||||
|
|
||||||
|
const LINKS = [
|
||||||
|
{ text: 'Home', href: '/', icon: HomeIcon },
|
||||||
|
{ text: 'Users', href: '/users', icon: UsersIcon }
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<ThemeRegistry>
|
||||||
|
{children}
|
||||||
|
</ThemeRegistry>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
9
src/app/page.tsx
Normal file
9
src/app/page.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex' }}>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
99
src/components/ThemeRegistry/EmotionCache.tsx
Normal file
99
src/components/ThemeRegistry/EmotionCache.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
'use client'
|
||||||
|
import * as React from 'react'
|
||||||
|
import createCache from '@emotion/cache'
|
||||||
|
import { useServerInsertedHTML } from 'next/navigation'
|
||||||
|
import { CacheProvider as DefaultCacheProvider } from '@emotion/react'
|
||||||
|
import type {
|
||||||
|
EmotionCache,
|
||||||
|
Options as OptionsOfCreateCache
|
||||||
|
} from '@emotion/cache'
|
||||||
|
|
||||||
|
export type NextAppDirEmotionCacheProviderProps = {
|
||||||
|
/** This is the options passed to createCache() from 'import createCache from "@emotion/cache"' */
|
||||||
|
options: Omit<OptionsOfCreateCache, 'insertionPoint'>
|
||||||
|
/** By default <CacheProvider /> from 'import { CacheProvider } from "@emotion/react"' */
|
||||||
|
CacheProvider?: (props: {
|
||||||
|
value: EmotionCache
|
||||||
|
children: React.ReactNode
|
||||||
|
}) => React.JSX.Element | null
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from https://github.com/garronej/tss-react/blob/main/src/next/appDir.tsx
|
||||||
|
export default function NextAppDirEmotionCacheProvider(
|
||||||
|
props: NextAppDirEmotionCacheProviderProps
|
||||||
|
) {
|
||||||
|
const { options, CacheProvider = DefaultCacheProvider, children } = props
|
||||||
|
|
||||||
|
const [registry] = React.useState(() => {
|
||||||
|
const cache = createCache(options)
|
||||||
|
cache.compat = true
|
||||||
|
const prevInsert = cache.insert
|
||||||
|
let inserted: { name: string; isGlobal: boolean }[] = []
|
||||||
|
cache.insert = (...args) => {
|
||||||
|
const [selector, serialized] = args
|
||||||
|
if (cache.inserted[serialized.name] === undefined) {
|
||||||
|
inserted.push({
|
||||||
|
name: serialized.name,
|
||||||
|
isGlobal: !selector
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return prevInsert(...args)
|
||||||
|
}
|
||||||
|
const flush = () => {
|
||||||
|
const prevInserted = inserted
|
||||||
|
inserted = []
|
||||||
|
return prevInserted
|
||||||
|
}
|
||||||
|
return { cache, flush }
|
||||||
|
})
|
||||||
|
|
||||||
|
useServerInsertedHTML(() => {
|
||||||
|
const inserted = registry.flush()
|
||||||
|
if (inserted.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let styles = ''
|
||||||
|
let dataEmotionAttribute = registry.cache.key
|
||||||
|
|
||||||
|
const globals: {
|
||||||
|
name: string
|
||||||
|
style: string
|
||||||
|
}[] = []
|
||||||
|
|
||||||
|
inserted.forEach(({ name, isGlobal }) => {
|
||||||
|
const style = registry.cache.inserted[name]
|
||||||
|
|
||||||
|
if (typeof style !== 'boolean') {
|
||||||
|
if (isGlobal) {
|
||||||
|
globals.push({ name, style })
|
||||||
|
} else {
|
||||||
|
styles += style
|
||||||
|
dataEmotionAttribute += ` ${name}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{globals.map(({ name, style }) => (
|
||||||
|
<style
|
||||||
|
key={name}
|
||||||
|
data-emotion={`${registry.cache.key}-global ${name}`}
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
dangerouslySetInnerHTML={{ __html: style }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{styles && (
|
||||||
|
<style
|
||||||
|
data-emotion={dataEmotionAttribute}
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
dangerouslySetInnerHTML={{ __html: styles }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return <CacheProvider value={registry.cache}>{children}</CacheProvider>
|
||||||
|
}
|
22
src/components/ThemeRegistry/ThemeRegistry.tsx
Normal file
22
src/components/ThemeRegistry/ThemeRegistry.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use client'
|
||||||
|
import * as React from 'react'
|
||||||
|
import { ThemeProvider } from '@mui/material/styles'
|
||||||
|
import CssBaseline from '@mui/material/CssBaseline'
|
||||||
|
import NextAppDirEmotionCacheProvider from './EmotionCache'
|
||||||
|
import theme from './theme'
|
||||||
|
|
||||||
|
export default function ThemeRegistry({
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<NextAppDirEmotionCacheProvider options={{ key: 'mui' }}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||||
|
<CssBaseline />
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</NextAppDirEmotionCacheProvider>
|
||||||
|
)
|
||||||
|
}
|
30
src/components/ThemeRegistry/theme.ts
Normal file
30
src/components/ThemeRegistry/theme.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Roboto } from 'next/font/google'
|
||||||
|
import { createTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
|
const roboto = Roboto({
|
||||||
|
weight: ['300', '400', '500', '700'],
|
||||||
|
subsets: ['latin'],
|
||||||
|
display: 'swap'
|
||||||
|
})
|
||||||
|
|
||||||
|
const theme = createTheme({
|
||||||
|
palette: {
|
||||||
|
mode: 'dark'
|
||||||
|
},
|
||||||
|
typography: {
|
||||||
|
fontFamily: roboto.style.fontFamily
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MuiAlert: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: ({ ownerState }) => ({
|
||||||
|
...(ownerState.severity === 'info' && {
|
||||||
|
backgroundColor: '#60a5fa'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default theme
|
29
tsconfig.json
Normal file
29
tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES6",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user