This commit is contained in:
Damien 2025-02-25 08:02:25 -05:00
commit 688c77d90a
20 changed files with 10261 additions and 0 deletions

4
.env Normal file
View 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
View File

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

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.idea/
.vscode/
node_modules/
build/
tmp/
temp/
.next
.env.local
!.vscode

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
.next
node_modules
*.ico

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none"
}

28
.vscode/launch.json vendored Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

41
package.json Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

45
src/app/layout.tsx Normal file
View 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
View 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>
)
}

View 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>
}

View 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>
)
}

View 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
View 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"]
}