Online Library Management System IN HTML CSS AND JAVASCRIPT WITH PHP AND MY SQL

Online Library Management System - Complete Project

Introduction to the Project

The Online Library Management System is a comprehensive web-based application designed to automate and streamline the operations of a library. This system provides a complete digital solution for managing books, members, borrowing transactions, and library resources efficiently. With role-based access control, the application supports three user types: Admin, Librarians, and Members/Students.

The platform eliminates manual record-keeping, reduces paperwork, and provides real-time information about book availability, due dates, and fines. It features automated notifications, detailed reports, and a user-friendly interface that makes library management effortless. Whether for school libraries, college libraries, or public libraries, this system offers all the essential features needed for modern library management.

Key Features

Admin Features

  • Dashboard Overview: View library statistics, active members, and transaction summaries
  • Staff Management: Add, edit, or remove librarians and staff members
  • System Configuration: Configure library settings, fine rates, and borrowing rules
  • Category Management: Create and manage book categories and subjects
  • Report Generation: Generate detailed reports on library usage and inventory
  • Backup & Restore: Database backup and restoration capabilities
  • Audit Logs: Track all system activities and changes

Librarian Features

  • Book Management: Add, edit, delete, and search books in the catalog
  • Member Management: Register new members, manage member profiles
  • Circulation Management: Issue books, return books, and renewals
  • Fine Management: Calculate and collect fines for overdue books
  • Reservation Management: Handle book reservations and holds
  • Overdue Notifications: Send reminders for overdue books
  • Inventory Tracking: Monitor book availability and condition
  • Daily Transactions: View and manage all daily library transactions

Member Features

  • Online Catalog: Search and browse available books
  • Book Reservations: Reserve books that are currently issued
  • Borrowing History: View personal borrowing history and current loans
  • Due Date Tracking: See due dates and return reminders
  • Online Renewals: Renew borrowed books online (if eligible)
  • Fine Payment: View and pay fines online
  • Profile Management: Update personal information and preferences
  • Wishlist: Create and manage personal reading wishlist

Technology Stack

  • Frontend: HTML5, CSS3, JavaScript (Vanilla), Bootstrap 5 for responsive design
  • Backend: PHP (Core PHP, no frameworks)
  • Database: MySQL
  • Additional Libraries:
  • Bootstrap 5 for UI components
  • Font Awesome for icons
  • Chart.js for analytics
  • DataTables for advanced table features
  • jQuery for AJAX operations
  • PHPMailer for email notifications

Project File Structure

online-library-system/
│
├── assets/
│   ├── css/
│   │   ├── style.css
│   │   ├── admin.css
│   │   └── responsive.css
│   ├── js/
│   │   ├── main.js
│   │   ├── validation.js
│   │   ├── datatable.js
│   │   └── chart.js
│   ├── images/
│   │   ├── books/
│   │   ├── profiles/
│   │   └── default-cover.jpg
│   └── plugins/
│       ├── bootstrap/
│       ├── datatables/
│       └── chart.js/
│
├── includes/
│   ├── config.php
│   ├── functions.php
│   ├── auth.php
│   ├── database.php
│   ├── mailer.php
│   └── validation.php
│
├── admin/
│   ├── dashboard.php
│   ├── manage_librarians.php
│   ├── manage_categories.php
│   ├── manage_publishers.php
│   ├── system_settings.php
│   ├── view_reports.php
│   ├── backup_database.php
│   └── audit_logs.php
│
├── librarian/
│   ├── dashboard.php
│   ├── manage_books.php
│   ├── add_book.php
│   ├── edit_book.php
│   ├── manage_members.php
│   ├── add_member.php
│   ├── issue_book.php
│   ├── return_book.php
│   ├── view_transactions.php
│   ├── manage_fines.php
│   ├── overdue_books.php
│   ├── reservations.php
│   └── reports.php
│
├── member/
│   ├── dashboard.php
│   ├── search_books.php
│   ├── book_details.php
│   ├── my_books.php
│   ├── borrow_history.php
│   ├── reservations.php
│   ├── fines.php
│   ├── wishlist.php
│   ├── profile.php
│   └── change_password.php
│
├── api/
│   ├── search_books.php
│   ├── renew_book.php
│   ├── reserve_book.php
│   ├── pay_fine.php
│   └── check_availability.php
│
├── index.php
├── login.php
├── register.php
├── forgot_password.php
├── reset_password.php
├── logout.php
└── sql/
└── database.sql

Database Schema

File: sql/database.sql

-- Create Database
CREATE DATABASE IF NOT EXISTS `library_management_system`;
USE `library_management_system`;
-- Users Table (for all system users)
CREATE TABLE `users` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`member_id` VARCHAR(20) UNIQUE,
`username` VARCHAR(50) UNIQUE NOT NULL,
`email` VARCHAR(100) UNIQUE NOT NULL,
`password` VARCHAR(255) NOT NULL,
`full_name` VARCHAR(100) NOT NULL,
`phone` VARCHAR(15),
`address` TEXT,
`profile_picture` VARCHAR(255) DEFAULT 'default.jpg',
`role` ENUM('admin', 'librarian', 'member') DEFAULT 'member',
`status` ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
`membership_type` ENUM('student', 'faculty', 'staff', 'public') DEFAULT 'student',
`membership_expiry` DATE,
`max_books_allowed` INT DEFAULT 5,
`fine_due` DECIMAL(10,2) DEFAULT 0.00,
`email_verified` BOOLEAN DEFAULT FALSE,
`verification_token` VARCHAR(255),
`reset_token` VARCHAR(255),
`reset_expires` DATETIME,
`last_login` DATETIME,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- Categories Table
CREATE TABLE `categories` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(100) NOT NULL,
`description` TEXT,
`parent_id` INT(11) DEFAULT NULL,
`status` BOOLEAN DEFAULT TRUE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`parent_id`) REFERENCES `categories`(`id`)
);
-- Publishers Table
CREATE TABLE `publishers` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`publisher_name` VARCHAR(255) NOT NULL,
`address` TEXT,
`phone` VARCHAR(15),
`email` VARCHAR(100),
`website` VARCHAR(255),
`status` BOOLEAN DEFAULT TRUE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- Authors Table
CREATE TABLE `authors` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`author_name` VARCHAR(255) NOT NULL,
`biography` TEXT,
`birth_date` DATE,
`death_date` DATE,
`nationality` VARCHAR(100),
`status` BOOLEAN DEFAULT TRUE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- Books Table
CREATE TABLE `books` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`isbn` VARCHAR(20) UNIQUE,
`title` VARCHAR(255) NOT NULL,
`subtitle` VARCHAR(255),
`edition` VARCHAR(50),
`publication_year` YEAR,
`publisher_id` INT(11),
`category_id` INT(11),
`language` VARCHAR(50) DEFAULT 'English',
`pages` INT,
`shelf_location` VARCHAR(50),
`total_copies` INT DEFAULT 1,
`available_copies` INT DEFAULT 1,
`cover_image` VARCHAR(255) DEFAULT 'default-cover.jpg',
`description` TEXT,
`tags` TEXT,
`status` ENUM('available', 'reference_only', 'damaged', 'lost', 'processing') DEFAULT 'available',
`added_by` INT(11),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`publisher_id`) REFERENCES `publishers`(`id`),
FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`),
FOREIGN KEY (`added_by`) REFERENCES `users`(`id`)
);
-- Book Authors Junction Table
CREATE TABLE `book_authors` (
`book_id` INT(11) NOT NULL,
`author_id` INT(11) NOT NULL,
PRIMARY KEY (`book_id`, `author_id`),
FOREIGN KEY (`book_id`) REFERENCES `books`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`author_id`) REFERENCES `authors`(`id`) ON DELETE CASCADE
);
-- Transactions Table (for book issues/returns)
CREATE TABLE `transactions` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`transaction_id` VARCHAR(20) UNIQUE NOT NULL,
`book_id` INT(11) NOT NULL,
`member_id` INT(11) NOT NULL,
`issued_by` INT(11) NOT NULL,
`issue_date` DATETIME NOT NULL,
`due_date` DATETIME NOT NULL,
`return_date` DATETIME,
`status` ENUM('issued', 'returned', 'overdue', 'renewed') DEFAULT 'issued',
`renewal_count` INT DEFAULT 0,
`fine_amount` DECIMAL(10,2) DEFAULT 0.00,
`fine_paid` BOOLEAN DEFAULT FALSE,
`remarks` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`book_id`) REFERENCES `books`(`id`),
FOREIGN KEY (`member_id`) REFERENCES `users`(`id`),
FOREIGN KEY (`issued_by`) REFERENCES `users`(`id`)
);
-- Reservations Table
CREATE TABLE `reservations` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`book_id` INT(11) NOT NULL,
`member_id` INT(11) NOT NULL,
`reservation_date` DATETIME NOT NULL,
`expiry_date` DATETIME NOT NULL,
`status` ENUM('pending', 'available', 'cancelled', 'completed') DEFAULT 'pending',
`notification_sent` BOOLEAN DEFAULT FALSE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`book_id`) REFERENCES `books`(`id`),
FOREIGN KEY (`member_id`) REFERENCES `users`(`id`)
);
-- Fines Table
CREATE TABLE `fines` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`transaction_id` INT(11) NOT NULL,
`member_id` INT(11) NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`reason` VARCHAR(255),
`status` ENUM('pending', 'paid', 'waived') DEFAULT 'pending',
`payment_date` DATETIME,
`payment_method` VARCHAR(50),
`payment_reference` VARCHAR(100),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`transaction_id`) REFERENCES `transactions`(`id`),
FOREIGN KEY (`member_id`) REFERENCES `users`(`id`)
);
-- Notifications Table
CREATE TABLE `notifications` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL,
`type` ENUM('due_reminder', 'overdue', 'reservation_available', 'fine', 'general') NOT NULL,
`title` VARCHAR(255) NOT NULL,
`message` TEXT NOT NULL,
`is_read` BOOLEAN DEFAULT FALSE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
);
-- Activity Logs Table
CREATE TABLE `activity_logs` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11),
`action` VARCHAR(100) NOT NULL,
`description` TEXT,
`ip_address` VARCHAR(45),
`user_agent` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
);
-- Settings Table
CREATE TABLE `settings` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`setting_key` VARCHAR(100) UNIQUE NOT NULL,
`setting_value` TEXT,
`description` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- Insert Default Settings
INSERT INTO `settings` (`setting_key`, `setting_value`, `description`) VALUES
('library_name', 'Central Library', 'Name of the library'),
('library_address', '123 Library Street, Book City', 'Physical address of the library'),
('library_phone', '+1-555-123-4567', 'Contact phone number'),
('library_email', '[email protected]', 'Contact email address'),
('fine_per_day', '1.00', 'Fine amount per day for overdue books'),
('max_books_per_member', '5', 'Maximum books a member can borrow'),
('loan_period_days', '14', 'Default loan period in days'),
('renewal_limit', '2', 'Maximum number of renewals allowed'),
('enable_reservations', '1', 'Enable book reservations (1=Yes, 0=No)'),
('enable_fines', '1', 'Enable fine calculation (1=Yes, 0=No)');
-- Insert Default Admin
INSERT INTO `users` (`username`, `email`, `password`, `full_name`, `role`, `status`, `email_verified`) 
VALUES ('admin', '[email protected]', '$2y$10$YourHashedPasswordHere', 'System Administrator', 'admin', 'active', TRUE);
-- Insert Sample Categories
INSERT INTO `categories` (`category_name`, `description`) VALUES
('Fiction', 'Fictional literature including novels, short stories'),
('Non-Fiction', 'Informational and factual books'),
('Science', 'Scientific books and publications'),
('History', 'Historical books and documents'),
('Biography', 'Biographies and autobiographies'),
('Children', 'Books for children and young readers'),
('Reference', 'Dictionaries, encyclopedias, and reference materials'),
('Textbooks', 'Academic and educational textbooks');

Core PHP Files

Database Configuration

File: includes/config.php

<?php
// Start session if not already started
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Database Configuration
define('DB_HOST', 'localhost');
define('DB_NAME', 'library_management_system');
define('DB_USER', 'root');
define('DB_PASS', '');
define('BASE_URL', 'http://localhost/online-library-system/');
define('SITE_NAME', 'Online Library Management System');
// Library Settings (will be loaded from database)
define('FINE_PER_DAY', 1.00); // Default fine per day in dollars
define('MAX_BOOKS_PER_MEMBER', 5);
define('LOAN_PERIOD_DAYS', 14);
define('RENEWAL_LIMIT', 2);
// File Upload Settings
define('MAX_FILE_SIZE', 5 * 1024 * 1024); // 5MB
define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif', 'pdf']);
// Date/Time Settings
date_default_timezone_set('America/New_York');
try {
$pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4", DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
// Load settings from database
$stmt = $pdo->query("SELECT setting_key, setting_value FROM settings");
while ($row = $stmt->fetch()) {
define(strtoupper($row['setting_key']), $row['setting_value']);
}
} catch(PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
// Include required files
require_once 'functions.php';
require_once 'auth.php';
?>

Helper Functions

File: includes/functions.php

<?php
/**
* Sanitize input data
*/
function sanitize($input) {
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}
/**
* Generate a unique transaction ID
*/
function generateTransactionId() {
return 'TXN' . date('Ymd') . strtoupper(uniqid());
}
/**
* Generate member ID
*/
function generateMemberId() {
return 'MEM' . date('Y') . str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT);
}
/**
* Calculate due date based on loan period
*/
function calculateDueDate($issue_date = null) {
if (!$issue_date) {
$issue_date = date('Y-m-d H:i:s');
}
return date('Y-m-d H:i:s', strtotime($issue_date . ' + ' . LOAN_PERIOD_DAYS . ' days'));
}
/**
* Calculate fine for overdue books
*/
function calculateFine($due_date, $return_date = null) {
if (!$return_date) {
$return_date = date('Y-m-d H:i:s');
}
$due = strtotime($due_date);
$return = strtotime($return_date);
if ($return <= $due) {
return 0;
}
$days_overdue = ceil(($return - $due) / (60 * 60 * 24));
return $days_overdue * FINE_PER_DAY;
}
/**
* Check if member can borrow more books
*/
function canBorrowMore($pdo, $member_id) {
// Get current borrowed count
$stmt = $pdo->prepare("
SELECT COUNT(*) FROM transactions 
WHERE member_id = ? AND status IN ('issued', 'overdue')
");
$stmt->execute([$member_id]);
$borrowed = $stmt->fetchColumn();
// Get member's max allowed
$stmt = $pdo->prepare("SELECT max_books_allowed FROM users WHERE id = ?");
$stmt->execute([$member_id]);
$max_allowed = $stmt->fetchColumn();
return $borrowed < $max_allowed;
}
/**
* Get book availability status
*/
function getBookStatus($pdo, $book_id) {
$stmt = $pdo->prepare("
SELECT b.*, 
(SELECT COUNT(*) FROM transactions WHERE book_id = b.id AND status IN ('issued', 'overdue')) as issued_count
FROM books b
WHERE b.id = ?
");
$stmt->execute([$book_id]);
$book = $stmt->fetch();
if (!$book) {
return ['status' => 'not_found', 'available' => 0];
}
$available = $book['total_copies'] - $book['issued_count'];
if ($available > 0) {
return ['status' => 'available', 'available' => $available];
} else {
return ['status' => 'unavailable', 'available' => 0];
}
}
/**
* Log user activity
*/
function logActivity($pdo, $user_id, $action, $description = '') {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$stmt = $pdo->prepare("
INSERT INTO activity_logs (user_id, action, description, ip_address, user_agent) 
VALUES (?, ?, ?, ?, ?)
");
return $stmt->execute([$user_id, $action, $description, $ip, $user_agent]);
}
/**
* Send notification to user
*/
function sendNotification($pdo, $user_id, $type, $title, $message) {
$stmt = $pdo->prepare("
INSERT INTO notifications (user_id, type, title, message) 
VALUES (?, ?, ?, ?)
");
return $stmt->execute([$user_id, $type, $title, $message]);
}
/**
* Format date for display
*/
function formatDate($date, $format = 'M d, Y') {
if (!$date || $date == '0000-00-00 00:00:00') {
return 'N/A';
}
return date($format, strtotime($date));
}
/**
* Get time ago string
*/
function timeAgo($datetime) {
$time = strtotime($datetime);
$now = time();
$diff = $now - $time;
if ($diff < 60) {
return $diff . ' seconds ago';
} elseif ($diff < 3600) {
return floor($diff / 60) . ' minutes ago';
} elseif ($diff < 86400) {
return floor($diff / 3600) . ' hours ago';
} elseif ($diff < 2592000) {
return floor($diff / 86400) . ' days ago';
} else {
return formatDate($datetime);
}
}
/**
* Get book cover URL
*/
function getBookCover($cover_image) {
if ($cover_image && file_exists('../assets/images/books/' . $cover_image)) {
return BASE_URL . 'assets/images/books/' . $cover_image;
}
return BASE_URL . 'assets/images/default-cover.jpg';
}
/**
* Get user profile picture URL
*/
function getProfilePicture($profile_picture) {
if ($profile_picture && file_exists('../assets/images/profiles/' . $profile_picture)) {
return BASE_URL . 'assets/images/profiles/' . $profile_picture;
}
return BASE_URL . 'assets/images/default-profile.png';
}
/**
* Generate pagination links
*/
function paginate($current_page, $total_pages, $url) {
if ($total_pages <= 1) {
return '';
}
$html = '<nav class="pagination"><ul>';
// Previous button
if ($current_page > 1) {
$html .= '<li><a href="' . $url . '?page=' . ($current_page - 1) . '">&laquo; Previous</a></li>';
}
// Page numbers
for ($i = 1; $i <= $total_pages; $i++) {
if ($i == $current_page) {
$html .= '<li class="active"><span>' . $i . '</span></li>';
} else {
$html .= '<li><a href="' . $url . '?page=' . $i . '">' . $i . '</a></li>';
}
}
// Next button
if ($current_page < $total_pages) {
$html .= '<li><a href="' . $url . '?page=' . ($current_page + 1) . '">Next &raquo;</a></li>';
}
$html .= '</ul></nav>';
return $html;
}
/**
* Check for overdue books and create fines
*/
function checkOverdueBooks($pdo) {
$stmt = $pdo->query("
SELECT t.*, u.fine_due 
FROM transactions t
JOIN users u ON t.member_id = u.id
WHERE t.status IN ('issued', 'overdue') 
AND t.due_date < NOW()
");
$overdue = $stmt->fetchAll();
foreach ($overdue as $transaction) {
// Update transaction status to overdue
$update = $pdo->prepare("UPDATE transactions SET status = 'overdue' WHERE id = ?");
$update->execute([$transaction['id']]);
// Calculate fine
$fine_amount = calculateFine($transaction['due_date']);
// Check if fine already exists
$check = $pdo->prepare("SELECT id FROM fines WHERE transaction_id = ?");
$check->execute([$transaction['id']]);
if (!$check->fetch()) {
// Create fine record
$fine = $pdo->prepare("
INSERT INTO fines (transaction_id, member_id, amount, reason, status) 
VALUES (?, ?, ?, 'Overdue book fine', 'pending')
");
$fine->execute([$transaction['id'], $transaction['member_id'], $fine_amount]);
// Update member's total fine due
$member = $pdo->prepare("UPDATE users SET fine_due = fine_due + ? WHERE id = ?");
$member->execute([$fine_amount, $transaction['member_id']]);
// Send notification
sendNotification(
$pdo, 
$transaction['member_id'], 
'overdue', 
'Book Overdue Notice', 
"Your borrowed book (ID: {$transaction['transaction_id']}) is overdue. Fine: $$fine_amount"
);
}
}
}
/**
* Send due date reminders
*/
function sendDueReminders($pdo) {
$reminder_days = [1, 3]; // Send reminder 1 day and 3 days before due
foreach ($reminder_days as $days) {
$target_date = date('Y-m-d H:i:s', strtotime('+' . $days . ' days'));
$date_start = date('Y-m-d 00:00:00', strtotime($target_date));
$date_end = date('Y-m-d 23:59:59', strtotime($target_date));
$stmt = $pdo->prepare("
SELECT t.*, u.email, u.full_name, b.title 
FROM transactions t
JOIN users u ON t.member_id = u.id
JOIN books b ON t.book_id = b.id
WHERE t.status = 'issued' 
AND t.due_date BETWEEN ? AND ?
");
$stmt->execute([$date_start, $date_end]);
$reminders = $stmt->fetchAll();
foreach ($reminders as $reminder) {
sendNotification(
$pdo,
$reminder['member_id'],
'due_reminder',
'Book Due Reminder',
"Your borrowed book '{$reminder['title']}' is due in {$days} days on " . formatDate($reminder['due_date'])
);
}
}
}
?>

Authentication Functions

File: includes/auth.php

<?php
/**
* Check if user is logged in
*/
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
/**
* Check if user is admin
*/
function isAdmin() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] == 'admin';
}
/**
* Check if user is librarian
*/
function isLibrarian() {
return isset($_SESSION['user_role']) && ($_SESSION['user_role'] == 'librarian' || $_SESSION['user_role'] == 'admin');
}
/**
* Check if user is member
*/
function isMember() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] == 'member';
}
/**
* Redirect to a URL
*/
function redirect($url) {
header("Location: " . BASE_URL . $url);
exit();
}
/**
* Require login to access page
*/
function requireLogin() {
if (!isLoggedIn()) {
$_SESSION['error'] = 'Please login to access this page.';
redirect('login.php');
}
}
/**
* Require admin access
*/
function requireAdmin() {
requireLogin();
if (!isAdmin()) {
$_SESSION['error'] = 'Access denied. Admin privileges required.';
redirect('index.php');
}
}
/**
* Require librarian access
*/
function requireLibrarian() {
requireLogin();
if (!isLibrarian()) {
$_SESSION['error'] = 'Access denied. Librarian privileges required.';
redirect('index.php');
}
}
/**
* Require member access
*/
function requireMember() {
requireLogin();
if (!isMember()) {
$_SESSION['error'] = 'Access denied. Member privileges required.';
redirect('index.php');
}
}
/**
* Check if member has active fines
*/
function hasActiveFines($pdo, $member_id) {
$stmt = $pdo->prepare("SELECT fine_due FROM users WHERE id = ?");
$stmt->execute([$member_id]);
$fine = $stmt->fetchColumn();
return $fine > 0;
}
/**
* Check if member can borrow books
*/
function canBorrowBooks($pdo, $member_id) {
// Check if member has overdue fines
if (hasActiveFines($pdo, $member_id)) {
return false;
}
// Check if member has reached max books limit
return canBorrowMore($pdo, $member_id);
}
/**
* Get current user data
*/
function getCurrentUser($pdo) {
if (!isLoggedIn()) {
return null;
}
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch();
}
/**
* Update last login timestamp
*/
function updateLastLogin($pdo, $user_id) {
$stmt = $pdo->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
$stmt->execute([$user_id]);
}
/**
* Logout user
*/
function logout() {
$_SESSION = array();
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 3600, '/');
}
session_destroy();
redirect('index.php');
}
?>

Frontend Pages

Main Landing Page

File: index.php

<?php include 'includes/config.php'; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo SITE_NAME; ?></title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary sticky-top">
<div class="container">
<a class="navbar-brand" href="index.php">
<i class="fas fa-book-open me-2"></i>
<?php echo SITE_NAME; ?>
</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 ms-auto">
<li class="nav-item">
<a class="nav-link active" href="index.php">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#features">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#catalog">Catalog</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contact">Contact</a>
</li>
<?php if (isLoggedIn()): ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
<i class="fas fa-user-circle"></i> <?php echo htmlspecialchars($_SESSION['full_name']); ?>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?php 
echo $_SESSION['user_role'] == 'admin' ? 'admin/dashboard.php' : 
($_SESSION['user_role'] == 'librarian' ? 'librarian/dashboard.php' : 'member/dashboard.php'); 
?>">
<i class="fas fa-tachometer-alt"></i> Dashboard
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item text-danger" href="logout.php">
<i class="fas fa-sign-out-alt"></i> Logout
</a>
</li>
</ul>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="login.php">Login</a>
</li>
<li class="nav-item">
<a class="nav-link btn btn-light text-primary ms-2" href="register.php">Register</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<!-- Hero Section -->
<section class="hero-section bg-light py-5">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6">
<h1 class="display-4 fw-bold mb-4">Welcome to the <?php echo LIBRARY_NAME; ?></h1>
<p class="lead mb-4">Your gateway to thousands of books, resources, and knowledge. Access our digital catalog, manage your loans, and discover new reads - all from the comfort of your home.</p>
<?php if (!isLoggedIn()): ?>
<div class="d-grid gap-2 d-md-flex">
<a href="register.php" class="btn btn-primary btn-lg px-4 me-md-2">
<i class="fas fa-user-plus"></i> Get Library Card
</a>
<a href="#catalog" class="btn btn-outline-primary btn-lg px-4">
<i class="fas fa-search"></i> Browse Catalog
</a>
</div>
<?php else: ?>
<a href="<?php 
echo $_SESSION['user_role'] == 'admin' ? 'admin/dashboard.php' : 
($_SESSION['user_role'] == 'librarian' ? 'librarian/dashboard.php' : 'member/dashboard.php'); 
?>" class="btn btn-primary btn-lg">
<i class="fas fa-tachometer-alt"></i> Go to Dashboard
</a>
<?php endif; ?>
</div>
<div class="col-lg-6">
<img src="assets/images/library-hero.svg" alt="Library" class="img-fluid">
</div>
</div>
</div>
</section>
<!-- Statistics Section -->
<section class="stats-section py-5">
<div class="container">
<div class="row text-center">
<?php
$total_books = $pdo->query("SELECT COUNT(*) FROM books WHERE status = 'available'")->fetchColumn();
$total_members = $pdo->query("SELECT COUNT(*) FROM users WHERE role = 'member' AND status = 'active'")->fetchColumn();
$books_issued = $pdo->query("SELECT COUNT(*) FROM transactions WHERE status IN ('issued', 'overdue')")->fetchColumn();
$total_categories = $pdo->query("SELECT COUNT(*) FROM categories")->fetchColumn();
?>
<div class="col-md-3 col-6 mb-3">
<div class="stat-card p-4 bg-white rounded shadow-sm">
<i class="fas fa-book fa-3x text-primary mb-3"></i>
<h3 class="fw-bold"><?php echo number_format($total_books); ?></h3>
<p class="text-muted">Total Books</p>
</div>
</div>
<div class="col-md-3 col-6 mb-3">
<div class="stat-card p-4 bg-white rounded shadow-sm">
<i class="fas fa-users fa-3x text-success mb-3"></i>
<h3 class="fw-bold"><?php echo number_format($total_members); ?></h3>
<p class="text-muted">Active Members</p>
</div>
</div>
<div class="col-md-3 col-6 mb-3">
<div class="stat-card p-4 bg-white rounded shadow-sm">
<i class="fas fa-hand-holding-heart fa-3x text-warning mb-3"></i>
<h3 class="fw-bold"><?php echo number_format($books_issued); ?></h3>
<p class="text-muted">Books Issued</p>
</div>
</div>
<div class="col-md-3 col-6 mb-3">
<div class="stat-card p-4 bg-white rounded shadow-sm">
<i class="fas fa-tags fa-3x text-info mb-3"></i>
<h3 class="fw-bold"><?php echo number_format($total_categories); ?></h3>
<p class="text-muted">Categories</p>
</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section id="features" class="features-section py-5 bg-light">
<div class="container">
<h2 class="text-center fw-bold mb-5">Why Choose Our Library?</h2>
<div class="row g-4">
<div class="col-md-4">
<div class="feature-card text-center p-4 bg-white rounded shadow-sm h-100">
<i class="fas fa-clock fa-3x text-primary mb-3"></i>
<h4>24/7 Online Access</h4>
<p class="text-muted">Access our catalog, renew books, and manage your account anytime, anywhere.</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-card text-center p-4 bg-white rounded shadow-sm h-100">
<i class="fas fa-mobile-alt fa-3x text-primary mb-3"></i>
<h4>Mobile Friendly</h4>
<p class="text-muted">Fully responsive design works perfectly on all your devices.</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-card text-center p-4 bg-white rounded shadow-sm h-100">
<i class="fas fa-bell fa-3x text-primary mb-3"></i>
<h4>Due Date Reminders</h4>
<p class="text-muted">Get email notifications before your books are due.</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-card text-center p-4 bg-white rounded shadow-sm h-100">
<i class="fas fa-search fa-3x text-primary mb-3"></i>
<h4>Advanced Search</h4>
<p class="text-muted">Find books by title, author, ISBN, or category instantly.</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-card text-center p-4 bg-white rounded shadow-sm h-100">
<i class="fas fa-bookmark fa-3x text-primary mb-3"></i>
<h4>Online Reservations</h4>
<p class="text-muted">Reserve books that are currently checked out.</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-card text-center p-4 bg-white rounded shadow-sm h-100">
<i class="fas fa-chart-line fa-3x text-primary mb-3"></i>
<h4>Reading History</h4>
<p class="text-muted">Keep track of all books you've borrowed.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Recent Books Section -->
<section id="catalog" class="catalog-section py-5">
<div class="container">
<h2 class="text-center fw-bold mb-5">Recently Added Books</h2>
<div class="row">
<?php
$stmt = $pdo->query("
SELECT b.*, c.category_name 
FROM books b
JOIN categories c ON b.category_id = c.id
WHERE b.status = 'available'
ORDER BY b.created_at DESC
LIMIT 8
");
$recent_books = $stmt->fetchAll();
foreach ($recent_books as $book):
?>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="book-card h-100">
<div class="book-cover">
<img src="<?php echo getBookCover($book['cover_image']); ?>" 
alt="<?php echo htmlspecialchars($book['title']); ?>" 
class="img-fluid">
</div>
<div class="book-info p-3">
<h5 class="book-title"><?php echo htmlspecialchars($book['title']); ?></h5>
<p class="book-category text-muted small">
<i class="fas fa-tag"></i> <?php echo htmlspecialchars($book['category_name']); ?>
</p>
<div class="book-meta d-flex justify-content-between align-items-center">
<span class="badge bg-success">Available</span>
<a href="member/book_details.php?id=<?php echo $book['id']; ?>" class="btn btn-sm btn-outline-primary">
View Details
</a>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="text-center mt-4">
<a href="member/search_books.php" class="btn btn-primary">
<i class="fas fa-search"></i> Browse Full Catalog
</a>
</div>
</div>
</section>
<!-- How It Works Section -->
<section class="how-it-works py-5 bg-light">
<div class="container">
<h2 class="text-center fw-bold mb-5">How It Works</h2>
<div class="row">
<div class="col-md-4">
<div class="step-card text-center">
<div class="step-number">1</div>
<i class="fas fa-user-plus fa-3x text-primary mb-3"></i>
<h4>Register</h4>
<p class="text-muted">Create your free account and get your library card.</p>
</div>
</div>
<div class="col-md-4">
<div class="step-card text-center">
<div class="step-number">2</div>
<i class="fas fa-search fa-3x text-primary mb-3"></i>
<h4>Search & Reserve</h4>
<p class="text-muted">Browse our catalog and reserve books you want.</p>
</div>
</div>
<div class="col-md-4">
<div class="step-card text-center">
<div class="step-number">3</div>
<i class="fas fa-book-reader fa-3x text-primary mb-3"></i>
<h4>Borrow & Enjoy</h4>
<p class="text-muted">Visit the library to pick up your books and start reading.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section py-5">
<div class="container">
<div class="row">
<div class="col-lg-6">
<h2 class="fw-bold mb-4">Visit Our Library</h2>
<p class="mb-4">We'd love to welcome you to our library. Here's how you can reach us:</p>
<div class="contact-info mb-4">
<div class="d-flex mb-3">
<i class="fas fa-map-marker-alt fa-2x text-primary me-3"></i>
<div>
<h5>Address</h5>
<p class="text-muted"><?php echo LIBRARY_ADDRESS; ?></p>
</div>
</div>
<div class="d-flex mb-3">
<i class="fas fa-phone fa-2x text-primary me-3"></i>
<div>
<h5>Phone</h5>
<p class="text-muted"><?php echo LIBRARY_PHONE; ?></p>
</div>
</div>
<div class="d-flex mb-3">
<i class="fas fa-envelope fa-2x text-primary me-3"></i>
<div>
<h5>Email</h5>
<p class="text-muted"><?php echo LIBRARY_EMAIL; ?></p>
</div>
</div>
<div class="d-flex mb-3">
<i class="fas fa-clock fa-2x text-primary me-3"></i>
<div>
<h5>Hours</h5>
<p class="text-muted">Monday-Friday: 9:00 AM - 8:00 PM<br>Saturday: 10:00 AM - 6:00 PM<br>Sunday: Closed</p>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="contact-form bg-white p-4 rounded shadow">
<h4 class="mb-4">Send Us a Message</h4>
<form action="includes/contact_process.php" method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<input type="text" class="form-control" name="name" placeholder="Your Name" required>
</div>
<div class="col-md-6 mb-3">
<input type="email" class="form-control" name="email" placeholder="Your Email" required>
</div>
<div class="col-12 mb-3">
<input type="text" class="form-control" name="subject" placeholder="Subject" required>
</div>
<div class="col-12 mb-3">
<textarea class="form-control" name="message" rows="5" placeholder="Your Message" required></textarea>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-paper-plane"></i> Send Message
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer bg-dark text-white py-5">
<div class="container">
<div class="row">
<div class="col-md-4 mb-4">
<h5><i class="fas fa-book-open me-2"></i><?php echo SITE_NAME; ?></h5>
<p class="text-white-50">Your premier destination for knowledge, learning, and discovery.</p>
<div class="social-links">
<a href="#" class="text-white me-2"><i class="fab fa-facebook fa-lg"></i></a>
<a href="#" class="text-white me-2"><i class="fab fa-twitter fa-lg"></i></a>
<a href="#" class="text-white me-2"><i class="fab fa-instagram fa-lg"></i></a>
<a href="#" class="text-white me-2"><i class="fab fa-linkedin fa-lg"></i></a>
</div>
</div>
<div class="col-md-2 mb-4">
<h5>Quick Links</h5>
<ul class="list-unstyled">
<li><a href="index.php" class="text-white-50">Home</a></li>
<li><a href="#features" class="text-white-50">Features</a></li>
<li><a href="#catalog" class="text-white-50">Catalog</a></li>
<li><a href="#contact" class="text-white-50">Contact</a></li>
</ul>
</div>
<div class="col-md-2 mb-4">
<h5>Services</h5>
<ul class="list-unstyled">
<li><a href="member/search_books.php" class="text-white-50">Search Books</a></li>
<li><a href="member/my_books.php" class="text-white-50">My Loans</a></li>
<li><a href="member/reservations.php" class="text-white-50">Reservations</a></li>
<li><a href="member/fines.php" class="text-white-50">Pay Fines</a></li>
</ul>
</div>
<div class="col-md-4 mb-4">
<h5>Newsletter</h5>
<p class="text-white-50">Subscribe for updates on new arrivals and events.</p>
<form action="includes/newsletter_process.php" method="POST" class="newsletter-form">
<div class="input-group">
<input type="email" class="form-control" name="email" placeholder="Your Email" required>
<button class="btn btn-primary" type="submit">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</form>
</div>
</div>
<hr class="border-secondary">
<div class="row">
<div class="col-12 text-center">
<p class="text-white-50 mb-0">&copy; 2024 <?php echo SITE_NAME; ?>. All rights reserved.</p>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>

Login Page

File: login.php

<?php include 'includes/config.php'; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - <?php echo SITE_NAME; ?></title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body class="bg-light">
<div class="container">
<div class="row justify-content-center align-items-center min-vh-100">
<div class="col-md-6 col-lg-5">
<div class="card shadow-lg border-0 rounded-lg">
<div class="card-header bg-primary text-white text-center py-4">
<h3 class="mb-0">
<i class="fas fa-book-open me-2"></i>
<?php echo SITE_NAME; ?>
</h3>
</div>
<div class="card-body p-5">
<h4 class="text-center mb-4">Welcome Back!</h4>
<?php if (isset($_SESSION['success'])): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle me-2"></i>
<?php echo $_SESSION['success']; unset($_SESSION['success']); ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle me-2"></i>
<?php echo $_SESSION['error']; unset($_SESSION['error']); ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['errors'])): ?>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach ($_SESSION['errors'] as $error): ?>
<li><?php echo htmlspecialchars($error); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php unset($_SESSION['errors']); ?>
<?php endif; ?>
<form action="includes/login_process.php" method="POST" onsubmit="return validateLogin()">
<div class="mb-4">
<label for="username_email" class="form-label">
<i class="fas fa-user me-2"></i>Username or Email
</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text" class="form-control" id="username_email" 
name="username_email" placeholder="Enter username or email" required>
</div>
</div>
<div class="mb-4">
<label for="password" class="form-label">
<i class="fas fa-lock me-2"></i>Password
</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input type="password" class="form-control" id="password" 
name="password" placeholder="Enter password" required>
<button class="btn btn-outline-secondary" type="button" onclick="togglePassword()">
<i class="fas fa-eye" id="togglePasswordIcon"></i>
</button>
</div>
</div>
<div class="mb-4 form-check">
<input type="checkbox" class="form-check-input" id="remember" name="remember">
<label class="form-check-label" for="remember">Remember me</label>
<a href="forgot_password.php" class="float-end text-decoration-none">Forgot Password?</a>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 mb-3">
<i class="fas fa-sign-in-alt me-2"></i>Login
</button>
</form>
<div class="text-center">
<p class="mb-0">Don't have an account? <a href="register.php" class="text-decoration-none">Register here</a></p>
</div>
<hr class="my-4">
<div class="text-center">
<a href="index.php" class="text-decoration-none">
<i class="fas fa-arrow-left me-1"></i>Back to Home
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
function togglePassword() {
const password = document.getElementById('password');
const icon = document.getElementById('togglePasswordIcon');
if (password.type === 'password') {
password.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
password.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
function validateLogin() {
const username = document.getElementById('username_email').value.trim();
const password = document.getElementById('password').value;
if (username === '' || password === '') {
alert('Please enter both username/email and password.');
return false;
}
return true;
}
</script>
</body>
</html>

Registration Page

File: register.php

<?php include 'includes/config.php'; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register - <?php echo SITE_NAME; ?></title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body class="bg-light">
<div class="container">
<div class="row justify-content-center align-items-center min-vh-100 py-4">
<div class="col-md-8 col-lg-6">
<div class="card shadow-lg border-0 rounded-lg">
<div class="card-header bg-primary text-white text-center py-4">
<h3 class="mb-0">
<i class="fas fa-user-plus me-2"></i>
Create Account
</h3>
<p class="mb-0 text-white-50">Join our library community today</p>
</div>
<div class="card-body p-5">
<?php if (isset($_SESSION['errors'])): ?>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach ($_SESSION['errors'] as $error): ?>
<li><?php echo htmlspecialchars($error); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php unset($_SESSION['errors']); ?>
<?php endif; ?>
<?php $form_data = $_SESSION['form_data'] ?? []; unset($_SESSION['form_data']); ?>
<form action="includes/register_process.php" method="POST" onsubmit="return validateRegistration()">
<div class="row">
<div class="col-md-6 mb-3">
<label for="full_name" class="form-label">
<i class="fas fa-user me-2"></i>Full Name *
</label>
<input type="text" class="form-control" id="full_name" name="full_name" 
value="<?php echo htmlspecialchars($form_data['full_name'] ?? ''); ?>" 
placeholder="Enter your full name" required>
</div>
<div class="col-md-6 mb-3">
<label for="username" class="form-label">
<i class="fas fa-at me-2"></i>Username *
</label>
<input type="text" class="form-control" id="username" name="username" 
value="<?php echo htmlspecialchars($form_data['username'] ?? ''); ?>" 
placeholder="Choose a username" required>
<small class="text-muted">3-20 characters, letters, numbers, underscores</small>
</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">
<i class="fas fa-envelope me-2"></i>Email Address *
</label>
<input type="email" class="form-control" id="email" name="email" 
value="<?php echo htmlspecialchars($form_data['email'] ?? ''); ?>" 
placeholder="Enter your email" required>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="phone" class="form-label">
<i class="fas fa-phone me-2"></i>Phone Number
</label>
<input type="tel" class="form-control" id="phone" name="phone" 
value="<?php echo htmlspecialchars($form_data['phone'] ?? ''); ?>" 
placeholder="Enter phone number">
</div>
<div class="col-md-6 mb-3">
<label for="membership_type" class="form-label">
<i class="fas fa-id-card me-2"></i>Membership Type *
</label>
<select class="form-select" id="membership_type" name="membership_type" required>
<option value="">Select Type</option>
<option value="student" <?php echo ($form_data['membership_type'] ?? '') == 'student' ? 'selected' : ''; ?>>Student</option>
<option value="faculty" <?php echo ($form_data['membership_type'] ?? '') == 'faculty' ? 'selected' : ''; ?>>Faculty/Teacher</option>
<option value="staff" <?php echo ($form_data['membership_type'] ?? '') == 'staff' ? 'selected' : ''; ?>>Staff</option>
<option value="public" <?php echo ($form_data['membership_type'] ?? '') == 'public' ? 'selected' : ''; ?>>Public</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="address" class="form-label">
<i class="fas fa-map-marker-alt me-2"></i>Address
</label>
<textarea class="form-control" id="address" name="address" rows="2" 
placeholder="Enter your address"><?php echo htmlspecialchars($form_data['address'] ?? ''); ?></textarea>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="password" class="form-label">
<i class="fas fa-lock me-2"></i>Password *
</label>
<div class="input-group">
<input type="password" class="form-control" id="password" name="password" 
placeholder="Create password" required>
<button class="btn btn-outline-secondary" type="button" onclick="togglePassword('password')">
<i class="fas fa-eye" id="togglePasswordIcon1"></i>
</button>
</div>
<small class="text-muted">Min 8 chars, 1 uppercase, 1 lowercase, 1 number</small>
</div>
<div class="col-md-6 mb-3">
<label for="confirm_password" class="form-label">
<i class="fas fa-lock me-2"></i>Confirm Password *
</label>
<div class="input-group">
<input type="password" class="form-control" id="confirm_password" name="confirm_password" 
placeholder="Confirm password" required>
<button class="btn btn-outline-secondary" type="button" onclick="togglePassword('confirm_password')">
<i class="fas fa-eye" id="togglePasswordIcon2"></i>
</button>
</div>
</div>
</div>
<div class="mb-4 form-check">
<input type="checkbox" class="form-check-input" id="terms" name="terms" required>
<label class="form-check-label" for="terms">
I agree to the <a href="#" target="_blank">Terms of Service</a> and 
<a href="#" target="_blank">Privacy Policy</a>
</label>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 mb-3">
<i class="fas fa-user-plus me-2"></i>Create Account
</button>
</form>
<div class="text-center">
<p class="mb-0">Already have an account? <a href="login.php" class="text-decoration-none">Sign In</a></p>
</div>
<hr class="my-4">
<div class="text-center">
<a href="index.php" class="text-decoration-none">
<i class="fas fa-arrow-left me-1"></i>Back to Home
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
function togglePassword(fieldId) {
const password = document.getElementById(fieldId);
const icon = document.getElementById('togglePasswordIcon' + (fieldId === 'password' ? '1' : '2'));
if (password.type === 'password') {
password.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
password.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
function validateRegistration() {
const fullname = document.getElementById('full_name').value.trim();
const username = document.getElementById('username').value.trim();
const email = document.getElementById('email').value.trim();
const password = document.getElementById('password').value;
const confirm = document.getElementById('confirm_password').value;
const membership = document.getElementById('membership_type').value;
const terms = document.getElementById('terms').checked;
// Check required fields
if (fullname === '' || username === '' || email === '' || password === '' || membership === '') {
alert('Please fill in all required fields.');
return false;
}
// Username validation
if (username.length < 3 || username.length > 20) {
alert('Username must be between 3 and 20 characters.');
return false;
}
if (!/^[a-zA-Z0-9_]+$/.test(username)) {
alert('Username can only contain letters, numbers, and underscores.');
return false;
}
// Email validation
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(email)) {
alert('Please enter a valid email address.');
return false;
}
// Password validation
if (password.length < 8) {
alert('Password must be at least 8 characters long.');
return false;
}
if (!/[A-Z]/.test(password)) {
alert('Password must contain at least one uppercase letter.');
return false;
}
if (!/[a-z]/.test(password)) {
alert('Password must contain at least one lowercase letter.');
return false;
}
if (!/[0-9]/.test(password)) {
alert('Password must contain at least one number.');
return false;
}
if (password !== confirm) {
alert('Passwords do not match.');
return false;
}
// Terms agreement
if (!terms) {
alert('You must agree to the Terms of Service and Privacy Policy.');
return false;
}
return true;
}
</script>
</body>
</html>

Librarian Dashboard

File: librarian/dashboard.php

<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
requireLibrarian();
$librarian_id = $_SESSION['user_id'];
// Get dashboard statistics
$stats = [];
// Total books
$stats['total_books'] = $pdo->query("SELECT COUNT(*) FROM books")->fetchColumn();
// Books issued today
$stats['issued_today'] = $pdo->query("
SELECT COUNT(*) FROM transactions 
WHERE DATE(issue_date) = CURDATE()
")->fetchColumn();
// Books due today
$stats['due_today'] = $pdo->query("
SELECT COUNT(*) FROM transactions 
WHERE DATE(due_date) = CURDATE() 
AND status IN ('issued', 'overdue')
")->fetchColumn();
// Overdue books
$stats['overdue'] = $pdo->query("
SELECT COUNT(*) FROM transactions 
WHERE due_date < NOW() 
AND status IN ('issued', 'overdue')
")->fetchColumn();
// Active members
$stats['active_members'] = $pdo->query("
SELECT COUNT(*) FROM users 
WHERE role = 'member' AND status = 'active'
")->fetchColumn();
// Pending reservations
$stats['pending_reservations'] = $pdo->query("
SELECT COUNT(*) FROM reservations 
WHERE status = 'pending'
")->fetchColumn();
// Recent transactions
$transactions = $pdo->query("
SELECT t.*, 
b.title as book_title,
u.full_name as member_name,
u.member_id
FROM transactions t
JOIN books b ON t.book_id = b.id
JOIN users u ON t.member_id = u.id
ORDER BY t.created_at DESC
LIMIT 10
")->fetchAll();
// Check for overdue books and send reminders
checkOverdueBooks($pdo);
sendDueReminders($pdo);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Librarian Dashboard - <?php echo SITE_NAME; ?></title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="../assets/css/admin.css">
</head>
<body>
<div class="d-flex">
<!-- Sidebar -->
<div class="sidebar bg-dark text-white" style="width: 280px; min-height: 100vh;">
<div class="sidebar-header p-4">
<a href="dashboard.php" class="text-white text-decoration-none">
<i class="fas fa-book-open fa-2x mb-3"></i>
<h5 class="mb-0">Library System</h5>
<small>Librarian Panel</small>
</a>
</div>
<div class="user-info p-4 border-top border-secondary">
<div class="d-flex align-items-center">
<div class="avatar bg-primary rounded-circle d-flex align-items-center justify-content-center me-3" 
style="width: 50px; height: 50px;">
<?php echo getAvatar($_SESSION['full_name']); ?>
</div>
<div>
<h6 class="mb-0"><?php echo htmlspecialchars($_SESSION['full_name']); ?></h6>
<small class="text-secondary">Librarian</small>
</div>
</div>
</div>
<nav class="nav flex-column p-3">
<a href="dashboard.php" class="nav-link text-white active">
<i class="fas fa-tachometer-alt me-3"></i>Dashboard
</a>
<a href="manage_books.php" class="nav-link text-white">
<i class="fas fa-book me-3"></i>Manage Books
</a>
<a href="manage_members.php" class="nav-link text-white">
<i class="fas fa-users me-3"></i>Manage Members
</a>
<a href="issue_book.php" class="nav-link text-white">
<i class="fas fa-hand-holding-heart me-3"></i>Issue Book
</a>
<a href="return_book.php" class="nav-link text-white">
<i class="fas fa-undo-alt me-3"></i>Return Book
</a>
<a href="view_transactions.php" class="nav-link text-white">
<i class="fas fa-exchange-alt me-3"></i>Transactions
</a>
<a href="overdue_books.php" class="nav-link text-white">
<i class="fas fa-exclamation-triangle me-3"></i>Overdue Books
<?php if ($stats['overdue'] > 0): ?>
<span class="badge bg-danger ms-auto"><?php echo $stats['overdue']; ?></span>
<?php endif; ?>
</a>
<a href="reservations.php" class="nav-link text-white">
<i class="fas fa-calendar-check me-3"></i>Reservations
<?php if ($stats['pending_reservations'] > 0): ?>
<span class="badge bg-warning ms-auto"><?php echo $stats['pending_reservations']; ?></span>
<?php endif; ?>
</a>
<a href="reports.php" class="nav-link text-white">
<i class="fas fa-chart-bar me-3"></i>Reports
</a>
<a href="../logout.php" class="nav-link text-white mt-4">
<i class="fas fa-sign-out-alt me-3"></i>Logout
</a>
</nav>
</div>
<!-- Main Content -->
<div class="main-content flex-grow-1 bg-light">
<header class="bg-white shadow-sm p-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-tachometer-alt me-2 text-primary"></i>
Dashboard
</h5>
<div>
<span class="text-muted me-3">
<i class="fas fa-calendar me-1"></i>
<?php echo date('F j, Y'); ?>
</span>
<span class="text-muted">
<i class="fas fa-clock me-1"></i>
<?php echo date('h:i A'); ?>
</span>
</div>
</div>
</header>
<div class="p-4">
<!-- Statistics Cards -->
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-white-50 mb-2">Total Books</h6>
<h3 class="mb-0"><?php echo number_format($stats['total_books']); ?></h3>
</div>
<i class="fas fa-book fa-3x text-white-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-white-50 mb-2">Issued Today</h6>
<h3 class="mb-0"><?php echo number_format($stats['issued_today']); ?></h3>
</div>
<i class="fas fa-hand-holding-heart fa-3x text-white-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-white-50 mb-2">Due Today</h6>
<h3 class="mb-0"><?php echo number_format($stats['due_today']); ?></h3>
</div>
<i class="fas fa-clock fa-3x text-white-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-white-50 mb-2">Active Members</h6>
<h3 class="mb-0"><?php echo number_format($stats['active_members']); ?></h3>
</div>
<i class="fas fa-users fa-3x text-white-50"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-white">
<h6 class="mb-0">Quick Actions</h6>
</div>
<div class="card-body">
<a href="issue_book.php" class="btn btn-primary me-2 mb-2">
<i class="fas fa-hand-holding-heart me-2"></i>Issue Book
</a>
<a href="return_book.php" class="btn btn-success me-2 mb-2">
<i class="fas fa-undo-alt me-2"></i>Return Book
</a>
<a href="add_book.php" class="btn btn-info me-2 mb-2">
<i class="fas fa-plus-circle me-2"></i>Add New Book
</a>
<a href="add_member.php" class="btn btn-warning me-2 mb-2">
<i class="fas fa-user-plus me-2"></i>Register Member
</a>
</div>
</div>
</div>
</div>
<!-- Recent Transactions -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h6 class="mb-0">Recent Transactions</h6>
<a href="view_transactions.php" class="btn btn-sm btn-outline-primary">
View All
</a>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Transaction ID</th>
<th>Book</th>
<th>Member</th>
<th>Issue Date</th>
<th>Due Date</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($transactions as $trans): ?>
<tr>
<td>
<small><?php echo $trans['transaction_id']; ?></small>
</td>
<td>
<strong><?php echo htmlspecialchars($trans['book_title']); ?></strong>
</td>
<td>
<?php echo htmlspecialchars($trans['member_name']); ?>
<br>
<small class="text-muted"><?php echo $trans['member_id']; ?></small>
</td>
<td><?php echo formatDate($trans['issue_date'], 'M d, Y'); ?></td>
<td>
<?php echo formatDate($trans['due_date'], 'M d, Y'); ?>
<?php if (strtotime($trans['due_date']) < time() && $trans['status'] == 'issued'): ?>
<span class="badge bg-danger">Overdue</span>
<?php endif; ?>
</td>
<td>
<span class="badge bg-<?php 
echo $trans['status'] == 'issued' ? 'success' : 
($trans['status'] == 'overdue' ? 'danger' : 'secondary'); 
?>">
<?php echo ucfirst($trans['status']); ?>
</span>
</td>
<td>
<?php if ($trans['status'] == 'issued'): ?>
<a href="return_book.php?transaction_id=<?php echo $trans['id']; ?>" 
class="btn btn-sm btn-success">
<i class="fas fa-undo"></i> Return
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="../assets/js/main.js"></script>
</body>
</html>

Member Dashboard

File: member/dashboard.php

<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
requireMember();
$member_id = $_SESSION['user_id'];
// Get member information
$member = getCurrentUser($pdo);
// Get current borrowed books
$stmt = $pdo->prepare("
SELECT t.*, b.title, b.cover_image, b.isbn,
DATEDIFF(t.due_date, NOW()) as days_remaining
FROM transactions t
JOIN books b ON t.book_id = b.id
WHERE t.member_id = ? AND t.status IN ('issued', 'overdue')
ORDER BY t.due_date ASC
");
$stmt->execute([$member_id]);
$borrowed_books = $stmt->fetchAll();
// Get borrowing history
$stmt = $pdo->prepare("
SELECT t.*, b.title, b.cover_image,
DATEDIFF(t.return_date, t.issue_date) as days_borrowed
FROM transactions t
JOIN books b ON t.book_id = b.id
WHERE t.member_id = ? AND t.status = 'returned'
ORDER BY t.return_date DESC
LIMIT 5
");
$stmt->execute([$member_id]);
$history = $stmt->fetchAll();
// Get pending reservations
$stmt = $pdo->prepare("
SELECT r.*, b.title, b.cover_image
FROM reservations r
JOIN books b ON r.book_id = b.id
WHERE r.member_id = ? AND r.status = 'pending'
ORDER BY r.reservation_date ASC
");
$stmt->execute([$member_id]);
$reservations = $stmt->fetchAll();
// Get notifications
$stmt = $pdo->prepare("
SELECT * FROM notifications 
WHERE user_id = ? AND is_read = 0
ORDER BY created_at DESC
LIMIT 5
");
$stmt->execute([$member_id]);
$notifications = $stmt->fetchAll();
// Get recommended books (based on category of previously borrowed books)
$stmt = $pdo->prepare("
SELECT DISTINCT b.*, c.category_name,
(SELECT COUNT(*) FROM transactions WHERE book_id = b.id) as times_borrowed
FROM books b
JOIN categories c ON b.category_id = c.id
WHERE b.category_id IN (
SELECT DISTINCT b2.category_id
FROM transactions t
JOIN books b2 ON t.book_id = b2.id
WHERE t.member_id = ?
)
AND b.id NOT IN (
SELECT book_id FROM transactions WHERE member_id = ?
)
AND b.status = 'available'
ORDER BY times_borrowed DESC
LIMIT 6
");
$stmt->execute([$member_id, $member_id]);
$recommendations = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Member Dashboard - <?php echo SITE_NAME; ?></title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="dashboard.php">
<i class="fas fa-book-open me-2"></i>
Library System
</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 ms-auto">
<li class="nav-item">
<a class="nav-link active" href="dashboard.php">
<i class="fas fa-tachometer-alt"></i> Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="search_books.php">
<i class="fas fa-search"></i> Search Books
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="my_books.php">
<i class="fas fa-book"></i> My Books
<?php if (count($borrowed_books) > 0): ?>
<span class="badge bg-danger ms-1"><?php echo count($borrowed_books); ?></span>
<?php endif; ?>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="fines.php">
<i class="fas fa-dollar-sign"></i> Fines
<?php if ($member['fine_due'] > 0): ?>
<span class="badge bg-warning ms-1">$<?php echo number_format($member['fine_due'], 2); ?></span>
<?php endif; ?>
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
<i class="fas fa-user-circle"></i> <?php echo htmlspecialchars($_SESSION['full_name']); ?>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="profile.php">
<i class="fas fa-id-card"></i> My Profile
</a>
</li>
<li>
<a class="dropdown-item" href="notifications.php">
<i class="fas fa-bell"></i> Notifications
<?php if (count($notifications) > 0): ?>
<span class="badge bg-danger ms-2"><?php echo count($notifications); ?></span>
<?php endif; ?>
</a>
</li>
<li>
<a class="dropdown-item" href="settings.php">
<i class="fas fa-cog"></i> Settings
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item text-danger" href="../logout.php">
<i class="fas fa-sign-out-alt"></i> Logout
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="container py-4">
<!-- Welcome Banner -->
<div class="row mb-4">
<div class="col-12">
<div class="card bg-primary text-white">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-8">
<h4>Welcome back, <?php echo htmlspecialchars($member['full_name']); ?>!</h4>
<p class="mb-0">
<i class="fas fa-id-card me-2"></i>Member ID: <?php echo $member['member_id']; ?>
<span class="mx-3">|</span>
<i class="fas fa-calendar me-2"></i>Membership valid until: 
<?php echo formatDate($member['membership_expiry'], 'M d, Y'); ?>
</p>
</div>
<div class="col-md-4 text-md-end">
<span class="badge bg-light text-primary p-2">
<i class="fas fa-book me-1"></i>
<?php echo count($borrowed_books); ?>/<?php echo $member['max_books_allowed']; ?> Books Borrowed
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Notifications Alert -->
<?php if (count($notifications) > 0): ?>
<div class="row mb-4">
<div class="col-12">
<div class="alert alert-info alert-dismissible fade show">
<i class="fas fa-bell me-2"></i>
You have <strong><?php echo count($notifications); ?></strong> unread notification(s).
<a href="notifications.php" class="alert-link">View all</a>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
</div>
</div>
<?php endif; ?>
<!-- Current Borrowed Books -->
<?php if (count($borrowed_books) > 0): ?>
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-white">
<h5 class="mb-0">
<i class="fas fa-book-open me-2 text-primary"></i>
Currently Borrowed Books
</h5>
</div>
<div class="card-body">
<div class="row">
<?php foreach ($borrowed_books as $book): ?>
<div class="col-md-6 mb-3">
<div class="card book-card h-100">
<div class="row g-0">
<div class="col-md-3">
<img src="<?php echo getBookCover($book['cover_image']); ?>" 
class="img-fluid rounded-start" alt="<?php echo htmlspecialchars($book['title']); ?>">
</div>
<div class="col-md-9">
<div class="card-body">
<h6 class="card-title"><?php echo htmlspecialchars($book['title']); ?></h6>
<p class="card-text small text-muted">ISBN: <?php echo $book['isbn']; ?></p>
<div class="d-flex justify-content-between align-items-center">
<div>
<small class="text-muted d-block">
<i class="fas fa-calendar-check me-1"></i>
Due: <?php echo formatDate($book['due_date'], 'M d, Y'); ?>
</small>
<?php if ($book['days_remaining'] < 0): ?>
<span class="badge bg-danger">
Overdue by <?php echo abs($book['days_remaining']); ?> days
</span>
<?php elseif ($book['days_remaining'] <= 3): ?>
<span class="badge bg-warning">
Due in <?php echo $book['days_remaining']; ?> days
</span>
<?php else: ?>
<span class="badge bg-success">
<?php echo $book['days_remaining']; ?> days remaining
</span>
<?php endif; ?>
</div>
<?php if ($book['days_remaining'] > 0 && $book['renewal_count'] < RENEWAL_LIMIT): ?>
<button class="btn btn-sm btn-outline-primary renew-book" 
data-transaction="<?php echo $book['id']; ?>">
<i class="fas fa-sync-alt"></i> Renew
</button>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Quick Search -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<form action="search_books.php" method="GET" class="row g-3">
<div class="col-md-10">
<div class="input-group">
<span class="input-group-text bg-white">
<i class="fas fa-search text-primary"></i>
</span>
<input type="text" class="form-control" name="q" 
placeholder="Search for books by title, author, or ISBN...">
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
Search
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Pending Reservations -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<i class="fas fa-calendar-check me-2 text-warning"></i>
Pending Reservations
</h6>
<?php if (count($reservations) > 0): ?>
<a href="reservations.php" class="btn btn-sm btn-outline-primary">View All</a>
<?php endif; ?>
</div>
<div class="card-body">
<?php if (count($reservations) > 0): ?>
<div class="list-group list-group-flush">
<?php foreach ($reservations as $res): ?>
<div class="list-group-item px-0">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?php echo htmlspecialchars($res['title']); ?></h6>
<small class="text-muted">
Reserved on: <?php echo formatDate($res['reservation_date']); ?>
</small>
</div>
<span class="badge bg-warning">Pending</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="text-muted text-center mb-0">No pending reservations</p>
<?php endif; ?>
</div>
</div>
</div>
<!-- Recent Borrowing History -->
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<i class="fas fa-history me-2 text-info"></i>
Recent Borrowing History
</h6>
<a href="borrow_history.php" class="btn btn-sm btn-outline-primary">View All</a>
</div>
<div class="card-body">
<?php if (count($history) > 0): ?>
<div class="list-group list-group-flush">
<?php foreach ($history as $item): ?>
<div class="list-group-item px-0">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?php echo htmlspecialchars($item['title']); ?></h6>
<small class="text-muted">
Returned: <?php echo formatDate($item['return_date']); ?>
</small>
</div>
<span class="badge bg-success">Returned</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p class="text-muted text-center mb-0">No borrowing history yet</p>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Recommended Books -->
<?php if (count($recommendations) > 0): ?>
<div class="row mt-2">
<div class="col-12">
<div class="card">
<div class="card-header bg-white">
<h5 class="mb-0">
<i class="fas fa-thumbs-up me-2 text-success"></i>
Recommended for You
</h5>
</div>
<div class="card-body">
<div class="row">
<?php foreach ($recommendations as $book): ?>
<div class="col-lg-2 col-md-4 col-6 mb-3">
<div class="book-card text-center">
<a href="book_details.php?id=<?php echo $book['id']; ?>">
<img src="<?php echo getBookCover($book['cover_image']); ?>" 
class="img-fluid mb-2" alt="<?php echo htmlspecialchars($book['title']); ?>"
style="height: 150px; object-fit: cover;">
<h6 class="small"><?php echo htmlspecialchars($book['title']); ?></h6>
<small class="text-muted"><?php echo $book['category_name']; ?></small>
</a>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
</div>
<!-- Renew Book Modal -->
<div class="modal fade" id="renewModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Renew Book</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to renew this book?</p>
<p class="text-muted small">You can renew a book up to <?php echo RENEWAL_LIMIT; ?> times.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirmRenew">Renew</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
let renewTransactionId = null;
// Renew book click handler
$('.renew-book').click(function() {
renewTransactionId = $(this).data('transaction');
$('#renewModal').modal('show');
});
// Confirm renew
$('#confirmRenew').click(function() {
if (renewTransactionId) {
$.ajax({
url: '../api/renew_book.php',
method: 'POST',
data: { transaction_id: renewTransactionId },
success: function(response) {
if (response.success) {
location.reload();
} else {
alert(response.message || 'Error renewing book');
}
},
error: function() {
alert('Error processing request');
}
});
}
$('#renewModal').modal('hide');
});
});
</script>
</body>
</html>

API Endpoints

Renew Book API

File: api/renew_book.php

<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
header('Content-Type: application/json');
if (!isMember()) {
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
exit();
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['transaction_id'])) {
echo json_encode(['success' => false, 'message' => 'Invalid request']);
exit();
}
$transaction_id = $_POST['transaction_id'];
$member_id = $_SESSION['user_id'];
// Check if transaction belongs to member and is eligible for renewal
$stmt = $pdo->prepare("
SELECT t.*, b.title, u.renewal_limit 
FROM transactions t
JOIN books b ON t.book_id = b.id
JOIN users u ON t.member_id = u.id
WHERE t.id = ? AND t.member_id = ? AND t.status = 'issued'
");
$stmt->execute([$transaction_id, $member_id]);
$transaction = $stmt->fetch();
if (!$transaction) {
echo json_encode(['success' => false, 'message' => 'Transaction not found']);
exit();
}
if ($transaction['renewal_count'] >= RENEWAL_LIMIT) {
echo json_encode(['success' => false, 'message' => 'Maximum renewals reached']);
exit();
}
// Check if book is reserved by someone else
$stmt = $pdo->prepare("
SELECT id FROM reservations 
WHERE book_id = ? AND status = 'pending'
");
$stmt->execute([$transaction['book_id']]);
if ($stmt->fetch()) {
echo json_encode(['success' => false, 'message' => 'Book is reserved by another member']);
exit();
}
// Calculate new due date
$new_due_date = date('Y-m-d H:i:s', strtotime($transaction['due_date'] . ' + ' . LOAN_PERIOD_DAYS . ' days'));
// Update transaction
$stmt = $pdo->prepare("
UPDATE transactions 
SET due_date = ?, renewal_count = renewal_count + 1, status = 'renewed' 
WHERE id = ?
");
$success = $stmt->execute([$new_due_date, $transaction_id]);
if ($success) {
// Log activity
logActivity($pdo, $member_id, 'renew_book', "Renewed book: {$transaction['title']}");
// Send notification
sendNotification(
$pdo,
$member_id,
'general',
'Book Renewed',
"Your book '{$transaction['title']}' has been renewed. New due date: " . formatDate($new_due_date)
);
echo json_encode(['success' => true, 'message' => 'Book renewed successfully']);
} else {
echo json_encode(['success' => false, 'message' => 'Error renewing book']);
}
?>

CSS Styling

File: assets/css/style.css

/* Global Styles */
:root {
--primary-color: #0d6efd;
--secondary-color: #6c757d;
--success-color: #198754;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #0dcaf0;
--light-bg: #f8f9fa;
--dark-bg: #212529;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f7fc;
}
/* Hero Section */
.hero-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 100px 0;
}
.hero-section h1 {
font-size: 3rem;
font-weight: 700;
}
/* Statistics Cards */
.stat-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0,0,0,0.1) !important;
}
/* Feature Cards */
.feature-card {
transition: all 0.3s ease;
border: none;
}
.feature-card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 40px rgba(0,0,0,0.1) !important;
}
/* Book Cards */
.book-card {
border: none;
border-radius: 10px;
overflow: hidden;
transition: all 0.3s ease;
background: white;
}
.book-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.15) !important;
}
.book-cover {
height: 200px;
overflow: hidden;
position: relative;
}
.book-cover img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.book-card:hover .book-cover img {
transform: scale(1.05);
}
.book-info {
padding: 15px;
}
.book-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 5px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.book-category {
font-size: 0.85rem;
margin-bottom: 10px;
}
/* Steps Section */
.step-card {
position: relative;
padding: 30px 20px;
background: white;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0,0,0,0.05);
}
.step-number {
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
width: 40px;
height: 40px;
background: var(--primary-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.2rem;
}
/* Dashboard Sidebar */
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
overflow-y: auto;
transition: all 0.3s ease;
}
.sidebar .nav-link {
color: rgba(255,255,255,0.8);
padding: 12px 20px;
border-radius: 5px;
margin: 2px 10px;
transition: all 0.3s ease;
}
.sidebar .nav-link:hover,
.sidebar .nav-link.active {
background: rgba(255,255,255,0.1);
color: white;
}
.sidebar .nav-link i {
width: 20px;
text-align: center;
}
/* Main Content Area */
.main-content {
margin-left: 280px;
padding: 20px;
min-height: 100vh;
}
/* Avatar Styles */
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.1rem;
}
.avatar-lg {
width: 60px;
height: 60px;
font-size: 1.5rem;
}
/* Form Styles */
.form-control:focus,
.form-select:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
}
.input-group-text {
background-color: white;
}
/* Table Styles */
.table {
background: white;
border-radius: 10px;
overflow: hidden;
}
.table thead th {
background-color: var(--light-bg);
border-bottom: 2px solid #dee2e6;
font-weight: 600;
color: var(--dark-bg);
}
/* Pagination */
.pagination {
justify-content: center;
margin-top: 20px;
}
.page-item.active .page-link {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
/* Alert Styles */
.alert {
border: none;
border-radius: 10px;
padding: 15px 20px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
}
.alert-warning {
background-color: #fff3cd;
color: #856404;
}
.alert-info {
background-color: #d1ecf1;
color: #0c5460;
}
/* Modal Styles */
.modal-content {
border: none;
border-radius: 15px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.modal-header {
border-bottom: 1px solid #dee2e6;
padding: 20px 30px;
}
.modal-body {
padding: 30px;
}
.modal-footer {
border-top: 1px solid #dee2e6;
padding: 20px 30px;
}
/* Loading Spinner */
.spinner-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255,255,255,0.8);
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
}
.spinner-overlay.show {
display: flex;
}
/* Responsive Styles */
@media (max-width: 768px) {
.hero-section h1 {
font-size: 2rem;
}
.sidebar {
transform: translateX(-100%);
}
.sidebar.show {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
.step-card {
margin-bottom: 30px;
}
.stat-card {
margin-bottom: 15px;
}
}
/* Print Styles */
@media print {
.no-print {
display: none !important;
}
.sidebar, .navbar, footer {
display: none !important;
}
.main-content {
margin-left: 0 !important;
}
}

How to Use the Project (Step-by-Step Guide)

Prerequisites

  1. XAMPP/WAMP/MAMP installed on your computer
  2. Code Editor (VS Code, Sublime Text, or any preferred editor)
  3. Web Browser (Chrome, Firefox, or Edge)
  4. Basic knowledge of PHP, MySQL, and HTML/CSS

Installation Steps

Step 1: Set Up Local Server

  1. Download and install XAMPP from https://www.apachefriends.org/
  2. Launch XAMPP Control Panel
  3. Start Apache and MySQL services

Step 2: Create Project Folder

  1. Navigate to C:\xampp\htdocs\ (Windows) or /Applications/XAMPP/htdocs/ (Mac)
  2. Create a new folder named online-library-system
  3. Create all the folders and files as shown in the Project File Structure section

Step 3: Set Up Database

  1. Open browser and go to http://localhost/phpmyadmin
  2. Click on "New" to create a new database
  3. Name the database library_management_system and select utf8_general_ci
  4. Click on "Import" tab
  5. Click "Choose File" and select the database.sql file from the sql folder
  6. Click "Go" to import the database structure and sample data

Step 4: Configure Database Connection

  1. Open includes/config.php
  2. Update database credentials if different from default:
   define('DB_HOST', 'localhost');
define('DB_NAME', 'library_management_system');
define('DB_USER', 'root');
define('DB_PASS', '');

Step 5: Create Password Hash for Admin

  1. Go to http://localhost/online-library-system/register.php
  2. Register a test user with password (e.g., Admin@123)
  3. Open phpMyAdmin, go to the users table
  4. Copy the password hash for the admin user
  5. Update the admin insert query in database.sql or manually update the admin password

Step 6: Set Folder Permissions

  1. Create the following folders and ensure they are writable:
  • assets/images/books/
  • assets/images/profiles/
  • assets/images/covers/
  1. On Windows, right-click folders → Properties → Security → give Write permission to Users
  2. On Mac/Linux, run: chmod 777 assets/images/books/ assets/images/profiles/

Step 7: Test the Installation

  1. Open browser and go to http://localhost/online-library-system/
  2. You should see the library homepage
  3. Test different user types: Admin Login:
  • Username: admin
  • Password: Admin@123 (or the password you set) Librarian Login:
  • Register a new user, then in phpMyAdmin change role to 'librarian' Member Login:
  • Register as a new member

System Walkthrough

For Administrators:

  1. Login with admin credentials
  2. Dashboard - View system statistics
  3. Manage Librarians - Add/Edit/Delete librarian accounts
  4. Manage Categories - Create book categories
  5. Manage Publishers - Add publishers
  6. System Settings - Configure library rules (loan period, fine amount, etc.)
  7. View Reports - Generate library usage reports
  8. Audit Logs - Monitor system activities

For Librarians:

  1. Dashboard - View daily statistics and quick actions
  2. Manage Books - Add new books, edit existing ones
  3. Manage Members - Register new members, manage profiles
  4. Issue Books - Issue books to members
  5. Return Books - Process book returns and calculate fines
  6. View Transactions - Track all borrowing activities
  7. Overdue Books - View and manage overdue books
  8. Reservations - Handle book reservations
  9. Reports - Generate circulation reports

For Members:

  1. Dashboard - View borrowed books, recommendations, notifications
  2. Search Books - Search catalog by title, author, ISBN
  3. My Books - View current borrowings and due dates
  4. Borrow History - View past borrowing history
  5. Reservations - Reserve books and view pending reservations
  6. Fines - View and pay fines online
  7. Profile - Update personal information
  8. Wishlist - Create reading wishlist

Key Features Explained

Book Management

  • Add books with ISBN, title, author, publisher, category
  • Upload book covers
  • Track multiple copies
  • Set status (available, reference only, damaged, lost)

Circulation Management

  • Issue books with automatic due date calculation
  • Return books with fine calculation
  • Renew books (limited renewals)
  • Track transaction history

Member Management

  • Member registration with unique ID
  • Membership types (student, faculty, staff, public)
  • Membership expiry tracking
  • Borrowing limits based on membership type

Fine Management

  • Automatic fine calculation for overdue books
  • Online fine payment
  • Fine history tracking
  • Fine waiver option for librarians

Reservation System

  • Reserve books currently issued
  • Automatic notification when available
  • Reservation expiry handling
  • Queue management for popular books

Notification System

  • Due date reminders (1 day and 3 days before)
  • Overdue notifications
  • Reservation available alerts
  • Fine payment confirmations

Reporting

  • Circulation reports
  • Popular books reports
  • Member activity reports
  • Fine collection reports
  • Inventory reports

Troubleshooting

Common Issues and Solutions

  1. Database Connection Error
  • Check if MySQL is running
  • Verify database credentials in config.php
  • Ensure database exists in phpMyAdmin
  1. 404 Page Not Found
  • Check file paths and folder structure
  • Verify BASE_URL in config.php
  • Ensure .htaccess is properly configured
  1. File Upload Issues
  • Check folder permissions (writable)
  • Verify MAX_FILE_SIZE in config.php
  • Check allowed file extensions
  1. Session/Login Issues
  • Clear browser cookies and cache
  • Check session_start() in config.php
  • Verify session save path is writable
  1. Date/Time Issues
  • Check timezone setting in config.php
  • Verify MySQL timezone
  • Check server system time

Security Best Practices

  1. Change default admin password immediately after installation
  2. Use HTTPS in production
  3. Regular backups of database and uploaded files
  4. Input validation on both client and server side
  5. SQL injection prevention using prepared statements
  6. XSS prevention using htmlspecialchars()
  7. CSRF tokens for forms
  8. Password hashing using bcrypt
  9. Rate limiting for login attempts
  10. Regular security updates

Future Enhancements

  1. Email Integration - Send emails for notifications using PHPMailer
  2. SMS Notifications - Send SMS reminders for due dates
  3. QR Code - Generate QR codes for books for easy scanning
  4. Barcode Scanner - Mobile app with barcode scanning
  5. Online Payments - Integrate payment gateways for fines
  6. E-books - Support for digital books
  7. Reading Challenges - Gamification features
  8. Social Features - Book reviews, ratings, recommendations
  9. API Development - REST API for mobile apps
  10. Multi-language Support - Internationalization

Conclusion

The Online Library Management System is a comprehensive, feature-rich application that automates all aspects of library operations. With its intuitive interface, role-based access control, and robust features, it provides an efficient solution for managing books, members, and circulation in any library setting.

The system is built with scalability in mind, allowing easy addition of new features and integration with other systems. Whether you're managing a small school library or a large public library, this system provides all the tools you need to streamline operations and enhance the user experience for both staff and members.

The modular code structure, comprehensive documentation, and security best practices make it easy to deploy, maintain, and extend. With future enhancements like mobile apps and e-book support, this system is ready to evolve with the changing needs of modern libraries.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper