This commit is contained in:
Flatlogic Bot 2025-12-05 05:31:29 +00:00
parent d4e364826a
commit 7c1bf16409
21 changed files with 930 additions and 157 deletions

77
all_requests.php Normal file
View File

@ -0,0 +1,77 @@
<?php
require_once 'auth_check.php';
require_once 'db/config.php';
require_once 'includes/helpers.php';
// Admin-only view
if ($_SESSION['role'] !== 'admin') {
header("Location: index.php"); // Redirect non-admins
exit;
}
$pageTitle = 'All Change Requests';
// Fetch all requests from the database
try {
$pdoconn = db();
$stmt = $pdoconn->prepare('SELECT cr.*, u.username as requester_name FROM change_requests cr JOIN users u ON cr.requester_id = u.id ORDER BY cr.created_at DESC');
$stmt->execute();
$requests = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$requests = [];
error_log("Database error: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($pageTitle) ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<?php include 'header.php'; ?>
<div class="container mt-5">
<h2>All Submitted Requests</h2>
<hr>
<?php if (empty($requests)): ?>
<div class="alert alert-info">No requests have been submitted yet.</div>
<?php else: ?>
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Change Title</th>
<th>Requester</th>
<th>Status</th>
<th>Submitted On</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($requests as $request): ?>
<tr>
<td><?= htmlspecialchars($request['id']) ?></td>
<td><?= htmlspecialchars($request['change_title']) ?></td>
<td><?= htmlspecialchars($request['requester_name']) ?></td>
<td><?= displayStatusBadge($request['status']) ?></td>
<td><?= htmlspecialchars(date('Y-m-d H:i', strtotime($request['created_at']))) ?></td>
<td>
<a href="view_request.php?id=<?= $request['id'] ?>" class="btn btn-sm btn-primary">View</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

39
assets/css/custom.css Normal file
View File

@ -0,0 +1,39 @@
body {
background-color: #f8f9fa;
}
.form-container {
max-width: 900px;
margin: 2rem auto;
padding: 2rem;
background-color: #fff;
border-radius: 0.5rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.form-section {
margin-bottom: 2rem;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 1rem;
border-bottom: 2px solid #0d6efd;
padding-bottom: 0.5rem;
}
.approval-history-table th,
.approval-history-table td {
vertical-align: middle;
text-align: center;
}
.stamp-cell {
height: 80px;
width: 80px;
border: 1px solid #dee2e6;
vertical-align: middle;
text-align: center;
font-size: 0.8rem;
color: #6c757d;
}

47
auth.php Normal file
View File

@ -0,0 +1,47 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: login.php');
exit();
}
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($username) || empty($password)) {
$_SESSION['error_message'] = 'Please enter both username and password.';
header('Location: login.php');
exit();
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute([':username' => $username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
// Password is correct, start session
session_regenerate_id(); // Prevents session fixation
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
$_SESSION['full_name'] = $user['full_name'];
$_SESSION['department'] = $user['department'];
header("Location: index.php");
exit();
} else {
$_SESSION['error_message'] = 'Invalid username or password.';
header("Location: login.php");
exit();
}
} catch (PDOException $e) {
// In a real app, you'd log this error
// error_log("Login error: " . $e->getMessage());
$_SESSION['error_message'] = 'A database error occurred. Please try again later.';
header('Location: login.php');
exit();
}

10
auth_check.php Normal file
View File

@ -0,0 +1,10 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// If user is not logged in, redirect to login page
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}

23
db/migrate.php Normal file
View File

@ -0,0 +1,23 @@
<?php
require_once __DIR__ . '/config.php';
try {
$pdo = db();
echo "Connected to database successfully.\n";
$migration_files = glob(__DIR__ . '/migrations/*.sql');
sort($migration_files);
foreach ($migration_files as $file) {
echo "Applying migration: " . basename($file) . "...\n";
$sql = file_get_contents($file);
$pdo->exec($sql);
echo "Applied successfully.\n";
}
echo "All migrations applied.\n";
} catch (PDOException $e) {
die("Database error: " . $e->getMessage());
}

View File

@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS `change_requests` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`request_date` DATE,
`requester_dept` VARCHAR(255),
`requester_name` VARCHAR(255),
`requester_ext` VARCHAR(50),
`related_request_no` VARCHAR(255),
`system_name` VARCHAR(255),
`program_name` VARCHAR(255),
`change_category` VARCHAR(50),
`reason_for_change` TEXT,
`description_of_change` TEXT,
`status` VARCHAR(50) DEFAULT 'draft',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,11 @@
-- 002_create_users_table.sql
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
full_name VARCHAR(100),
department VARCHAR(100),
position VARCHAR(100),
role ENUM('requester', 'approver', 'admin') NOT NULL DEFAULT 'requester',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,7 @@
-- 003_add_requester_id_to_change_requests.sql
ALTER TABLE change_requests
ADD COLUMN requester_id INT NULL,
ADD CONSTRAINT fk_requester_id
FOREIGN KEY (requester_id)
REFERENCES users(id)
ON DELETE SET NULL;

View File

@ -0,0 +1,8 @@
-- Add change_title and admin_comment columns
ALTER TABLE `change_requests`
ADD COLUMN `change_title` VARCHAR(255) NOT NULL AFTER `program_name`,
ADD COLUMN `admin_comment` TEXT AFTER `description_of_change`;
-- Modify status column
ALTER TABLE `change_requests`
MODIFY COLUMN `status` ENUM('Pending', 'Approved', 'In Development', 'Completed', 'Rejected') NOT NULL DEFAULT 'Pending';

49
db/seed_users.php Normal file
View File

@ -0,0 +1,49 @@
<?php
require_once __DIR__ . '/config.php';
try {
$pdo = db();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Default users
$users = [
[
'username' => 'admin',
'password' => password_hash('admin_password', PASSWORD_DEFAULT),
'full_name' => 'Admin User',
'department' => 'RSS SA',
'position' => 'System Administrator',
'role' => 'admin'
],
[
'username' => 'requester',
'password' => password_hash('requester_password', PASSWORD_DEFAULT),
'full_name' => 'Regular User',
'department' => 'RSS JP Sales',
'position' => 'Sales Rep',
'role' => 'requester'
]
];
$stmt = $pdo->prepare(
"INSERT INTO users (username, password, full_name, department, position, role)
VALUES (:username, :password, :full_name, :department, :position, :role)
ON DUPLICATE KEY UPDATE password=VALUES(password), full_name=VALUES(full_name), role=VALUES(role);"
);
foreach ($users as $user) {
$stmt->execute([
':username' => $user['username'],
':password' => $user['password'],
':full_name' => $user['full_name'],
':department' => $user['department'],
':position' => $user['position'],
':role' => $user['role']
]);
echo "User '{$user['username']}' seeded successfully.\n";
}
} catch (PDOException $e) {
die("Error seeding users: " . $e->getMessage());
}

51
header.php Normal file
View File

@ -0,0 +1,51 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$is_logged_in = isset($_SESSION["user_id"]);
$username = $is_logged_in ? $_SESSION["username"] : "";
$is_admin = $is_logged_in && $_SESSION['role'] === 'admin';
?>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="index.php">Change Request System</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav mr-auto">
<?php if ($is_logged_in): ?>
<li class="nav-item">
<a class="nav-link" href="index.php">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="request_form.php">New Request</a>
</li>
<li class="nav-item">
<a class="nav-link" href="my_requests.php">My Requests</a>
</li>
<li class="nav-item">
<a class="nav-link" href="search.php">Search</a>
</li>
<?php if ($is_admin): ?>
<li class="nav-item">
<a class="nav-link" href="all_requests.php">All Requests</a>
</li>
<?php endif; ?>
<?php endif; ?>
</ul>
<ul class="navbar-nav">
<?php if ($is_logged_in): ?>
<li class="nav-item">
<span class="navbar-text">Welcome, <?= htmlspecialchars($username) ?></span>
</li>
<li class="nav-item ml-2">
<a href="logout.php" class="btn btn-outline-danger">Logout</a>
</li>
<?php else: ?>
<li class="nav-item">
<a href="login.php" class="btn btn-primary">Login</a>
</li>
<?php endif; ?>
</ul>
</div>
</nav>

22
includes/helpers.php Normal file
View File

@ -0,0 +1,22 @@
<?php
function displayStatusBadge($status) {
$badgeClass = 'badge-secondary'; // Default
switch ($status) {
case 'Pending':
$badgeClass = 'badge-warning';
break;
case 'Approved':
$badgeClass = 'badge-success';
break;
case 'In Development':
$badgeClass = 'badge-primary';
break;
case 'Completed':
$badgeClass = 'badge-dark';
break;
case 'Rejected':
$badgeClass = 'badge-danger';
break;
}
return '<span class="badge badge-pill ' . $badgeClass . '">' . htmlspecialchars(ucfirst($status)) . '</span>';
}

165
index.php
View File

@ -1,150 +1,33 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
require_once __DIR__ . '/auth_check.php';
$pageTitle = "Dashboard";
?>
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($pageTitle) ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<?php include 'header.php'; ?>
<div class="container mt-5">
<div class="card">
<div class="card-body">
<h1 class="card-title">Dashboard</h1>
<p>Welcome to your Approval Workflow Application.</p>
<a href="request_form.php" class="btn btn-primary">Create New Request</a>
<a href="my_requests.php" class="btn btn-secondary">View My Requests</a>
</div>
</div>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

66
login.php Normal file
View File

@ -0,0 +1,66 @@
<?php
session_start();
// If user is already logged in, redirect to index.php
if (isset($_SESSION['user_id'])) {
header("Location: index.php");
exit();
}
$error_message = '';
if (isset($_SESSION['error_message'])) {
$error_message = $_SESSION['error_message'];
unset($_SESSION['error_message']);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-4">
<div class="card mt-5">
<div class="card-body">
<h3 class="card-title text-center mb-4">Login</h3>
<?php if ($error_message): ?>
<div class="alert alert-danger" role="alert">
<?php echo htmlspecialchars($error_message); ?>
</div>
<?php endif; ?>
<form action="auth.php" method="POST">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Login</button>
</div>
</form>
</div>
<div class="card-footer text-center">
<p class="text-muted small mb-0">Default users:</p>
<p class="text-muted small mb-0">admin / admin_password</p>
<p class="text-muted small mb-0">requester / requester_password</p>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

21
logout.php Normal file
View File

@ -0,0 +1,21 @@
<?php
session_start();
// Unset all of the session variables.
$_SESSION = array();
// If it's desired to kill the session, also delete the session cookie.
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Finally, destroy the session.
session_destroy();
// Redirect to login page
header("Location: login.php");
exit();

69
my_requests.php Normal file
View File

@ -0,0 +1,69 @@
<?php
require_once 'auth_check.php';
require_once 'db/config.php';
require_once 'includes/helpers.php';
$pageTitle = 'My Change Requests';
$userId = $_SESSION['user_id'];
// Fetch requests for the logged-in user
try {
$pdoconn = db();
$stmt = $pdoconn->prepare('SELECT * FROM change_requests WHERE requester_id = :requester_id ORDER BY created_at DESC');
$stmt->bindParam(':requester_id', $userId, PDO::PARAM_INT);
$stmt->execute();
$requests = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// Handle DB error
$requests = [];
error_log("Database error: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($pageTitle) ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<?php include 'header.php'; // Use a shared header ?>
<div class="container mt-5">
<h2>My Submitted Requests</h2>
<hr>
<?php if (empty($requests)): ?>
<div class="alert alert-info">You have not submitted any requests yet.</div>
<?php else: ?>
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Change Title</th>
<th>Status</th>
<th>Submitted On</th>
</tr>
</thead>
<tbody>
<?php foreach ($requests as $request): ?>
<tr>
<td><?= htmlspecialchars($request['id']) ?></td>
<td><?= htmlspecialchars($request['change_title']) ?></td>
<td><?= displayStatusBadge($request['status']) ?></td>
<td><?= htmlspecialchars(date('Y-m-d H:i', strtotime($request['created_at']))) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<a href="request_form.php" class="btn btn-primary">Submit New Request</a>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -6,6 +6,27 @@ $username = $_SESSION['username'] ?? 'User';
$full_name = $_SESSION['full_name'] ?? '';
$department = $_SESSION['department'] ?? '';
// Autogenerate Related Change Request No.
require_once __DIR__ . '/db/config.php';
$pdo = db();
$year = date('Y');
$month = date('m');
$prefix = "$department/$year/$month/";
$stmt = $pdo->prepare("SELECT related_request_no FROM change_requests WHERE related_request_no LIKE ? ORDER BY related_request_no DESC LIMIT 1");
$stmt->execute([$prefix . '%']);
$lastRequest = $stmt->fetch();
$running_no = 1;
if ($lastRequest) {
$last_no = (int)substr($lastRequest['related_request_no'], strlen($prefix));
$running_no = $last_no + 1;
}
$related_request_no = $prefix . str_pad($running_no, 3, '0', STR_PAD_LEFT);
?>
<!DOCTYPE html>
<html lang="en">
@ -55,30 +76,17 @@ $department = $_SESSION['department'] ?? '';
</div>
<div class="col-md-6">
<label for="relatedRequestNo" class="form-label">Related Change Request No. / 関連依頼No.</label>
<input type="text" class="form-control" id="relatedRequestNo" name="related_request_no">
<input type="text" class="form-control" id="relatedRequestNo" name="related_request_no" value="<?= htmlspecialchars($related_request_no); ?>" readonly>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4">
<label for="requesterDept" class="form-label">Department / 所属</label>
<select class="form-control" id="requesterDept" name="requester_dept">
<option value="" disabled selected>Select Department</option>
<option value="RSS JP Sales" <?= ($department == "RSS JP Sales") ? "selected" : "" ?>>RSS JP Sales</option>
<option value="RSS EA Sales" <?= ($department == "RSS EA Sales") ? "selected" : "" ?>>RSS EA Sales</option>
<option value="RSI Sales" <?= ($department == "RSI Sales") ? "selected" : "" ?>>RSI Sales</option>
<option value="RSM Sales" <?= ($department == "RSM Sales") ? "selected" : "" ?>>RSM Sales</option>
<option value="RSP Sales" <?= ($department == "RSP Sales") ? "selected" : "" ?>>RSP Sales</option>
<option value="RSS ACC" <?= ($department == "RSS ACC") ? "selected" : "" ?>>RSS ACC</option>
<option value="RSS HRADM" <?= ($department == "RSS HRADM") ? "selected" : "" ?>>RSS HRADM</option>
<option value="RSS WH" <?= ($department == "RSS WH") ? "selected" : "" ?>>RSS WH</option>
<option value="RSS SA" <?= ($department == "RSS SA") ? "selected" : "" ?>>RSS SA</option>
<option value="RSS IS" <?= ($department == "RSS IS") ? "selected" : "" ?>>RSS IS</option>
<option value="RSS QA" <?= ($department == "RSS QA") ? "selected" : "" ?>>RSS QA</option>
</select>
<input type="text" class="form-control" id="requesterDept" name="requester_dept" value="<?= htmlspecialchars($department); ?>" readonly>
</div>
<div class="col-md-4">
<label for="requesterName" class="form-label">Requester Name / 依頼者氏名</label>
<input type="text" class="form-control" id="requesterName" name="requester_name" value="<?= htmlspecialchars($full_name); ?>">
<input type="text" class="form-control" id="requesterName" name="requester_name" value="<?= htmlspecialchars($full_name); ?>" readonly>
</div>
<div class="col-md-4">
<label for="requesterTel" class="form-label">TEL</label>

87
search.php Normal file
View File

@ -0,0 +1,87 @@
<?php
session_start();
require_once 'auth_check.php';
require_once 'db/config.php';
require_once 'includes/helpers.php';
$pageTitle = 'Search Requests';
$searchTerm = filter_input(INPUT_GET, 'q', FILTER_SANITIZE_STRING);
$results = [];
if ($searchTerm) {
try {
$pdoconn = db();
$stmt = $pdoconn->prepare("SELECT cr.*, u.username as requester_name FROM change_requests cr JOIN users u ON cr.requester_id = u.id WHERE cr.program_name LIKE :term OR cr.system_name LIKE :term ORDER BY cr.created_at DESC");
$stmt->bindValue(':term', '%' . $searchTerm . '%', PDO::PARAM_STR);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$results = [];
error_log("Database error: " . $e->getMessage());
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($pageTitle) ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<?php include 'header.php'; ?>
<div class="container mt-5">
<h2>Search Change Requests</h2>
<hr>
<form action="search.php" method="GET" class="mb-4">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search by Program or System Name..." value="<?= htmlspecialchars($searchTerm) ?>">
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
</form>
<?php if ($searchTerm): ?>
<h3>Results for "<?= htmlspecialchars($searchTerm) ?>"</h3>
<?php if (empty($results)): ?>
<div class="alert alert-info">No requests found matching your search term.</div>
<?php else: ?>
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Change Title</th>
<th>Requester</th>
<th>Status</th>
<th>Submitted On</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($results as $request): ?>
<tr>
<td><?= htmlspecialchars($request['id']) ?></td>
<td><?= htmlspecialchars($request['change_title']) ?></td>
<td><?= htmlspecialchars($request['requester_name']) ?></td>
<td><?= displayStatusBadge($request['status']) ?></td>
<td><?= htmlspecialchars(date('Y-m-d H:i', strtotime($request['created_at']))) ?></td>
<td>
<a href="view_request.php?id=<?= $request['id'] ?>" class="btn btn-sm btn-primary">View</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php endif; ?>
</div>
<script src="https://code.jquery.com/jquery-3.b.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

72
submit_request.php Normal file
View File

@ -0,0 +1,72 @@
<?php
require_once __DIR__ . '/auth_check.php';
require_once 'db/config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: request_form.php');
exit;
}
try {
$pdo = db();
$status = 'Pending';
$requester_id = $_SESSION['user_id'] ?? null;
$sql = "INSERT INTO change_requests (
requester_id,
request_date,
requester_dept,
requester_name,
requester_ext,
related_request_no,
system_name,
program_name,
change_title,
change_category,
reason_for_change,
description_of_change,
status
) VALUES (
:requester_id,
:request_date,
:requester_dept,
:requester_name,
:requester_ext,
:related_request_no,
:system_name,
:program_name,
:change_title,
:change_category,
:reason_for_change,
:description_of_change,
:status
)";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':requester_id', $requester_id, PDO::PARAM_INT);
$stmt->bindValue(':request_date', !empty($_POST['request_date']) ? $_POST['request_date'] : null);
$stmt->bindValue(':requester_dept', $_POST['requester_dept'] ?? null);
$stmt->bindValue(':requester_name', $_POST['requester_name'] ?? null);
$stmt->bindValue(':requester_ext', $_POST['requester_ext'] ?? null);
$stmt->bindValue(':related_request_no', $_POST['related_request_no'] ?? null);
$stmt->bindValue(':system_name', $_POST['system_name'] ?? null);
$stmt->bindValue(':program_name', $_POST['program_name'] ?? null);
$stmt->bindValue(':change_title', $_POST['change_title'] ?? null);
$stmt->bindValue(':change_category', $_POST['change_category'] ?? null);
$stmt->bindValue(':reason_for_change', $_POST['reason_for_change'] ?? null);
$stmt->bindValue(':description_of_change', $_POST['description_of_change'] ?? null);
$stmt->bindValue(':status', $status);
$stmt->execute();
$_SESSION['success_message'] = "Request submitted successfully with status: $status.";
} catch (PDOException $e) {
$_SESSION['error_message'] = "Error submitting request: " . $e->getMessage();
}
header('Location: request_form.php');
exit;

92
update_request_status.php Normal file
View File

@ -0,0 +1,92 @@
<?php
require_once 'auth_check.php';
require_once 'db/config.php';
require_once 'mail/MailService.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: all_requests.php');
exit;
}
// Admin-only action
if ($_SESSION['role'] !== 'admin') {
$_SESSION['error_message'] = "You are not authorized to perform this action.";
header('Location: index.php');
exit;
}
$requestId = filter_input(INPUT_POST, 'request_id', FILTER_VALIDATE_INT);
$newStatus = filter_input(INPUT_POST, 'new_status', FILTER_SANITIZE_STRING);
$adminComment = filter_input(INPUT_POST, 'admin_comment', FILTER_SANITIZE_STRING);
if (!$requestId || !$newStatus) {
$_SESSION['error_message'] = "Invalid data provided.";
header('Location: all_requests.php');
exit;
}
// Validate status
$allowed_statuses = ['Pending', 'Approved', 'In Development', 'Completed', 'Rejected'];
if (!in_array($newStatus, $allowed_statuses)) {
$_SESSION['error_message'] = "Invalid status value.";
header('Location: view_request.php?id=' . $requestId);
exit;
}
try {
$pdoconn = db();
// Fetch request details
$stmt = $pdoconn->prepare("SELECT cr.status, cr.change_title FROM change_requests cr WHERE cr.id = :id");
$stmt->bindParam(':id', $requestId, PDO::PARAM_INT);
$stmt->execute();
$request = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$request) {
$_SESSION['error_message'] = "Request not found.";
header('Location: all_requests.php');
exit;
}
// Update the request status and/or comment
$sql = "UPDATE change_requests SET status = :status, admin_comment = :comment WHERE id = :id";
$stmt = $pdoconn->prepare($sql);
$stmt->bindParam(':status', $newStatus, PDO::PARAM_STR);
$stmt->bindParam(':comment', $adminComment, PDO::PARAM_STR);
$stmt->bindParam(':id', $requestId, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->rowCount() > 0) {
$_SESSION['success_message'] = "Request status updated successfully.";
// Send email notification if the status has changed
if ($request['status'] !== $newStatus) {
// NOTE: Email sending is disabled because requester email is not available in the users table.
/*
$to = $request['requester_email']; // This column does not exist
$subject = "Update on your Change Request #{$requestId}";
$body = "<p>The status of your change request '{$request['change_title']}' has been updated to <strong>" . htmlspecialchars(ucfirst($newStatus)) . "</strong>.</p>";
if (!empty($adminComment)) {
$body .= "<p><strong>Admin Comment:</strong> " . htmlspecialchars($adminComment) . "</p>";
}
$body .= "<p>You can view the request here: <a href=\"http://{$_SERVER['HTTP_HOST']}/view_request.php?id={$requestId}\">View Request</a></p>";
MailService::sendMail($to, $subject, $body, strip_tags($body));
$_SESSION['success_message'] = "Request status updated and notification sent.";
*/
}
} else {
$_SESSION['info_message'] = "No changes were made to the request.";
}
} catch (PDOException $e) {
$_SESSION['error_message'] = "Database error: " . $e->getMessage();
error_log("DB Error: " . $e->getMessage());
} catch (Exception $e) {
$_SESSION['error_message'] = "Error: " . $e->getMessage();
error_log("General Error: " . $e->getMessage());
}
header('Location: view_request.php?id=' . $requestId);
exit;

115
view_request.php Normal file
View File

@ -0,0 +1,115 @@
<?php
require_once 'auth_check.php';
require_once 'db/config.php';
$pageTitle = 'View Change Request';
$requestId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if (!$requestId) {
header("Location: all_requests.php");
exit;
}
// Fetch the request and requester info
try {
$pdoconn = db();
$stmt = $pdoconn->prepare('SELECT cr.*, u.full_name as requester_full_name, u.username as requester_username, cr.related_request_id FROM change_requests cr JOIN users u ON cr.requester_id = u.id WHERE cr.id = :id');
$stmt->bindParam(':id', $requestId, PDO::PARAM_INT);
$stmt->execute();
$request = $stmt->fetch(PDO::FETCH_ASSOC);
var_dump($request);
} catch (PDOException $e) {
$request = null;
error_log("DB Error: " . $e->getMessage());
}
if (!$request) {
$_SESSION['error_message'] = "Request not found.";
header("Location: all_requests.php");
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($pageTitle) ?> - #<?= htmlspecialchars($request['id']) ?></title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<?php include 'header.php'; ?>
<div class="container mt-5">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h3>Change Request #<?= htmlspecialchars($request['id']) ?></h3>
<span class="badge badge-info" style="font-size: 1rem;"><?= htmlspecialchars(ucfirst($request['status'])) ?></span>
</div>
<div class="card-body">
<h5 class="card-title"><?= htmlspecialchars($request['change_title']) ?></h5>
<hr>
<!-- Request Details -->
<div class="row">
<div class="col-md-6">
<p><strong>Requester:</strong> <?= htmlspecialchars($request['requester_full_name']) ?></p>
<p><strong>Department:</strong> <?= htmlspecialchars($request['requester_dept']) ?></p>
<p><strong>System Name:</strong> <?= htmlspecialchars($request['system_name']) ?></p>
</div>
<div class="col-md-6">
<p><strong>Submission Date:</strong> <?= date('Y-m-d H:i', strtotime($request['created_at'])) ?></p>
<p><strong>Category:</strong> <?= htmlspecialchars($request['change_category']) ?></p>
<p><strong>Program Name:</strong> <?= htmlspecialchars($request['program_name']) ?></p>
<?php if (!empty($request['related_request_id'])): ?>
<p><strong>Related Change Request No:</strong> <a href="view_request.php?id=<?= htmlspecialchars($request['related_request_id']) ?>"><?= htmlspecialchars($request['related_request_id']) ?></a></p>
<?php endif; ?>
</div>
</div>
<div class="mt-3">
<h6><strong>Reason for Change:</strong></h6>
<p><?= nl2br(htmlspecialchars($request['reason_for_change'])) ?></p>
</div>
<div class="mt-3">
<h6><strong>Description of Change:</strong></h6>
<p><?= nl2br(htmlspecialchars($request['description_of_change'])) ?></p>
</div>
<?php if ($_SESSION['role'] === 'admin'): ?>
<hr>
<div class="mt-4">
<h4>Approval Action</h4>
<form action="update_request_status.php" method="POST">
<input type="hidden" name="request_id" value="<?= $request['id'] ?>">
<div class="form-group">
<label for="new_status">Change Status:</label>
<select name="new_status" id="new_status" class="form-control">
<option value="Pending" <?= $request['status'] === 'Pending' ? 'selected' : '' ?>>Pending</option>
<option value="Approved" <?= $request['status'] === 'Approved' ? 'selected' : '' ?>>Approved</option>
<option value="In Development" <?= $request['status'] === 'In Development' ? 'selected' : '' ?>>In Development</option>
<option value="Completed" <?= $request['status'] === 'Completed' ? 'selected' : '' ?>>Completed</option>
<option value="Rejected" <?= $request['status'] === 'Rejected' ? 'selected' : '' ?>>Rejected</option>
</select>
</div>
<div class="form-group">
<label for="admin_comment">Comment:</label>
<textarea name="admin_comment" id="admin_comment" class="form-control" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-success">Update Status</button>
</form>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>