oprava
This commit is contained in:
parent
a2d6967a96
commit
4208ca4fe3
1
z1/.gitattributes
vendored
Normal file
1
z1/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.sh text eol=lf
|
||||||
@ -1,12 +1,12 @@
|
|||||||
FROM node:20-alpine
|
FROM node:20-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY backend ./backend
|
COPY backend ./backend
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["node", "backend/app.js"]
|
CMD ["node", "backend/app.js"]
|
||||||
@ -1,226 +1,226 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const mysql = require('mysql2/promise');
|
const mysql = require('mysql2/promise');
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
const session = require('express-session');
|
const session = require('express-session');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
const FRONTEND_DIR = path.join(__dirname, '../frontend');
|
const FRONTEND_DIR = path.join(__dirname, '../frontend');
|
||||||
|
|
||||||
const pool = mysql.createPool({
|
const pool = mysql.createPool({
|
||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
port: Number(process.env.DB_PORT),
|
port: Number(process.env.DB_PORT),
|
||||||
user: process.env.DB_USER,
|
user: process.env.DB_USER,
|
||||||
password: process.env.DB_PASSWORD,
|
password: process.env.DB_PASSWORD,
|
||||||
database: process.env.DB_NAME,
|
database: process.env.DB_NAME,
|
||||||
waitForConnections: true,
|
waitForConnections: true,
|
||||||
connectionLimit: 10,
|
connectionLimit: 10,
|
||||||
queueLimit: 0
|
queueLimit: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
app.use(session({
|
app.use(session({
|
||||||
secret: process.env.SESSION_SECRET,
|
secret: process.env.SESSION_SECRET,
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false
|
saveUninitialized: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
if (req.session.user) {
|
if (req.session.user) {
|
||||||
return res.redirect('/index.html');
|
return res.redirect('/index.html');
|
||||||
}
|
}
|
||||||
res.sendFile(path.join(FRONTEND_DIR, 'login.html'));
|
res.sendFile(path.join(FRONTEND_DIR, 'login.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/register', (req, res) => {
|
app.get('/register', (req, res) => {
|
||||||
res.sendFile(path.join(FRONTEND_DIR, 'register.html'));
|
res.sendFile(path.join(FRONTEND_DIR, 'register.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/index.html', (req, res) => {
|
app.get('/index.html', (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
res.sendFile(path.join(FRONTEND_DIR, 'index.html'));
|
res.sendFile(path.join(FRONTEND_DIR, 'index.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/register', async (req, res) => {
|
app.post('/register', async (req, res) => {
|
||||||
const { username, password, password2 } = req.body;
|
const { username, password, password2 } = req.body;
|
||||||
|
|
||||||
if (!username || !password || !password2) {
|
if (!username || !password || !password2) {
|
||||||
return res.redirect('/register.html?error=' + encodeURIComponent('Vyplň všetky polia'));
|
return res.redirect('/register.html?error=' + encodeURIComponent('Vyplň všetky polia'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== password2) {
|
if (password !== password2) {
|
||||||
return res.redirect('/register.html?error=' + encodeURIComponent('Heslá sa nezhodujú'));
|
return res.redirect('/register.html?error=' + encodeURIComponent('Heslá sa nezhodujú'));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||||||
|
|
||||||
await pool.execute(
|
await pool.execute(
|
||||||
'INSERT INTO users (username, password) VALUES (?, ?)',
|
'INSERT INTO users (username, password) VALUES (?, ?)',
|
||||||
[username, hashedPassword]
|
[username, hashedPassword]
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.redirect('/register.html?error=' + encodeURIComponent('Používateľ už existuje'));
|
return res.redirect('/register.html?error=' + encodeURIComponent('Používateľ už existuje'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/login', async (req, res) => {
|
app.post('/login', async (req, res) => {
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
return res.redirect('/?error=Vyplň%20všetky%20polia');
|
return res.redirect('/?error=Vyplň%20všetky%20polia');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.execute(
|
const [rows] = await pool.execute(
|
||||||
'SELECT * FROM users WHERE username = ?',
|
'SELECT * FROM users WHERE username = ?',
|
||||||
[username]
|
[username]
|
||||||
);
|
);
|
||||||
|
|
||||||
const row = rows[0];
|
const row = rows[0];
|
||||||
|
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return res.redirect('/?error=Zlé%20meno%20alebo%20heslo');
|
return res.redirect('/?error=Zlé%20meno%20alebo%20heslo');
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await bcrypt.compare(password, row.password);
|
const result = await bcrypt.compare(password, row.password);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return res.redirect('/?error=Zlé%20meno%20alebo%20heslo');
|
return res.redirect('/?error=Zlé%20meno%20alebo%20heslo');
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
username: row.username
|
username: row.username
|
||||||
};
|
};
|
||||||
|
|
||||||
return res.redirect('/index.html');
|
return res.redirect('/index.html');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.redirect('/?error=Chyba%20databázy');
|
return res.redirect('/?error=Chyba%20databázy');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/logout', (req, res) => {
|
app.post('/logout', (req, res) => {
|
||||||
req.session.destroy(() => {
|
req.session.destroy(() => {
|
||||||
res.redirect('/');
|
res.redirect('/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/me', (req, res) => {
|
app.get('/api/me', (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(req.session.user);
|
res.json(req.session.user);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/tasks', async (req, res) => {
|
app.get('/api/tasks', async (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.execute(
|
const [rows] = await pool.execute(
|
||||||
'SELECT * FROM tasks WHERE user_id = ? ORDER BY deadline ASC',
|
'SELECT * FROM tasks WHERE user_id = ? ORDER BY deadline ASC',
|
||||||
[req.session.user.id]
|
[req.session.user.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.status(500).json({ error: 'Chyba pri načítaní úloh' });
|
return res.status(500).json({ error: 'Chyba pri načítaní úloh' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/tasks', async (req, res) => {
|
app.post('/api/tasks', async (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, description, deadline } = req.body;
|
const { title, description, deadline } = req.body;
|
||||||
|
|
||||||
if (!title || !description || !deadline) {
|
if (!title || !description || !deadline) {
|
||||||
return res.status(400).json({ error: 'Vyplň všetky polia' });
|
return res.status(400).json({ error: 'Vyplň všetky polia' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [result] = await pool.execute(
|
const [result] = await pool.execute(
|
||||||
'INSERT INTO tasks (user_id, title, description, deadline) VALUES (?, ?, ?, ?)',
|
'INSERT INTO tasks (user_id, title, description, deadline) VALUES (?, ?, ?, ?)',
|
||||||
[req.session.user.id, title, description, deadline]
|
[req.session.user.id, title, description, deadline]
|
||||||
);
|
);
|
||||||
|
|
||||||
const [rows] = await pool.execute(
|
const [rows] = await pool.execute(
|
||||||
'SELECT * FROM tasks WHERE id = ?',
|
'SELECT * FROM tasks WHERE id = ?',
|
||||||
[result.insertId]
|
[result.insertId]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json(rows[0]);
|
res.json(rows[0]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.status(500).json({ error: 'Chyba pri ukladaní úlohy' });
|
return res.status(500).json({ error: 'Chyba pri ukladaní úlohy' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/tasks/:id', async (req, res) => {
|
app.delete('/api/tasks/:id', async (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await pool.execute(
|
await pool.execute(
|
||||||
'DELETE FROM tasks WHERE id = ? AND user_id = ?',
|
'DELETE FROM tasks WHERE id = ? AND user_id = ?',
|
||||||
[req.params.id, req.session.user.id]
|
[req.params.id, req.session.user.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.status(500).json({ error: 'Chyba pri mazaní úlohy' });
|
return res.status(500).json({ error: 'Chyba pri mazaní úlohy' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.patch('/api/tasks/:id/toggle', async (req, res) => {
|
app.patch('/api/tasks/:id/toggle', async (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
return res.status(401).json({ error: 'Neprihlásený používateľ' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status } = req.body;
|
const { status } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await pool.execute(
|
await pool.execute(
|
||||||
'UPDATE tasks SET status = ? WHERE id = ? AND user_id = ?',
|
'UPDATE tasks SET status = ? WHERE id = ? AND user_id = ?',
|
||||||
[status ? 1 : 0, req.params.id, req.session.user.id]
|
[status ? 1 : 0, req.params.id, req.session.user.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return res.status(500).json({ error: 'Chyba pri zmene stavu úlohy' });
|
return res.status(500).json({ error: 'Chyba pri zmene stavu úlohy' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(express.static(FRONTEND_DIR));
|
app.use(express.static(FRONTEND_DIR));
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
app.listen(PORT, async () => {
|
app.listen(PORT, async () => {
|
||||||
try {
|
try {
|
||||||
const connection = await pool.getConnection();
|
const connection = await pool.getConnection();
|
||||||
console.log('Pripojenie na MySQL úspešné.');
|
console.log('Pripojenie na MySQL úspešné.');
|
||||||
connection.release();
|
connection.release();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Nepodarilo sa pripojiť na MySQL:', err.message);
|
console.error('Nepodarilo sa pripojiť na MySQL:', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Server beží na http://localhost:${PORT}`);
|
console.log(`Server beží na http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
@ -1,15 +1,15 @@
|
|||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
username VARCHAR(100) NOT NULL UNIQUE,
|
username VARCHAR(100) NOT NULL UNIQUE,
|
||||||
password VARCHAR(255) NOT NULL
|
password VARCHAR(255) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tasks (
|
CREATE TABLE IF NOT EXISTS tasks (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
user_id INT NOT NULL,
|
user_id INT NOT NULL,
|
||||||
title VARCHAR(255) NOT NULL,
|
title VARCHAR(255) NOT NULL,
|
||||||
description TEXT NOT NULL,
|
description TEXT NOT NULL,
|
||||||
deadline DATE NOT NULL,
|
deadline DATE NOT NULL,
|
||||||
status TINYINT(1) NOT NULL DEFAULT 0,
|
status TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@ -1,42 +1,42 @@
|
|||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
container_name: todo_db
|
container_name: todo_db
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: NajsilnejsieHeslo123.
|
MYSQL_ROOT_PASSWORD: NajsilnejsieHeslo123.
|
||||||
MYSQL_DATABASE: zkt_zadanie
|
MYSQL_DATABASE: zkt_zadanie
|
||||||
MYSQL_USER: todo_user
|
MYSQL_USER: todo_user
|
||||||
MYSQL_PASSWORD: SilneHeslo123.
|
MYSQL_PASSWORD: SilneHeslo123.
|
||||||
volumes:
|
volumes:
|
||||||
- todo_mysql_data:/var/lib/mysql
|
- todo_mysql_data:/var/lib/mysql
|
||||||
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
|
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
ports:
|
ports:
|
||||||
- "3307:3306"
|
- "3307:3306"
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: backend/Dockerfile
|
dockerfile: backend/Dockerfile
|
||||||
container_name: todo_backend
|
container_name: todo_backend
|
||||||
restart: always
|
restart: always
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: frontend/Dockerfile
|
dockerfile: frontend/Dockerfile
|
||||||
container_name: todo_frontend
|
container_name: todo_frontend
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
todo_mysql_data:
|
todo_mysql_data:
|
||||||
@ -1,9 +1,9 @@
|
|||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
||||||
|
|
||||||
COPY frontend/index.html /usr/share/nginx/html/index.html
|
COPY frontend/index.html /usr/share/nginx/html/index.html
|
||||||
COPY frontend/login.html /usr/share/nginx/html/login.html
|
COPY frontend/login.html /usr/share/nginx/html/login.html
|
||||||
COPY frontend/register.html /usr/share/nginx/html/register.html
|
COPY frontend/register.html /usr/share/nginx/html/register.html
|
||||||
COPY frontend/generovanie_karticiek.js /usr/share/nginx/html/generovanie_karticiek.js
|
COPY frontend/generovanie_karticiek.js /usr/share/nginx/html/generovanie_karticiek.js
|
||||||
COPY frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
@ -1,209 +1,209 @@
|
|||||||
const emptyMessage = document.getElementById("emptyMessage");
|
const emptyMessage = document.getElementById("emptyMessage");
|
||||||
const cardContainer = document.getElementById("cardContainer");
|
const cardContainer = document.getElementById("cardContainer");
|
||||||
const addCardBtn = document.getElementById("addCardBtn");
|
const addCardBtn = document.getElementById("addCardBtn");
|
||||||
const taskModal = document.getElementById("taskModal");
|
const taskModal = document.getElementById("taskModal");
|
||||||
const saveTaskBtn = document.getElementById("saveTaskBtn");
|
const saveTaskBtn = document.getElementById("saveTaskBtn");
|
||||||
const cancelTaskBtn = document.getElementById("cancelTaskBtn");
|
const cancelTaskBtn = document.getElementById("cancelTaskBtn");
|
||||||
const filterDate = document.getElementById("filterDate");
|
const filterDate = document.getElementById("filterDate");
|
||||||
const loggedUser = document.getElementById("loggedUser");
|
const loggedUser = document.getElementById("loggedUser");
|
||||||
|
|
||||||
let tasks = [];
|
let tasks = [];
|
||||||
|
|
||||||
function formatDate(isoDate) {
|
function formatDate(isoDate) {
|
||||||
if (!isoDate) return "bez termínu";
|
if (!isoDate) return "bez termínu";
|
||||||
return new Date(isoDate).toLocaleDateString("sk-SK", {
|
return new Date(isoDate).toLocaleDateString("sk-SK", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
year: "numeric"
|
year: "numeric"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateEmptyVisibility() {
|
function updateEmptyVisibility() {
|
||||||
if (tasks.length === 0) {
|
if (tasks.length === 0) {
|
||||||
emptyMessage.classList.remove("hidden");
|
emptyMessage.classList.remove("hidden");
|
||||||
} else {
|
} else {
|
||||||
emptyMessage.classList.add("hidden");
|
emptyMessage.classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCard(task) {
|
function createCard(task) {
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "p-6 rounded-xl shadow-md border hover:shadow-lg transition-all duration-200";
|
card.className = "p-6 rounded-xl shadow-md border hover:shadow-lg transition-all duration-200";
|
||||||
card.dataset.deadline = task.deadline || "";
|
card.dataset.deadline = task.deadline || "";
|
||||||
card.dataset.id = task.id;
|
card.dataset.id = task.id;
|
||||||
|
|
||||||
if (task.status) {
|
if (task.status) {
|
||||||
card.classList.add("bg-green-100", "border-green-200");
|
card.classList.add("bg-green-100", "border-green-200");
|
||||||
} else {
|
} else {
|
||||||
card.classList.add("bg-yellow-100", "border-yellow-200");
|
card.classList.add("bg-yellow-100", "border-yellow-200");
|
||||||
}
|
}
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<h3 class="font-bold text-lg mb-2 text-gray-800">${task.title}</h3>
|
<h3 class="font-bold text-lg mb-2 text-gray-800">${task.title}</h3>
|
||||||
<p class="text-gray-700 mb-4">${task.description}</p>
|
<p class="text-gray-700 mb-4">${task.description}</p>
|
||||||
<p class="text-sm text-gray-600 mb-5">Termín: ${formatDate(task.deadline)}</p>
|
<p class="text-sm text-gray-600 mb-5">Termín: ${formatDate(task.deadline)}</p>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<input type="checkbox" class="task-checkbox h-5 w-5 text-green-600 rounded border-gray-300 focus:ring-green-500" ${task.status ? "checked" : ""}>
|
<input type="checkbox" class="task-checkbox h-5 w-5 text-green-600 rounded border-gray-300 focus:ring-green-500" ${task.status ? "checked" : ""}>
|
||||||
<label class="ml-2 text-sm text-gray-700 cursor-pointer">Hotovo</label>
|
<label class="ml-2 text-sm text-gray-700 cursor-pointer">Hotovo</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="delete-btn px-5 py-2 bg-red-600 text-white text-sm rounded-lg hover:bg-red-700 transition">
|
<button class="delete-btn px-5 py-2 bg-red-600 text-white text-sm rounded-lg hover:bg-red-700 transition">
|
||||||
Zmazať
|
Zmazať
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const checkbox = card.querySelector(".task-checkbox");
|
const checkbox = card.querySelector(".task-checkbox");
|
||||||
checkbox.addEventListener("change", async () => {
|
checkbox.addEventListener("change", async () => {
|
||||||
const newValue = checkbox.checked ? 1 : 0;
|
const newValue = checkbox.checked ? 1 : 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/tasks/${task.id}/toggle`, {
|
const response = await fetch(`/api/tasks/${task.id}/toggle`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ status: newValue })
|
body: JSON.stringify({ status: newValue })
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.error("PATCH neprešiel:", response.status);
|
console.error("PATCH neprešiel:", response.status);
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
task.status = newValue;
|
task.status = newValue;
|
||||||
|
|
||||||
card.classList.remove(
|
card.classList.remove(
|
||||||
"bg-yellow-100",
|
"bg-yellow-100",
|
||||||
"border-yellow-200",
|
"border-yellow-200",
|
||||||
"bg-green-100",
|
"bg-green-100",
|
||||||
"border-green-200"
|
"border-green-200"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
card.classList.add("bg-green-100", "border-green-200");
|
card.classList.add("bg-green-100", "border-green-200");
|
||||||
} else {
|
} else {
|
||||||
card.classList.add("bg-yellow-100", "border-yellow-200");
|
card.classList.add("bg-yellow-100", "border-yellow-200");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Chyba fetchu:", error);
|
console.error("Chyba fetchu:", error);
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteBtn = card.querySelector(".delete-btn");
|
const deleteBtn = card.querySelector(".delete-btn");
|
||||||
deleteBtn.addEventListener("click", async () => {
|
deleteBtn.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/tasks/${task.id}`, {
|
const response = await fetch(`/api/tasks/${task.id}`, {
|
||||||
method: "DELETE"
|
method: "DELETE"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
tasks = tasks.filter(t => t.id !== task.id);
|
tasks = tasks.filter(t => t.id !== task.id);
|
||||||
renderTasks();
|
renderTasks();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Chyba pri mazaní úlohy:", error);
|
console.error("Chyba pri mazaní úlohy:", error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cardContainer.appendChild(card);
|
cardContainer.appendChild(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTasks() {
|
function renderTasks() {
|
||||||
const order = filterDate.value;
|
const order = filterDate.value;
|
||||||
|
|
||||||
const sortedTasks = [...tasks].sort((a, b) => {
|
const sortedTasks = [...tasks].sort((a, b) => {
|
||||||
const da = a.deadline ? new Date(a.deadline) : new Date("9999-12-31");
|
const da = a.deadline ? new Date(a.deadline) : new Date("9999-12-31");
|
||||||
const db = b.deadline ? new Date(b.deadline) : new Date("9999-12-31");
|
const db = b.deadline ? new Date(b.deadline) : new Date("9999-12-31");
|
||||||
return order === "asc" ? da - db : db - da;
|
return order === "asc" ? da - db : db - da;
|
||||||
});
|
});
|
||||||
|
|
||||||
cardContainer.innerHTML = "";
|
cardContainer.innerHTML = "";
|
||||||
sortedTasks.forEach(createCard);
|
sortedTasks.forEach(createCard);
|
||||||
updateEmptyVisibility();
|
updateEmptyVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showModal() {
|
function showModal() {
|
||||||
taskModal.classList.remove("hidden");
|
taskModal.classList.remove("hidden");
|
||||||
document.getElementById("taskTitle").value = "";
|
document.getElementById("taskTitle").value = "";
|
||||||
document.getElementById("taskDescription").value = "";
|
document.getElementById("taskDescription").value = "";
|
||||||
document.getElementById("taskDeadline").value = "";
|
document.getElementById("taskDeadline").value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideModal() {
|
function hideModal() {
|
||||||
taskModal.classList.add("hidden");
|
taskModal.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadUser() {
|
async function loadUser() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/me");
|
const response = await fetch("/api/me");
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
window.location.href = "/login.html";
|
window.location.href = "/login.html";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await response.json();
|
const user = await response.json();
|
||||||
loggedUser.textContent = `Prihlásený ako: ${user.username}`;
|
loggedUser.textContent = `Prihlásený ako: ${user.username}`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Chyba pri načítaní používateľa:", error);
|
console.error("Chyba pri načítaní používateľa:", error);
|
||||||
window.location.href = "/login.html";
|
window.location.href = "/login.html";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadTasks() {
|
async function loadTasks() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/tasks");
|
const response = await fetch("/api/tasks");
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
window.location.href = "/login.html";
|
window.location.href = "/login.html";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks = await response.json();
|
tasks = await response.json();
|
||||||
renderTasks();
|
renderTasks();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Chyba pri načítaní úloh:", error);
|
console.error("Chyba pri načítaní úloh:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addCardBtn.addEventListener("click", showModal);
|
addCardBtn.addEventListener("click", showModal);
|
||||||
cancelTaskBtn.addEventListener("click", hideModal);
|
cancelTaskBtn.addEventListener("click", hideModal);
|
||||||
|
|
||||||
saveTaskBtn.addEventListener("click", async () => {
|
saveTaskBtn.addEventListener("click", async () => {
|
||||||
const title = document.getElementById("taskTitle").value.trim();
|
const title = document.getElementById("taskTitle").value.trim();
|
||||||
const description = document.getElementById("taskDescription").value.trim();
|
const description = document.getElementById("taskDescription").value.trim();
|
||||||
const deadline = document.getElementById("taskDeadline").value;
|
const deadline = document.getElementById("taskDeadline").value;
|
||||||
|
|
||||||
if (!title || !description || !deadline) {
|
if (!title || !description || !deadline) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/tasks", {
|
const response = await fetch("/api/tasks", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ title, description, deadline })
|
body: JSON.stringify({ title, description, deadline })
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const newTask = await response.json();
|
const newTask = await response.json();
|
||||||
tasks.push(newTask);
|
tasks.push(newTask);
|
||||||
renderTasks();
|
renderTasks();
|
||||||
hideModal();
|
hideModal();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Chyba pri pridávaní úlohy:", error);
|
console.error("Chyba pri pridávaní úlohy:", error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
filterDate.addEventListener("change", renderTasks);
|
filterDate.addEventListener("change", renderTasks);
|
||||||
|
|
||||||
loadUser();
|
loadUser();
|
||||||
loadTasks();
|
loadTasks();
|
||||||
@ -1,94 +1,94 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="sk">
|
<html lang="sk">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>To-Do aplikácia</title>
|
<title>To-Do aplikácia</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<style>
|
<style>
|
||||||
select {
|
select {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
|
||||||
background-position: right 0.75rem center;
|
background-position: right 0.75rem center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 1.25em;
|
background-size: 1.25em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="flex flex-col min-h-screen bg-gray-100">
|
<body class="flex flex-col min-h-screen bg-gray-100">
|
||||||
|
|
||||||
<header class="bg-orange-500 h-20 flex justify-between items-center shadow-md px-6">
|
<header class="bg-orange-500 h-20 flex justify-between items-center shadow-md px-6">
|
||||||
<h1 class="text-white font-bold text-2xl">To-Do aplikácia</h1>
|
<h1 class="text-white font-bold text-2xl">To-Do aplikácia</h1>
|
||||||
|
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<span id="loggedUser" class="text-white font-medium"></span>
|
<span id="loggedUser" class="text-white font-medium"></span>
|
||||||
<form action="/logout" method="POST">
|
<form action="/logout" method="POST">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="bg-red-600 hover:bg-red-700 text-white font-medium px-4 py-2 rounded-lg transition"
|
class="bg-red-600 hover:bg-red-700 text-white font-medium px-4 py-2 rounded-lg transition"
|
||||||
>
|
>
|
||||||
Odhlásiť sa
|
Odhlásiť sa
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex-1 p-6 flex flex-col items-center bg-gray-50">
|
<main class="flex-1 p-6 flex flex-col items-center bg-gray-50">
|
||||||
|
|
||||||
<div class="w-full max-w-3xl mb-8 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
<div class="w-full max-w-3xl mb-8 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||||
|
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<select id="filterDate" class="pl-10 pr-4 py-2.5 bg-white border border-gray-300 rounded-lg shadow-sm focus:border-orange-400 focus:ring-orange-400 appearance-none">
|
<select id="filterDate" class="pl-10 pr-4 py-2.5 bg-white border border-gray-300 rounded-lg shadow-sm focus:border-orange-400 focus:ring-orange-400 appearance-none">
|
||||||
<option value="asc">Najskôr najbližší termín</option>
|
<option value="asc">Najskôr najbližší termín</option>
|
||||||
<option value="desc">Najskôr najvzdialenejší termín</option>
|
<option value="desc">Najskôr najvzdialenejší termín</option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="addCardBtn" class="px-7 py-3.5 bg-orange-600 hover:bg-orange-700 text-white font-semibold rounded-full shadow-lg transition-all transform hover:scale-105 flex items-center gap-2">
|
<button id="addCardBtn" class="px-7 py-3.5 bg-orange-600 hover:bg-orange-700 text-white font-semibold rounded-full shadow-lg transition-all transform hover:scale-105 flex items-center gap-2">
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||||
</svg>
|
</svg>
|
||||||
Pridať úlohu
|
Pridať úlohu
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="emptyMessage" class="hidden text-center py-16 w-full max-w-3xl">
|
<div id="emptyMessage" class="hidden text-center py-16 w-full max-w-3xl">
|
||||||
<p class="text-2xl font-medium text-gray-500 mb-6">Zatiaľ žiadne úlohy</p>
|
<p class="text-2xl font-medium text-gray-500 mb-6">Zatiaľ žiadne úlohy</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="cardContainer" class="w-full max-w-3xl space-y-5">
|
<div id="cardContainer" class="w-full max-w-3xl space-y-5">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="bg-orange-500 h-16 flex justify-center items-center text-white font-medium shadow-inner">
|
<footer class="bg-orange-500 h-16 flex justify-center items-center text-white font-medium shadow-inner">
|
||||||
<div>By Michal</div>
|
<div>By Michal</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<div id="taskModal" class="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center hidden z-50">
|
<div id="taskModal" class="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center hidden z-50">
|
||||||
<div class="bg-white rounded-xl p-8 w-full max-w-md shadow-2xl">
|
<div class="bg-white rounded-xl p-8 w-full max-w-md shadow-2xl">
|
||||||
<h2 class="text-2xl font-bold mb-6 text-gray-800">Nová úloha</h2>
|
<h2 class="text-2xl font-bold mb-6 text-gray-800">Nová úloha</h2>
|
||||||
|
|
||||||
<label for="taskTitle" class="block text-sm font-medium text-gray-700 mb-1">Názov</label>
|
<label for="taskTitle" class="block text-sm font-medium text-gray-700 mb-1">Názov</label>
|
||||||
<input id="taskTitle" type="text" class="w-full p-3 border border-gray-300 rounded-lg mb-4 focus:outline-none focus:ring-2 focus:ring-orange-400" placeholder="Zadajte názov úlohy">
|
<input id="taskTitle" type="text" class="w-full p-3 border border-gray-300 rounded-lg mb-4 focus:outline-none focus:ring-2 focus:ring-orange-400" placeholder="Zadajte názov úlohy">
|
||||||
|
|
||||||
<label for="taskDescription" class="block text-sm font-medium text-gray-700 mb-1">Popis</label>
|
<label for="taskDescription" class="block text-sm font-medium text-gray-700 mb-1">Popis</label>
|
||||||
<textarea id="taskDescription" class="w-full p-3 border border-gray-300 rounded-lg mb-4 focus:outline-none focus:ring-2 focus:ring-orange-400" rows="4" placeholder="Detailnejší popis..."></textarea>
|
<textarea id="taskDescription" class="w-full p-3 border border-gray-300 rounded-lg mb-4 focus:outline-none focus:ring-2 focus:ring-orange-400" rows="4" placeholder="Detailnejší popis..."></textarea>
|
||||||
|
|
||||||
<label for="taskDeadline" class="block text-sm font-medium text-gray-700 mb-1">Termín dokončenia</label>
|
<label for="taskDeadline" class="block text-sm font-medium text-gray-700 mb-1">Termín dokončenia</label>
|
||||||
<input id="taskDeadline" type="date" class="w-full p-3 border border-gray-300 rounded-lg mb-6 focus:outline-none focus:ring-2 focus:ring-orange-400">
|
<input id="taskDeadline" type="date" class="w-full p-3 border border-gray-300 rounded-lg mb-6 focus:outline-none focus:ring-2 focus:ring-orange-400">
|
||||||
|
|
||||||
<div class="flex justify-end gap-3">
|
<div class="flex justify-end gap-3">
|
||||||
<button id="cancelTaskBtn" class="px-5 py-2.5 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition">Zrušiť</button>
|
<button id="cancelTaskBtn" class="px-5 py-2.5 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition">Zrušiť</button>
|
||||||
<button id="saveTaskBtn" class="px-5 py-2.5 bg-green-600 text-white rounded-lg hover:bg-green-700 transition">Uložiť</button>
|
<button id="saveTaskBtn" class="px-5 py-2.5 bg-green-600 text-white rounded-lg hover:bg-green-700 transition">Uložiť</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="generovanie_karticiek.js"></script>
|
<script src="generovanie_karticiek.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,77 +1,77 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="sk">
|
<html lang="sk">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>Prihlásenie</title>
|
<title>Prihlásenie</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
<body class="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
||||||
|
|
||||||
<div class="w-full max-w-sm bg-white rounded-xl shadow-lg p-8">
|
<div class="w-full max-w-sm bg-white rounded-xl shadow-lg p-8">
|
||||||
|
|
||||||
<h1 class="text-3xl font-bold text-center text-gray-800 mb-8">
|
<h1 class="text-3xl font-bold text-center text-gray-800 mb-8">
|
||||||
Prihlásenie
|
Prihlásenie
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<form id="loginForm" action="/login" method="POST" class="space-y-6">
|
<form id="loginForm" action="/login" method="POST" class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label for="meno" class="block text-sm font-medium text-gray-700 mb-1">
|
<label for="meno" class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Meno / používateľ
|
Meno / používateľ
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="meno"
|
id="meno"
|
||||||
name="username"
|
name="username"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
||||||
placeholder="Zadaj svoje meno"
|
placeholder="Zadaj svoje meno"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="heslo" class="block text-sm font-medium text-gray-700 mb-1">
|
<label for="heslo" class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Heslo
|
Heslo
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="heslo"
|
id="heslo"
|
||||||
name="password"
|
name="password"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 rounded-lg transition duration-200"
|
class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 rounded-lg transition duration-200"
|
||||||
>
|
>
|
||||||
Prihlásiť sa
|
Prihlásiť sa
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="text-center mt-6 text-sm text-gray-600">
|
<div class="text-center mt-6 text-sm text-gray-600">
|
||||||
Ešte nemáš účet?
|
Ešte nemáš účet?
|
||||||
<a href="/register.html" class="text-orange-600 hover:underline font-medium">
|
<a href="/register.html" class="text-orange-600 hover:underline font-medium">
|
||||||
Zaregistruj sa
|
Zaregistruj sa
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p id="error" class="hidden mt-4 text-sm text-red-700 bg-red-50 border border-red-200 rounded-lg px-4 py-3 text-center"></p>
|
<p id="error" class="hidden mt-4 text-sm text-red-700 bg-red-50 border border-red-200 rounded-lg px-4 py-3 text-center"></p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const error = params.get('error');
|
const error = params.get('error');
|
||||||
const errorBox = document.getElementById('error');
|
const errorBox = document.getElementById('error');
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
errorBox.textContent = error;
|
errorBox.textContent = error;
|
||||||
errorBox.classList.remove('hidden');
|
errorBox.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,42 +1,42 @@
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index login.html;
|
index login.html;
|
||||||
|
|
||||||
location = / {
|
location = / {
|
||||||
try_files /login.html =404;
|
try_files /login.html =404;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://backend:3000;
|
proxy_pass http://backend:3000;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /login {
|
location = /login {
|
||||||
proxy_pass http://backend:3000/login;
|
proxy_pass http://backend:3000/login;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /register {
|
location = /register {
|
||||||
proxy_pass http://backend:3000/register;
|
proxy_pass http://backend:3000/register;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /logout {
|
location = /logout {
|
||||||
proxy_pass http://backend:3000/logout;
|
proxy_pass http://backend:3000/logout;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ =404;
|
try_files $uri $uri/ =404;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,91 +1,91 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="sk">
|
<html lang="sk">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>Registrácia</title>
|
<title>Registrácia</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
<body class="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
||||||
|
|
||||||
<div class="w-full max-w-sm bg-white rounded-xl shadow-lg p-8">
|
<div class="w-full max-w-sm bg-white rounded-xl shadow-lg p-8">
|
||||||
|
|
||||||
<h1 class="text-3xl font-bold text-center text-gray-800 mb-8">
|
<h1 class="text-3xl font-bold text-center text-gray-800 mb-8">
|
||||||
Registrácia
|
Registrácia
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<form id="registerForm" action="/register" method="POST" class="space-y-6">
|
<form id="registerForm" action="/register" method="POST" class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label for="meno" class="block text-sm font-medium text-gray-700 mb-1">
|
<label for="meno" class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Meno / používateľ
|
Meno / používateľ
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="meno"
|
id="meno"
|
||||||
name="username"
|
name="username"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
||||||
placeholder="Zadaj svoje meno"
|
placeholder="Zadaj svoje meno"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="heslo" class="block text-sm font-medium text-gray-700 mb-1">
|
<label for="heslo" class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Heslo
|
Heslo
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="heslo"
|
id="heslo"
|
||||||
name="password"
|
name="password"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
||||||
placeholder="••••••••"
|
placeholder="••••••••"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="heslo2" class="block text-sm font-medium text-gray-700 mb-1">
|
<label for="heslo2" class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Heslo znova
|
Heslo znova
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="heslo2"
|
id="heslo2"
|
||||||
name="password2"
|
name="password2"
|
||||||
required
|
required
|
||||||
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"
|
||||||
placeholder="Potvrď heslo"
|
placeholder="Potvrď heslo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 rounded-lg transition duration-200"
|
class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 rounded-lg transition duration-200"
|
||||||
>
|
>
|
||||||
Vytvoriť účet
|
Vytvoriť účet
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="text-center mt-6 text-sm text-gray-600">
|
<div class="text-center mt-6 text-sm text-gray-600">
|
||||||
Už máš účet?
|
Už máš účet?
|
||||||
<a href="/login.html" class="text-orange-600 hover:underline font-medium">
|
<a href="/login.html" class="text-orange-600 hover:underline font-medium">
|
||||||
Prihlás sa
|
Prihlás sa
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p id="error" class="hidden mt-4 text-sm text-red-700 bg-red-50 border border-red-200 rounded-lg px-4 py-3 text-center"></p>
|
<p id="error" class="hidden mt-4 text-sm text-red-700 bg-red-50 border border-red-200 rounded-lg px-4 py-3 text-center"></p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const error = params.get('error');
|
const error = params.get('error');
|
||||||
const errorBox = document.getElementById('error');
|
const errorBox = document.getElementById('error');
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
errorBox.textContent = error;
|
errorBox.textContent = error;
|
||||||
errorBox.classList.remove('hidden');
|
errorBox.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
4296
z1/package-lock.json
generated
4296
z1/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"body-parser": "^2.2.2",
|
"body-parser": "^2.2.2",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-session": "^1.19.0",
|
"express-session": "^1.19.0",
|
||||||
"mysql2": "^3.20.0",
|
"mysql2": "^3.20.0",
|
||||||
"sqlite3": "^6.0.1"
|
"sqlite3": "^6.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user