first commit
This commit is contained in:
117
app/controllers/AuthController.php
Normal file
117
app/controllers/AuthController.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../models/User.php';
|
||||
|
||||
class AuthController
|
||||
{
|
||||
public function loginForm(): void
|
||||
{
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
require __DIR__ . '/../views/login.php';
|
||||
}
|
||||
|
||||
public function login(): void
|
||||
{
|
||||
$login = trim($_POST['login'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($login) || empty($password)) {
|
||||
$_SESSION['error'] = 'Proszę podać login i hasło.';
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = User::verifyPassword($login, $password);
|
||||
|
||||
if (!$user) {
|
||||
$_SESSION['error'] = 'Nieprawidłowy login lub hasło.';
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Generowanie kodu weryfikacyjnego
|
||||
$code = sprintf('%06d', random_int(0, 999999));
|
||||
|
||||
User::saveVerificationCode($user['id'], $code);
|
||||
|
||||
// Symulacja wysyłki maila
|
||||
$_SESSION['pending_user_id'] = $user['id'];
|
||||
$_SESSION['pending_user_login'] = $user['login'];
|
||||
$_SESSION['simulated_code'] = $code; // W produkcji nie pokazywać!
|
||||
|
||||
header('Location: /weryfikacja');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function verifyForm(): void
|
||||
{
|
||||
if (!isset($_SESSION['pending_user_id'])) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
$simulatedCode = $_SESSION['simulated_code'] ?? null;
|
||||
$userLogin = $_SESSION['pending_user_login'] ?? '';
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require __DIR__ . '/../views/verify.php';
|
||||
}
|
||||
|
||||
public function verify(): void
|
||||
{
|
||||
if (!isset($_SESSION['pending_user_id'])) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$code = trim($_POST['code'] ?? '');
|
||||
$userId = $_SESSION['pending_user_id'];
|
||||
|
||||
if (empty($code)) {
|
||||
$_SESSION['error'] = 'Proszę podać kod weryfikacyjny.';
|
||||
header('Location: /weryfikacja');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!User::verifyCode($userId, $code)) {
|
||||
$_SESSION['error'] = 'Nieprawidłowy lub wygasły kod weryfikacyjny.';
|
||||
header('Location: /weryfikacja');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Usunięcie kodu po użyciu
|
||||
User::deleteVerificationCode($userId);
|
||||
|
||||
// Pełna autoryzacja
|
||||
$_SESSION['user_id'] = $userId;
|
||||
$_SESSION['user_login'] = $_SESSION['pending_user_login'];
|
||||
$_SESSION['logged_in'] = true;
|
||||
|
||||
unset($_SESSION['pending_user_id']);
|
||||
unset($_SESSION['pending_user_login']);
|
||||
unset($_SESSION['simulated_code']);
|
||||
|
||||
header('Location: /panel');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function dashboard(): void
|
||||
{
|
||||
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$userLogin = $_SESSION['user_login'] ?? 'Użytkownik';
|
||||
require __DIR__ . '/../views/dashboard.php';
|
||||
}
|
||||
|
||||
public function logout(): void
|
||||
{
|
||||
session_destroy();
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
179
app/controllers/EventController.php
Normal file
179
app/controllers/EventController.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../models/Event.php';
|
||||
|
||||
class EventController
|
||||
{
|
||||
private function requireAuth(): void
|
||||
{
|
||||
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function index(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
|
||||
// Pobierz rok i miesiąc z parametrów lub użyj bieżących
|
||||
$year = isset($_GET['rok']) ? (int) $_GET['rok'] : (int) date('Y');
|
||||
$month = isset($_GET['miesiac']) ? (int) $_GET['miesiac'] : (int) date('m');
|
||||
|
||||
// Walidacja
|
||||
if ($month < 1) {
|
||||
$month = 12;
|
||||
$year--;
|
||||
} elseif ($month > 12) {
|
||||
$month = 1;
|
||||
$year++;
|
||||
}
|
||||
|
||||
$events = Event::getByMonth($userId, $year, $month);
|
||||
|
||||
// Grupowanie wydarzeń po dacie
|
||||
$eventsByDate = [];
|
||||
foreach ($events as $event) {
|
||||
$eventsByDate[$event['event_date']][] = $event;
|
||||
}
|
||||
|
||||
$success = $_SESSION['success'] ?? null;
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['success'], $_SESSION['error']);
|
||||
|
||||
require __DIR__ . '/../views/calendar/index.php';
|
||||
}
|
||||
|
||||
public function dayEvents(string $date): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
|
||||
// Walidacja daty
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
|
||||
$_SESSION['error'] = 'Nieprawidłowy format daty.';
|
||||
header('Location: /kalendarz');
|
||||
exit;
|
||||
}
|
||||
|
||||
$events = Event::getByDate($userId, $date);
|
||||
|
||||
$success = $_SESSION['success'] ?? null;
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['success'], $_SESSION['error']);
|
||||
|
||||
require __DIR__ . '/../views/calendar/day.php';
|
||||
}
|
||||
|
||||
public function create(string $date = null): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
$event = null;
|
||||
$isEdit = false;
|
||||
$selectedDate = $date ?? date('Y-m-d');
|
||||
|
||||
require __DIR__ . '/../views/calendar/form.php';
|
||||
}
|
||||
|
||||
public function store(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$content = trim($_POST['content'] ?? '');
|
||||
$eventDate = $_POST['event_date'] ?? '';
|
||||
|
||||
if (empty($title)) {
|
||||
$_SESSION['error'] = 'Tytuł wydarzenia jest wymagany.';
|
||||
header('Location: /kalendarz/nowe');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($eventDate)) {
|
||||
$_SESSION['error'] = 'Data wydarzenia jest wymagana.';
|
||||
header('Location: /kalendarz/nowe');
|
||||
exit;
|
||||
}
|
||||
|
||||
Event::create($userId, $title, $content, $eventDate);
|
||||
$_SESSION['success'] = 'Wydarzenie zostało dodane.';
|
||||
header('Location: /kalendarz/dzien/' . $eventDate);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function edit(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
$event = Event::getById($id, $userId);
|
||||
|
||||
if (!$event) {
|
||||
$_SESSION['error'] = 'Wydarzenie nie zostało znalezione.';
|
||||
header('Location: /kalendarz');
|
||||
exit;
|
||||
}
|
||||
|
||||
$isEdit = true;
|
||||
$selectedDate = $event['event_date'];
|
||||
|
||||
require __DIR__ . '/../views/calendar/form.php';
|
||||
}
|
||||
|
||||
public function update(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$content = trim($_POST['content'] ?? '');
|
||||
$eventDate = $_POST['event_date'] ?? '';
|
||||
|
||||
if (empty($title)) {
|
||||
$_SESSION['error'] = 'Tytuł wydarzenia jest wymagany.';
|
||||
header("Location: /kalendarz/edytuj/$id");
|
||||
exit;
|
||||
}
|
||||
|
||||
$event = Event::getById($id, $userId);
|
||||
if (!$event) {
|
||||
$_SESSION['error'] = 'Wydarzenie nie zostało znalezione.';
|
||||
header('Location: /kalendarz');
|
||||
exit;
|
||||
}
|
||||
|
||||
Event::update($id, $userId, $title, $content, $eventDate);
|
||||
$_SESSION['success'] = 'Wydarzenie zostało zaktualizowane.';
|
||||
header('Location: /kalendarz/dzien/' . $eventDate);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$event = Event::getById($id, $userId);
|
||||
|
||||
if (!$event) {
|
||||
$_SESSION['error'] = 'Wydarzenie nie zostało znalezione.';
|
||||
header('Location: /kalendarz');
|
||||
exit;
|
||||
}
|
||||
|
||||
$eventDate = $event['event_date'];
|
||||
Event::delete($id, $userId);
|
||||
$_SESSION['success'] = 'Wydarzenie zostało usunięte.';
|
||||
header('Location: /kalendarz/dzien/' . $eventDate);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
126
app/controllers/NoteController.php
Normal file
126
app/controllers/NoteController.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../models/Note.php';
|
||||
|
||||
class NoteController
|
||||
{
|
||||
private function requireAuth(): void
|
||||
{
|
||||
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function index(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
$notes = Note::getAllByUser($userId);
|
||||
|
||||
$success = $_SESSION['success'] ?? null;
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['success'], $_SESSION['error']);
|
||||
|
||||
require __DIR__ . '/../views/notes/index.php';
|
||||
}
|
||||
|
||||
public function create(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
$note = null;
|
||||
$isEdit = false;
|
||||
|
||||
require __DIR__ . '/../views/notes/form.php';
|
||||
}
|
||||
|
||||
public function store(): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$content = trim($_POST['content'] ?? '');
|
||||
$color = $_POST['color'] ?? 'primary';
|
||||
|
||||
if (empty($title)) {
|
||||
$_SESSION['error'] = 'Tytuł notatki jest wymagany.';
|
||||
header('Location: /notatnik/nowa');
|
||||
exit;
|
||||
}
|
||||
|
||||
Note::create($userId, $title, $content, $color);
|
||||
$_SESSION['success'] = 'Notatka została dodana.';
|
||||
header('Location: /notatnik');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function edit(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$userLogin = $_SESSION['user_login'];
|
||||
$note = Note::getById($id, $userId);
|
||||
|
||||
if (!$note) {
|
||||
$_SESSION['error'] = 'Notatka nie została znaleziona.';
|
||||
header('Location: /notatnik');
|
||||
exit;
|
||||
}
|
||||
|
||||
$isEdit = true;
|
||||
require __DIR__ . '/../views/notes/form.php';
|
||||
}
|
||||
|
||||
public function update(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$content = trim($_POST['content'] ?? '');
|
||||
$color = $_POST['color'] ?? 'primary';
|
||||
|
||||
if (empty($title)) {
|
||||
$_SESSION['error'] = 'Tytuł notatki jest wymagany.';
|
||||
header("Location: /notatnik/edytuj/$id");
|
||||
exit;
|
||||
}
|
||||
|
||||
$note = Note::getById($id, $userId);
|
||||
if (!$note) {
|
||||
$_SESSION['error'] = 'Notatka nie została znaleziona.';
|
||||
header('Location: /notatnik');
|
||||
exit;
|
||||
}
|
||||
|
||||
Note::update($id, $userId, $title, $content, $color);
|
||||
$_SESSION['success'] = 'Notatka została zaktualizowana.';
|
||||
header('Location: /notatnik');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$this->requireAuth();
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$note = Note::getById($id, $userId);
|
||||
|
||||
if (!$note) {
|
||||
$_SESSION['error'] = 'Notatka nie została znaleziona.';
|
||||
header('Location: /notatnik');
|
||||
exit;
|
||||
}
|
||||
|
||||
Note::delete($id, $userId);
|
||||
$_SESSION['success'] = 'Notatka została usunięta.';
|
||||
header('Location: /notatnik');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
22
app/models/Database.php
Normal file
22
app/models/Database.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class Database
|
||||
{
|
||||
private static ?PDO $instance = null;
|
||||
private static string $dbPath = __DIR__ . '/../../database/app.db';
|
||||
|
||||
public static function getInstance(): PDO
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new PDO('sqlite:' . self::$dbPath);
|
||||
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
self::$instance->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function getDbPath(): string
|
||||
{
|
||||
return self::$dbPath;
|
||||
}
|
||||
}
|
||||
182
app/models/Event.php
Normal file
182
app/models/Event.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/Database.php';
|
||||
|
||||
class Event
|
||||
{
|
||||
public static function ensureTable(): void
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$db->exec('
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT,
|
||||
event_date DATE NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
');
|
||||
}
|
||||
|
||||
public static function getAllByUser(int $userId): array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM events
|
||||
WHERE user_id = :user_id
|
||||
ORDER BY event_date ASC
|
||||
');
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getByMonth(int $userId, int $year, int $month): array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$startDate = sprintf('%04d-%02d-01', $year, $month);
|
||||
$endDate = sprintf('%04d-%02d-%02d', $year, $month, cal_days_in_month(CAL_GREGORIAN, $month, $year));
|
||||
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM events
|
||||
WHERE user_id = :user_id
|
||||
AND event_date BETWEEN :start_date AND :end_date
|
||||
ORDER BY event_date ASC
|
||||
');
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate
|
||||
]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getByDate(int $userId, string $date): array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM events
|
||||
WHERE user_id = :user_id AND event_date = :event_date
|
||||
ORDER BY created_at ASC
|
||||
');
|
||||
$stmt->execute(['user_id' => $userId, 'event_date' => $date]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getById(int $id, int $userId): ?array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM events
|
||||
WHERE id = :id AND user_id = :user_id
|
||||
');
|
||||
$stmt->execute(['id' => $id, 'user_id' => $userId]);
|
||||
$event = $stmt->fetch();
|
||||
return $event ?: null;
|
||||
}
|
||||
|
||||
public static function create(int $userId, string $title, string $content, string $eventDate): int
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
INSERT INTO events (user_id, title, content, event_date, created_at, updated_at)
|
||||
VALUES (:user_id, :title, :content, :event_date, datetime("now"), datetime("now"))
|
||||
');
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'event_date' => $eventDate
|
||||
]);
|
||||
return (int) $db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function update(int $id, int $userId, string $title, string $content, string $eventDate): bool
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
UPDATE events
|
||||
SET title = :title, content = :content, event_date = :event_date, updated_at = datetime("now")
|
||||
WHERE id = :id AND user_id = :user_id
|
||||
');
|
||||
return $stmt->execute([
|
||||
'id' => $id,
|
||||
'user_id' => $userId,
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'event_date' => $eventDate
|
||||
]);
|
||||
}
|
||||
|
||||
public static function delete(int $id, int $userId): bool
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
DELETE FROM events
|
||||
WHERE id = :id AND user_id = :user_id
|
||||
');
|
||||
return $stmt->execute(['id' => $id, 'user_id' => $userId]);
|
||||
}
|
||||
|
||||
public static function countByUser(int $userId): int
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM events WHERE user_id = :user_id');
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return (int) $stmt->fetch()['count'];
|
||||
}
|
||||
|
||||
public static function countThisWeek(int $userId): int
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Poniedziałek tego tygodnia
|
||||
$monday = date('Y-m-d', strtotime('monday this week'));
|
||||
// Niedziela tego tygodnia
|
||||
$sunday = date('Y-m-d', strtotime('sunday this week'));
|
||||
|
||||
$stmt = $db->prepare('
|
||||
SELECT COUNT(*) as count FROM events
|
||||
WHERE user_id = :user_id
|
||||
AND event_date BETWEEN :monday AND :sunday
|
||||
');
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'monday' => $monday,
|
||||
'sunday' => $sunday
|
||||
]);
|
||||
return (int) $stmt->fetch()['count'];
|
||||
}
|
||||
|
||||
public static function getThisWeek(int $userId): array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
|
||||
$monday = date('Y-m-d', strtotime('monday this week'));
|
||||
$sunday = date('Y-m-d', strtotime('sunday this week'));
|
||||
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM events
|
||||
WHERE user_id = :user_id
|
||||
AND event_date BETWEEN :monday AND :sunday
|
||||
ORDER BY event_date ASC
|
||||
');
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'monday' => $monday,
|
||||
'sunday' => $sunday
|
||||
]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
103
app/models/Note.php
Normal file
103
app/models/Note.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/Database.php';
|
||||
|
||||
class Note
|
||||
{
|
||||
public static function ensureTable(): void
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$db->exec('
|
||||
CREATE TABLE IF NOT EXISTS notes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT,
|
||||
color VARCHAR(20) DEFAULT "primary",
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
');
|
||||
}
|
||||
|
||||
public static function getAllByUser(int $userId): array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM notes
|
||||
WHERE user_id = :user_id
|
||||
ORDER BY updated_at DESC
|
||||
');
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getById(int $id, int $userId): ?array
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM notes
|
||||
WHERE id = :id AND user_id = :user_id
|
||||
');
|
||||
$stmt->execute(['id' => $id, 'user_id' => $userId]);
|
||||
$note = $stmt->fetch();
|
||||
return $note ?: null;
|
||||
}
|
||||
|
||||
public static function create(int $userId, string $title, string $content, string $color = 'primary'): int
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
INSERT INTO notes (user_id, title, content, color, created_at, updated_at)
|
||||
VALUES (:user_id, :title, :content, :color, datetime("now"), datetime("now"))
|
||||
');
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'color' => $color
|
||||
]);
|
||||
return (int) $db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function update(int $id, int $userId, string $title, string $content, string $color): bool
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
UPDATE notes
|
||||
SET title = :title, content = :content, color = :color, updated_at = datetime("now")
|
||||
WHERE id = :id AND user_id = :user_id
|
||||
');
|
||||
return $stmt->execute([
|
||||
'id' => $id,
|
||||
'user_id' => $userId,
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'color' => $color
|
||||
]);
|
||||
}
|
||||
|
||||
public static function delete(int $id, int $userId): bool
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
DELETE FROM notes
|
||||
WHERE id = :id AND user_id = :user_id
|
||||
');
|
||||
return $stmt->execute(['id' => $id, 'user_id' => $userId]);
|
||||
}
|
||||
|
||||
public static function countByUser(int $userId): int
|
||||
{
|
||||
self::ensureTable();
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM notes WHERE user_id = :user_id');
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return (int) $stmt->fetch()['count'];
|
||||
}
|
||||
}
|
||||
61
app/models/User.php
Normal file
61
app/models/User.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/Database.php';
|
||||
|
||||
class User
|
||||
{
|
||||
public static function findByLogin(string $login): ?array
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('SELECT * FROM users WHERE login = :login');
|
||||
$stmt->execute(['login' => $login]);
|
||||
$user = $stmt->fetch();
|
||||
return $user ?: null;
|
||||
}
|
||||
|
||||
public static function verifyPassword(string $login, string $password): ?array
|
||||
{
|
||||
$user = self::findByLogin($login);
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
return $user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function saveVerificationCode(int $userId, string $code): bool
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$expiresAt = date('Y-m-d H:i:s', strtotime('+10 minutes'));
|
||||
|
||||
$stmt = $db->prepare('
|
||||
INSERT OR REPLACE INTO verification_codes (user_id, code, expires_at, created_at)
|
||||
VALUES (:user_id, :code, :expires_at, datetime("now"))
|
||||
');
|
||||
|
||||
return $stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'code' => $code,
|
||||
'expires_at' => $expiresAt
|
||||
]);
|
||||
}
|
||||
|
||||
public static function verifyCode(int $userId, string $code): bool
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('
|
||||
SELECT * FROM verification_codes
|
||||
WHERE user_id = :user_id
|
||||
AND code = :code
|
||||
AND expires_at > datetime("now")
|
||||
');
|
||||
$stmt->execute(['user_id' => $userId, 'code' => $code]);
|
||||
return (bool) $stmt->fetch();
|
||||
}
|
||||
|
||||
public static function deleteVerificationCode(int $userId): bool
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->prepare('DELETE FROM verification_codes WHERE user_id = :user_id');
|
||||
return $stmt->execute(['user_id' => $userId]);
|
||||
}
|
||||
}
|
||||
164
app/views/calendar/day.php
Normal file
164
app/views/calendar/day.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../layout.php';
|
||||
|
||||
$dayNames = [
|
||||
1 => 'Poniedziałek', 2 => 'Wtorek', 3 => 'Środa', 4 => 'Czwartek',
|
||||
5 => 'Piątek', 6 => 'Sobota', 7 => 'Niedziela'
|
||||
];
|
||||
|
||||
$monthNames = [
|
||||
1 => 'stycznia', 2 => 'lutego', 3 => 'marca', 4 => 'kwietnia',
|
||||
5 => 'maja', 6 => 'czerwca', 7 => 'lipca', 8 => 'sierpnia',
|
||||
9 => 'września', 10 => 'października', 11 => 'listopada', 12 => 'grudnia'
|
||||
];
|
||||
|
||||
$timestamp = strtotime($date);
|
||||
$dayOfWeek = date('N', $timestamp);
|
||||
$day = date('j', $timestamp);
|
||||
$month = date('n', $timestamp);
|
||||
$year = date('Y', $timestamp);
|
||||
|
||||
$formattedDate = $dayNames[$dayOfWeek] . ', ' . $day . ' ' . $monthNames[$month] . ' ' . $year;
|
||||
$isToday = ($date === date('Y-m-d'));
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-check-circle-fill me-2"></i>
|
||||
<?= htmlspecialchars($success) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<a href="/kalendarz?rok=<?= $year ?>&miesiac=<?= $month ?>" class="btn btn-outline-light mb-2">
|
||||
<i class="bi bi-arrow-left me-1"></i>Powrót do kalendarza
|
||||
</a>
|
||||
<h2 class="text-white mb-0">
|
||||
<?php if ($isToday): ?>
|
||||
<span class="badge bg-success me-2">Dzisiaj</span>
|
||||
<?php endif; ?>
|
||||
<?= $formattedDate ?>
|
||||
</h2>
|
||||
</div>
|
||||
<a href="/kalendarz/nowe/<?= $date ?>" class="btn btn-light btn-lg">
|
||||
<i class="bi bi-plus-lg me-1"></i>Dodaj wydarzenie
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (empty($events)): ?>
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="bi bi-calendar-x text-muted" style="font-size: 4rem;"></i>
|
||||
<h4 class="mt-3 text-muted">Brak wydarzeń</h4>
|
||||
<p class="text-muted mb-4">Nie masz żadnych wydarzeń zaplanowanych na ten dzień.</p>
|
||||
<a href="/kalendarz/nowe/<?= $date ?>" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg me-1"></i>Dodaj wydarzenie
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row g-3">
|
||||
<?php foreach ($events as $event): ?>
|
||||
<div class="col-12">
|
||||
<div class="card event-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="card-title mb-2">
|
||||
<i class="bi bi-calendar-event text-primary me-2"></i>
|
||||
<?= htmlspecialchars($event['title']) ?>
|
||||
</h5>
|
||||
<?php if (!empty($event['content'])): ?>
|
||||
<p class="card-text text-muted mb-2">
|
||||
<?= nl2br(htmlspecialchars($event['content'])) ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-clock me-1"></i>
|
||||
Dodano: <?= date('d.m.Y H:i', strtotime($event['created_at'])) ?>
|
||||
</small>
|
||||
</div>
|
||||
<div class="event-actions ms-3">
|
||||
<a href="/kalendarz/edytuj/<?= $event['id'] ?>" class="btn btn-sm btn-outline-primary" title="Edytuj">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||
onclick="confirmDelete(<?= $event['id'] ?>, '<?= htmlspecialchars(addslashes($event['title'])) ?>')"
|
||||
title="Usuń">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Modal potwierdzenia usunięcia -->
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header border-0">
|
||||
<div class="text-center w-100">
|
||||
<div class="bg-danger bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 80px; height: 80px;">
|
||||
<i class="bi bi-exclamation-triangle text-danger" style="font-size: 2.5rem;"></i>
|
||||
</div>
|
||||
<h5 class="modal-title">Potwierdzenie usunięcia</h5>
|
||||
</div>
|
||||
<button type="button" class="btn-close position-absolute top-0 end-0 m-3" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p class="mb-1">Czy na pewno chcesz usunąć wydarzenie:</p>
|
||||
<p class="fw-bold text-primary" id="deleteEventTitle"></p>
|
||||
<p class="text-muted small mb-0">Ta operacja jest nieodwracalna.</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center border-0">
|
||||
<button type="button" class="btn btn-secondary px-4" data-bs-dismiss="modal">
|
||||
<i class="bi bi-x-lg me-1"></i>Anuluj
|
||||
</button>
|
||||
<form id="deleteForm" method="POST" class="d-inline">
|
||||
<button type="submit" class="btn btn-danger px-4">
|
||||
<i class="bi bi-trash me-1"></i>Usuń
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.event-card {
|
||||
border-left: 4px solid #667eea;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.event-card:hover {
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function confirmDelete(id, title) {
|
||||
document.getElementById('deleteEventTitle').textContent = '"' + title + '"';
|
||||
document.getElementById('deleteForm').action = '/kalendarz/usun/' + id;
|
||||
new bootstrap.Modal(document.getElementById('deleteModal')).show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout('Wydarzenia - ' . $formattedDate, $content, true, 'kalendarz', 'col-lg-8');
|
||||
72
app/views/calendar/form.php
Normal file
72
app/views/calendar/form.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../layout.php';
|
||||
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-center text-white">
|
||||
<i class="bi bi-<?= $isEdit ? 'pencil-square' : 'calendar-plus' ?> icon-large"></i>
|
||||
<h4 class="mt-2 mb-0"><?= $isEdit ? 'Edytuj wydarzenie' : 'Nowe wydarzenie' ?></h4>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="<?= $isEdit ? '/kalendarz/zapisz/' . $event['id'] : '/kalendarz/dodaj' ?>">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">
|
||||
<i class="bi bi-type me-1"></i>Tytuł wydarzenia
|
||||
</label>
|
||||
<input type="text" class="form-control" id="title" name="title"
|
||||
value="<?= htmlspecialchars($event['title'] ?? '') ?>"
|
||||
placeholder="Np. Spotkanie z klientem" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="event_date" class="form-label">
|
||||
<i class="bi bi-calendar-date me-1"></i>Data
|
||||
</label>
|
||||
<input type="date" class="form-control" id="event_date" name="event_date"
|
||||
value="<?= htmlspecialchars($event['event_date'] ?? $selectedDate) ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="content" class="form-label">
|
||||
<i class="bi bi-text-paragraph me-1"></i>Opis (opcjonalnie)
|
||||
</label>
|
||||
<textarea class="form-control" id="content" name="content" rows="4"
|
||||
placeholder="Dodatkowe informacje o wydarzeniu..."><?= htmlspecialchars($event['content'] ?? '') ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-check-lg me-2"></i><?= $isEdit ? 'Zapisz zmiany' : 'Dodaj wydarzenie' ?>
|
||||
</button>
|
||||
<?php
|
||||
$backUrl = '/kalendarz';
|
||||
if ($isEdit && isset($event['event_date'])) {
|
||||
$backUrl = '/kalendarz/dzien/' . $event['event_date'];
|
||||
} elseif (isset($selectedDate)) {
|
||||
$backUrl = '/kalendarz/dzien/' . $selectedDate;
|
||||
}
|
||||
?>
|
||||
<a href="<?= $backUrl ?>" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-2"></i>Anuluj
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout($isEdit ? 'Edytuj wydarzenie' : 'Nowe wydarzenie', $content, true, 'kalendarz', 'col-md-6');
|
||||
200
app/views/calendar/index.php
Normal file
200
app/views/calendar/index.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../layout.php';
|
||||
|
||||
$monthNames = [
|
||||
1 => 'Styczeń', 2 => 'Luty', 3 => 'Marzec', 4 => 'Kwiecień',
|
||||
5 => 'Maj', 6 => 'Czerwiec', 7 => 'Lipiec', 8 => 'Sierpień',
|
||||
9 => 'Wrzesień', 10 => 'Październik', 11 => 'Listopad', 12 => 'Grudzień'
|
||||
];
|
||||
|
||||
$dayNames = ['Pon', 'Wt', 'Śr', 'Czw', 'Pt', 'Sob', 'Ndz'];
|
||||
|
||||
// Obliczenia kalendarza
|
||||
$firstDayOfMonth = mktime(0, 0, 0, $month, 1, $year);
|
||||
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
$dayOfWeek = date('N', $firstDayOfMonth); // 1 = poniedziałek, 7 = niedziela
|
||||
|
||||
$prevMonth = $month - 1;
|
||||
$prevYear = $year;
|
||||
if ($prevMonth < 1) {
|
||||
$prevMonth = 12;
|
||||
$prevYear--;
|
||||
}
|
||||
|
||||
$nextMonth = $month + 1;
|
||||
$nextYear = $year;
|
||||
if ($nextMonth > 12) {
|
||||
$nextMonth = 1;
|
||||
$nextYear++;
|
||||
}
|
||||
|
||||
$today = date('Y-m-d');
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-check-circle-fill me-2"></i>
|
||||
<?= htmlspecialchars($success) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-transparent">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<a href="/kalendarz?rok=<?= $prevYear ?>&miesiac=<?= $prevMonth ?>" class="btn btn-outline-primary">
|
||||
<i class="bi bi-chevron-left"></i>
|
||||
</a>
|
||||
<h4 class="mb-0">
|
||||
<i class="bi bi-calendar3 me-2"></i>
|
||||
<?= $monthNames[$month] ?> <?= $year ?>
|
||||
</h4>
|
||||
<a href="/kalendarz?rok=<?= $nextYear ?>&miesiac=<?= $nextMonth ?>" class="btn btn-outline-primary">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-bordered mb-0 calendar-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<?php foreach ($dayNames as $dayName): ?>
|
||||
<th class="text-center py-2 bg-light"><?= $dayName ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$currentDay = 1;
|
||||
$started = false;
|
||||
|
||||
for ($week = 0; $week < 6; $week++):
|
||||
if ($currentDay > $daysInMonth) break;
|
||||
?>
|
||||
<tr>
|
||||
<?php for ($dow = 1; $dow <= 7; $dow++):
|
||||
$cellDate = null;
|
||||
$isToday = false;
|
||||
$hasEvents = false;
|
||||
$dayEvents = [];
|
||||
|
||||
if (!$started && $dow == $dayOfWeek) {
|
||||
$started = true;
|
||||
}
|
||||
|
||||
if ($started && $currentDay <= $daysInMonth) {
|
||||
$cellDate = sprintf('%04d-%02d-%02d', $year, $month, $currentDay);
|
||||
$isToday = ($cellDate === $today);
|
||||
$hasEvents = isset($eventsByDate[$cellDate]);
|
||||
if ($hasEvents) {
|
||||
$dayEvents = $eventsByDate[$cellDate];
|
||||
}
|
||||
$displayDay = $currentDay;
|
||||
$currentDay++;
|
||||
} else {
|
||||
$displayDay = null;
|
||||
}
|
||||
?>
|
||||
<td class="calendar-cell <?= $isToday ? 'today' : '' ?> <?= $hasEvents ? 'has-events' : '' ?>"
|
||||
<?php if ($cellDate): ?>onclick="window.location='/kalendarz/dzien/<?= $cellDate ?>'"<?php endif; ?>>
|
||||
<?php if ($displayDay): ?>
|
||||
<div class="day-number <?= $isToday ? 'bg-primary text-white' : '' ?>">
|
||||
<?= $displayDay ?>
|
||||
</div>
|
||||
<?php if ($hasEvents): ?>
|
||||
<div class="day-events">
|
||||
<?php foreach (array_slice($dayEvents, 0, 2) as $evt): ?>
|
||||
<div class="event-dot" title="<?= htmlspecialchars($evt['title']) ?>">
|
||||
<?= htmlspecialchars(mb_substr($evt['title'], 0, 15)) ?><?= mb_strlen($evt['title']) > 15 ? '...' : '' ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php if (count($dayEvents) > 2): ?>
|
||||
<div class="event-more">+<?= count($dayEvents) - 2 ?> więcej</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<?php endfor; ?>
|
||||
</tr>
|
||||
<?php endfor; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<a href="/kalendarz?rok=<?= date('Y') ?>&miesiac=<?= date('m') ?>" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-calendar-check me-1"></i>Dzisiaj
|
||||
</a>
|
||||
<a href="/kalendarz/nowe" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg me-1"></i>Nowe wydarzenie
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.calendar-table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
.calendar-cell {
|
||||
height: 100px;
|
||||
vertical-align: top;
|
||||
padding: 5px !important;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.calendar-cell:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.calendar-cell.today {
|
||||
background-color: #e7f1ff;
|
||||
}
|
||||
.calendar-cell.has-events {
|
||||
background-color: #fff3cd;
|
||||
}
|
||||
.calendar-cell.today.has-events {
|
||||
background-color: #d1e7dd;
|
||||
}
|
||||
.day-number {
|
||||
display: inline-block;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.day-events {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.event-dot {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.event-more {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout('Kalendarz', $content, true, 'kalendarz', 'col-lg-10');
|
||||
179
app/views/dashboard.php
Normal file
179
app/views/dashboard.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/layout.php';
|
||||
require_once __DIR__ . '/../models/Note.php';
|
||||
require_once __DIR__ . '/../models/Event.php';
|
||||
|
||||
$userId = $_SESSION['user_id'] ?? 0;
|
||||
$notesCount = Note::countByUser($userId);
|
||||
$eventsThisWeek = Event::countThisWeek($userId);
|
||||
$upcomingEvents = Event::getThisWeek($userId);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="bg-primary bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center me-4"
|
||||
style="width: 80px; height: 80px;">
|
||||
<i class="bi bi-person-circle text-primary" style="font-size: 2.5rem;"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-1">
|
||||
Witaj, <span class="text-primary"><?= htmlspecialchars($userLogin) ?></span>!
|
||||
</h3>
|
||||
<p class="text-muted mb-0">
|
||||
Pomyślnie zalogowano z uwierzytelnianiem dwuskładnikowym.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<div class="bg-success bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 60px; height: 60px;">
|
||||
<i class="bi bi-shield-fill-check text-success fs-3"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Status 2FA</h5>
|
||||
<p class="text-success mb-0 fw-bold">Aktywne</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<div class="bg-primary bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 60px; height: 60px;">
|
||||
<i class="bi bi-journal-text text-primary fs-3"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Notatki</h5>
|
||||
<p class="text-primary mb-0 fw-bold fs-4"><?= $notesCount ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<div class="bg-warning bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 60px; height: 60px;">
|
||||
<i class="bi bi-calendar-event text-warning fs-3"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Ten tydzień</h5>
|
||||
<p class="text-warning mb-0 fw-bold fs-4"><?= $eventsThisWeek ?> <?= $eventsThisWeek == 1 ? 'wydarzenie' : ($eventsThisWeek >= 2 && $eventsThisWeek <= 4 ? 'wydarzenia' : 'wydarzeń') ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<div class="bg-info bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 60px; height: 60px;">
|
||||
<i class="bi bi-clock-fill text-info fs-3"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Sesja</h5>
|
||||
<p class="text-info mb-0 fw-bold">Aktywna</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-transparent">
|
||||
<h5 class="mb-0"><i class="bi bi-lightning-fill text-warning me-2"></i>Szybkie akcje</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/notatnik/nowa" class="btn btn-outline-primary">
|
||||
<i class="bi bi-plus-lg me-2"></i>Nowa notatka
|
||||
</a>
|
||||
<a href="/kalendarz/nowe" class="btn btn-outline-warning">
|
||||
<i class="bi bi-calendar-plus me-2"></i>Nowe wydarzenie
|
||||
</a>
|
||||
<a href="/kalendarz" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-calendar3 me-2"></i>Otwórz kalendarz
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-transparent d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-calendar-week text-primary me-2"></i>Wydarzenia tego tygodnia</h5>
|
||||
<a href="/kalendarz" class="btn btn-sm btn-outline-primary">Zobacz wszystkie</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($upcomingEvents)): ?>
|
||||
<p class="text-muted text-center mb-0 py-3">
|
||||
<i class="bi bi-calendar-x d-block mb-2" style="font-size: 2rem;"></i>
|
||||
Brak wydarzeń w tym tygodniu
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<?php foreach (array_slice($upcomingEvents, 0, 4) as $event): ?>
|
||||
<li class="d-flex align-items-center mb-2 pb-2 border-bottom">
|
||||
<div class="bg-primary bg-opacity-10 rounded p-2 me-3">
|
||||
<i class="bi bi-calendar-event text-primary"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-semibold"><?= htmlspecialchars($event['title']) ?></div>
|
||||
<small class="text-muted">
|
||||
<?= date('d.m (D)', strtotime($event['event_date'])) ?>
|
||||
</small>
|
||||
</div>
|
||||
<a href="/kalendarz/dzien/<?= $event['event_date'] ?>" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<?php if (count($upcomingEvents) > 4): ?>
|
||||
<li class="text-center text-muted small pt-2">
|
||||
+ <?= count($upcomingEvents) - 4 ?> więcej wydarzeń
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-transparent">
|
||||
<h5 class="mb-0"><i class="bi bi-info-circle-fill text-info me-2"></i>Informacje o sesji</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<i class="bi bi-calendar3 text-muted me-2"></i>
|
||||
<strong>Data logowania:</strong> <?= date('d.m.Y H:i') ?>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<i class="bi bi-shield-check text-muted me-2"></i>
|
||||
<strong>Metoda 2FA:</strong> Kod Email
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<i class="bi bi-lock text-muted me-2"></i>
|
||||
<strong>Połączenie:</strong> Szyfrowane
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout('Pulpit', $content, true, 'pulpit', 'col-lg-10');
|
||||
203
app/views/layout.php
Normal file
203
app/views/layout.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
function renderLayout(string $title, string $content, bool $showMenu = false, string $activeMenu = '', string $containerSize = 'col-md-5'): void
|
||||
{
|
||||
$userLogin = $_SESSION['user_login'] ?? '';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= htmlspecialchars($title) ?> - System 2FA</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
body.with-menu {
|
||||
display: block;
|
||||
padding-top: 70px;
|
||||
}
|
||||
body:not(.with-menu) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.navbar-custom {
|
||||
background: rgba(255,255,255,0.95) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.nav-link {
|
||||
color: #495057 !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 10px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.nav-link:hover, .nav-link.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white !important;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
}
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 15px 15px 0 0 !important;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 25px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
|
||||
background: linear-gradient(135deg, #5a6fd6 0%, #6a4190 100%);
|
||||
}
|
||||
.form-control, .form-select {
|
||||
border-radius: 10px;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 2px solid #e9ecef;
|
||||
}
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||
}
|
||||
.icon-large {
|
||||
font-size: 3rem;
|
||||
}
|
||||
.code-display {
|
||||
background: #f8f9fa;
|
||||
border: 2px dashed #667eea;
|
||||
border-radius: 10px;
|
||||
padding: 1rem;
|
||||
font-family: monospace;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.note-card {
|
||||
border-radius: 15px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
height: 100%;
|
||||
}
|
||||
.note-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
||||
}
|
||||
.note-card .card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.note-content {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.note-actions {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.note-card:hover .note-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
.color-option {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: 3px solid transparent;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.color-option:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.color-option.selected {
|
||||
border-color: #333;
|
||||
}
|
||||
.modal-confirm .modal-header {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.modal-confirm .modal-footer {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="<?= $showMenu ? 'with-menu' : '' ?>">
|
||||
<?php if ($showMenu): ?>
|
||||
<nav class="navbar navbar-expand-lg navbar-custom fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/panel">
|
||||
<i class="bi bi-shield-lock-fill me-2"></i>System 2FA
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $activeMenu === 'pulpit' ? 'active' : '' ?>" href="/panel">
|
||||
<i class="bi bi-house-fill me-1"></i>Pulpit
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $activeMenu === 'notatnik' ? 'active' : '' ?>" href="/notatnik">
|
||||
<i class="bi bi-journal-text me-1"></i>Notatnik
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $activeMenu === 'kalendarz' ? 'active' : '' ?>" href="/kalendarz">
|
||||
<i class="bi bi-calendar3 me-1"></i>Kalendarz
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle me-1"></i><?= htmlspecialchars($userLogin) ?>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item text-danger" href="/wyloguj-sie">
|
||||
<i class="bi bi-box-arrow-right me-2"></i>Wyloguj się
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="container <?= $showMenu ? 'py-4' : '' ?>">
|
||||
<div class="row justify-content-center">
|
||||
<div class="<?= $containerSize ?>">
|
||||
<?= $content ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
53
app/views/login.php
Normal file
53
app/views/login.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/layout.php';
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-center text-white">
|
||||
<i class="bi bi-shield-lock icon-large"></i>
|
||||
<h4 class="mt-2 mb-0">Logowanie</h4>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="/zaloguj">
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">
|
||||
<i class="bi bi-person-fill me-1"></i>Login
|
||||
</label>
|
||||
<input type="text" class="form-control" id="login" name="login"
|
||||
placeholder="Wprowadź login" required autofocus>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="password" class="form-label">
|
||||
<i class="bi bi-key-fill me-1"></i>Hasło
|
||||
</label>
|
||||
<input type="password" class="form-control" id="password" name="password"
|
||||
placeholder="Wprowadź hasło" required>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-box-arrow-in-right me-2"></i>Zaloguj się
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center text-muted bg-transparent border-0 pb-4">
|
||||
<small>
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
System zabezpieczony uwierzytelnianiem dwuskładnikowym
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout('Logowanie', $content);
|
||||
91
app/views/notes/form.php
Normal file
91
app/views/notes/form.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../layout.php';
|
||||
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
$colors = [
|
||||
'primary' => '#667eea',
|
||||
'success' => '#28a745',
|
||||
'danger' => '#dc3545',
|
||||
'warning' => '#ffc107',
|
||||
'info' => '#17a2b8',
|
||||
'secondary' => '#6c757d'
|
||||
];
|
||||
|
||||
$currentColor = $note['color'] ?? 'primary';
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-center text-white">
|
||||
<i class="bi bi-<?= $isEdit ? 'pencil-square' : 'plus-circle' ?> icon-large"></i>
|
||||
<h4 class="mt-2 mb-0"><?= $isEdit ? 'Edytuj notatkę' : 'Nowa notatka' ?></h4>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="<?= $isEdit ? '/notatnik/zapisz/' . $note['id'] : '/notatnik/dodaj' ?>">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">
|
||||
<i class="bi bi-type me-1"></i>Tytuł
|
||||
</label>
|
||||
<input type="text" class="form-control" id="title" name="title"
|
||||
value="<?= htmlspecialchars($note['title'] ?? '') ?>"
|
||||
placeholder="Wprowadź tytuł notatki" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">
|
||||
<i class="bi bi-text-paragraph me-1"></i>Treść
|
||||
</label>
|
||||
<textarea class="form-control" id="content" name="content" rows="6"
|
||||
placeholder="Wprowadź treść notatki..."><?= htmlspecialchars($note['content'] ?? '') ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-palette me-1"></i>Kolor
|
||||
</label>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<?php foreach ($colors as $name => $hex): ?>
|
||||
<div class="color-option <?= $currentColor === $name ? 'selected' : '' ?>"
|
||||
style="background-color: <?= $hex ?>;"
|
||||
onclick="selectColor('<?= $name ?>', this)"
|
||||
title="<?= ucfirst($name) ?>">
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<input type="hidden" name="color" id="colorInput" value="<?= htmlspecialchars($currentColor) ?>">
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-check-lg me-2"></i><?= $isEdit ? 'Zapisz zmiany' : 'Dodaj notatkę' ?>
|
||||
</button>
|
||||
<a href="/notatnik" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-2"></i>Powrót do listy
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function selectColor(color, element) {
|
||||
document.querySelectorAll('.color-option').forEach(el => el.classList.remove('selected'));
|
||||
element.classList.add('selected');
|
||||
document.getElementById('colorInput').value = color;
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout($isEdit ? 'Edytuj notatkę' : 'Nowa notatka', $content, true, 'notatnik', 'col-md-6');
|
||||
120
app/views/notes/index.php
Normal file
120
app/views/notes/index.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../layout.php';
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-check-circle-fill me-2"></i>
|
||||
<?= htmlspecialchars($success) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="text-white mb-0">
|
||||
<i class="bi bi-journal-text me-2"></i>Moje notatki
|
||||
</h2>
|
||||
<a href="/notatnik/nowa" class="btn btn-light btn-lg">
|
||||
<i class="bi bi-plus-lg me-1"></i>Nowa notatka
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (empty($notes)): ?>
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="bi bi-journal-x text-muted" style="font-size: 4rem;"></i>
|
||||
<h4 class="mt-3 text-muted">Brak notatek</h4>
|
||||
<p class="text-muted mb-4">Nie masz jeszcze żadnych notatek. Utwórz pierwszą!</p>
|
||||
<a href="/notatnik/nowa" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg me-1"></i>Utwórz notatkę
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row g-4">
|
||||
<?php foreach ($notes as $note): ?>
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card note-card border-top border-4 border-<?= htmlspecialchars($note['color']) ?>">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<h5 class="card-title mb-0"><?= htmlspecialchars($note['title']) ?></h5>
|
||||
<div class="note-actions">
|
||||
<a href="/notatnik/edytuj/<?= $note['id'] ?>" class="btn btn-sm btn-outline-primary" title="Edytuj">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||
onclick="confirmDelete(<?= $note['id'] ?>, '<?= htmlspecialchars(addslashes($note['title'])) ?>')"
|
||||
title="Usuń">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="note-content text-muted small">
|
||||
<?= nl2br(htmlspecialchars($note['content'] ?: 'Brak treści')) ?>
|
||||
</p>
|
||||
<div class="mt-auto pt-2 border-top">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-clock me-1"></i>
|
||||
<?= date('d.m.Y H:i', strtotime($note['updated_at'])) ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Modal potwierdzenia usunięcia -->
|
||||
<div class="modal fade modal-confirm" id="deleteModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="text-center w-100">
|
||||
<div class="bg-danger bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 80px; height: 80px;">
|
||||
<i class="bi bi-exclamation-triangle text-danger" style="font-size: 2.5rem;"></i>
|
||||
</div>
|
||||
<h5 class="modal-title">Potwierdzenie usunięcia</h5>
|
||||
</div>
|
||||
<button type="button" class="btn-close position-absolute top-0 end-0 m-3" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p class="mb-1">Czy na pewno chcesz usunąć notatkę:</p>
|
||||
<p class="fw-bold text-primary" id="deleteNoteTitle"></p>
|
||||
<p class="text-muted small mb-0">Ta operacja jest nieodwracalna.</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="button" class="btn btn-secondary px-4" data-bs-dismiss="modal">
|
||||
<i class="bi bi-x-lg me-1"></i>Anuluj
|
||||
</button>
|
||||
<form id="deleteForm" method="POST" class="d-inline">
|
||||
<button type="submit" class="btn btn-danger px-4">
|
||||
<i class="bi bi-trash me-1"></i>Usuń
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function confirmDelete(id, title) {
|
||||
document.getElementById('deleteNoteTitle').textContent = '"' + title + '"';
|
||||
document.getElementById('deleteForm').action = '/notatnik/usun/' + id;
|
||||
new bootstrap.Modal(document.getElementById('deleteModal')).show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout('Notatnik', $content, true, 'notatnik', 'col-12');
|
||||
74
app/views/verify.php
Normal file
74
app/views/verify.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/layout.php';
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-center text-white">
|
||||
<i class="bi bi-phone icon-large"></i>
|
||||
<h4 class="mt-2 mb-0">Weryfikacja dwuskładnikowa</h4>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<?= htmlspecialchars($error) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-envelope-fill me-2"></i>
|
||||
Kod weryfikacyjny został wysłany na adres e-mail powiązany z kontem
|
||||
<strong><?= htmlspecialchars($userLogin) ?></strong>.
|
||||
</div>
|
||||
|
||||
<?php if (isset($simulatedCode)): ?>
|
||||
<div class="mb-4">
|
||||
<label class="form-label text-muted">
|
||||
<i class="bi bi-bug-fill me-1"></i>Kod testowy (symulacja):
|
||||
</label>
|
||||
<div class="code-display">
|
||||
<?= htmlspecialchars($simulatedCode) ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="/zweryfikuj">
|
||||
<div class="mb-4">
|
||||
<label for="code" class="form-label">
|
||||
<i class="bi bi-123 me-1"></i>Kod weryfikacyjny
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-lg text-center"
|
||||
id="code" name="code" maxlength="6"
|
||||
placeholder="000000" required autofocus
|
||||
style="letter-spacing: 0.5rem; font-size: 1.5rem;">
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-check-circle me-2"></i>Zweryfikuj
|
||||
</button>
|
||||
<a href="/wyloguj-sie" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-2"></i>Anuluj i wróć
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center text-muted bg-transparent border-0 pb-4">
|
||||
<small>
|
||||
<i class="bi bi-clock me-1"></i>
|
||||
Kod jest ważny przez 10 minut
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('code').addEventListener('input', function(e) {
|
||||
this.value = this.value.replace(/[^0-9]/g, '');
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
renderLayout('Weryfikacja', $content);
|
||||
Reference in New Issue
Block a user