initial import

This commit is contained in:
2023-12-27 15:45:49 +01:00
commit c0b7458bf7
20 changed files with 7918 additions and 0 deletions

3
.eslintrc.json Normal file
View File

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

38
.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
postgres

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18

20
README.md Normal file
View File

@@ -0,0 +1,20 @@
## Next.js App Router Course - Starter
This is the starter template for the Next.js App Router Course. It contains the starting code for the dashboard application.
For more information, see the [course curriculum](https://nextjs.org/learn) on the Next.js Website.
## ToDo
Zadnje sam stao na koraku 12 (nisam ga dovršio): https://nextjs.org/learn/dashboard-app/mutating-data
# Authentication
Authentication consists of the following parts:
* `next-auth` boilerplate
* `middleware.ts` = hooks-up `next-auth` into the page processing pipeline
* `auth.config.ts` = defines how user session is to be checked and redirects anonymous user to login page
* `auth.ts` = verifies user credentials during the log-in action (i.e. against a database)
* exports `auth`, `signIn`, `signOut` actions
* UI boilerplate
* `sidenav.tsx` = implements logout action - calls `signOut` from `auth.ts`
* `login-form.tsx` = implements login form
* `actions.ts` = handles login-form validation and submition - calls `signIn` from `auth.ts`

14
app/layout.tsx Normal file
View File

@@ -0,0 +1,14 @@
import '@/app/ui/global.css';
import { inter } from '@/app/ui/fonts';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
}

2
app/lib/actions.ts Normal file
View File

@@ -0,0 +1,2 @@
'use server';

25
app/lib/sql-shim.ts Normal file
View File

@@ -0,0 +1,25 @@
import { Client, QueryResult, QueryResultRow } from 'pg';
const client = new Client({
// connectionString: process.env.DATABASE_URL,
host: process.env.POSTGRES_HOST,
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB
});
client.connect();
/** an adapter function which simulates @vercel/postgres `sql` function */
export function sql<T extends QueryResultRow>(strings: TemplateStringsArray, ...values: any[]): Promise<QueryResult<T>> {
// string values need to be wrapped in single quotes
const fixedValues = values.map((value) => {
if (typeof value === 'string') {
return `'${value}'`;
}
return value;
});
const query = String.raw(strings, ...fixedValues);
return client.query<T>(query);
}

51
app/page.tsx Normal file
View File

@@ -0,0 +1,51 @@
import Link from 'next/link';
import styles from '@/app/ui/home.module.css';
import { lusitana } from './ui/fonts';
import Image from 'next/image';
import {
Cog8ToothIcon
} from '@heroicons/react/24/outline';
export default function Page() {
return (
<main className="flex min-h-screen flex-col p-6 bg-base-300">
<div className="card card-compact card-bordered max-w-[36em] bg-base-100 shadow-s my-1">
<div className="card-body">
<h2 className="card-title">2023-05 Budakova <Cog8ToothIcon className="h-[1em] w-[1em] absolute right-4 cursor-pointer" /></h2>
<div className="card-actions">
<div className="badge badge-lg badge-success cursor-pointer">GDKG</div>
<div className="badge badge-lg badge-neutral cursor-pointer">HEP Elektra</div>
<div className="badge badge-lg badge-neutral cursor-pointer">Iskon</div>
<div className="badge badge-lg badge-neutral cursor-pointer">Plinara</div>
</div>
</div>
</div>
<div className="card card-compact card-bordered max-w-[36em] bg-base-100 shadow-s my-1">
<div className="card-body">
<h2 className="card-title">2023-05 Kopernikova <Cog8ToothIcon className="h-[1em] w-[1em] absolute right-4 cursor-pointer" /></h2>
<div className="card-actions">
<div className="badge badge-lg badge-success cursor-pointer">GDKG</div>
<div className="badge badge-lg badge-neutral cursor-pointer">HEP Elektra</div>
<div className="badge badge-lg badge-neutral cursor-pointer">Iskon</div>
<div className="badge badge-lg badge-neutral cursor-pointer">Plinara</div>
</div>
</div>
</div>
<div className="card card-compact card-bordered max-w-[36em] bg-base-100 shadow-s my-1">
<div className="card-body">
<h2 className="card-title">2023-05 Šišićeva <Cog8ToothIcon className="h-[1em] w-[1em] absolute right-4 cursor-pointer" /></h2>
<div className="card-actions">
<div className="badge badge-lg badge-success cursor-pointer">GDKG</div>
<div className="badge badge-lg badge-neutral cursor-pointer">HEP Elektra</div>
<div className="badge badge-lg badge-neutral cursor-pointer">Iskon</div>
<div className="badge badge-lg badge-neutral cursor-pointer">Plinara</div>
</div>
</div>
</div>
</main>
);
}

19
app/ui/button.tsx Normal file
View File

@@ -0,0 +1,19 @@
import clsx from 'clsx';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
}
export function Button({ children, className, ...rest }: ButtonProps) {
return (
<button
{...rest}
className={clsx(
'flex h-10 items-center rounded-lg bg-blue-500 px-4 text-sm font-medium text-white transition-colors hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:bg-blue-600 aria-disabled:cursor-not-allowed aria-disabled:opacity-50',
className,
)}
>
{children}
</button>
);
}

4
app/ui/fonts.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Inter, Lusitana } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
export const lusitana = Lusitana({ weight: ["400", "700"], subsets: ['latin'] });

18
app/ui/global.css Normal file
View File

@@ -0,0 +1,18 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
input[type='number'] {
-moz-appearance: textfield;
appearance: textfield;
}
input[type='number']::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type='number']::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}

7
app/ui/home.module.css Normal file
View File

@@ -0,0 +1,7 @@
.shape {
height: 0;
width: 0;
border-bottom: 30px solid indigo;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
}

33
docker-compose.yml Normal file
View File

@@ -0,0 +1,33 @@
# this compose file runs Postgres db and exposes it's port to the host machine
version: "3.7"
volumes:
postgres:
pgadmin:
services:
db:
image: postgres:16.1-alpine
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dashboard
ports:
- 5432:5432
volumes:
# store postgres data in a local `postgres` directory
- postgres:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4:8.1
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: SuperSecret
PGADMIN_LISTEN_PORT: 80
ports:
- 8080:80
volumes:
- pgadmin:/var/lib/pgadmin
depends_on:
- db

4
next.config.js Normal file
View File

@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
module.exports = nextConfig;

7552
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

46
package.json Normal file
View File

@@ -0,0 +1,46 @@
{
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev",
"prettier": "prettier --write --ignore-unknown .",
"prettier:check": "prettier --check --ignore-unknown .",
"start": "next start",
"seed": "node -r dotenv/config ./scripts/seed.js"
},
"dependencies": {
"@heroicons/react": "^2.0.18",
"@tailwindcss/forms": "^0.5.7",
"@types/node": "20.5.7",
"autoprefixer": "10.4.15",
"bcrypt": "^5.1.1",
"clsx": "^2.0.0",
"daisyui": "^4.4.24",
"next": "^14.0.2",
"next-auth": "^5.0.0-beta.4",
"pg": "^8.11.3",
"postcss": "8.4.31",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.2.2",
"use-debounce": "^10.0.0",
"zod": "^3.22.2"
},
"devDependencies": {
"@types/bcrypt": "^5.0.1",
"@types/pg": "^8.10.9",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.14",
"@vercel/style-guide": "^5.0.1",
"dotenv": "^16.3.1",
"eslint": "^8.52.0",
"eslint-config-next": "^14.0.0",
"eslint-config-prettier": "9.0.0",
"prettier": "^3.0.3",
"prettier-plugin-tailwindcss": "0.5.4"
},
"engines": {
"node": ">=18.17.0"
}
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

6
prettier.config.js Normal file
View File

@@ -0,0 +1,6 @@
const styleguide = require('@vercel/style-guide/prettier');
module.exports = {
...styleguide,
plugins: [...styleguide.plugins, 'prettier-plugin-tailwindcss'],
};

35
tailwind.config.ts Normal file
View File

@@ -0,0 +1,35 @@
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
gridTemplateColumns: {
'13': 'repeat(13, minmax(0, 1fr))',
},
colors: {
blue: {
400: '#2589FE',
500: '#0070F3',
600: '#2F6FEB',
},
},
},
keyframes: {
shimmer: {
'100%': {
transform: 'translateX(100%)',
},
},
},
},
plugins: [
require('@tailwindcss/forms'),
require("daisyui")
],
};
export default config;

34
tsconfig.json Normal file
View File

@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"app/lib/placeholder-data.js",
"scripts/seed.js"
],
"exclude": ["node_modules"]
}