Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Basic validation
|
||||
if (empty(trim($_POST['name'])) || empty(trim($_POST['description']))) {
|
||||
// Redirect back with an error message if needed, but for now we just exit.
|
||||
header('Location: index.php?error=emptyfields');
|
||||
exit;
|
||||
}
|
||||
|
||||
$name = trim($_POST['name']);
|
||||
$description = trim($_POST['description']);
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$sql = "INSERT INTO processes (name, description) VALUES (?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$name, $description]);
|
||||
|
||||
$process_id = $pdo->lastInsertId();
|
||||
|
||||
// Insert process steps if available
|
||||
if (isset($_POST['steps']) && is_array($_POST['steps'])) {
|
||||
$step_order = 0;
|
||||
$stmt_steps = $pdo->prepare("INSERT INTO process_steps (process_id, title, description, step_order) VALUES (?, ?, ?, ?)");
|
||||
foreach ($_POST['steps'] as $step) {
|
||||
if (isset($step['title']) && isset($step['description'])) {
|
||||
$stmt_steps->execute([$process_id, $step['title'], $step['description'], $step_order]);
|
||||
$step_order++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, log this error. For now, we'll just die.
|
||||
error_log("DB Error: " . $e->getMessage());
|
||||
header('Location: index.php?error=dberror');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Redirect back to the main page after successful insertion
|
||||
header('Location: index.php?success=processadded');
|
||||
exit;
|
||||
} else {
|
||||
// If not a POST request, redirect to the main page
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../ai/LocalAIApi.php';
|
||||
|
||||
$keyword = $_GET['keyword'] ?? 'new process';
|
||||
|
||||
$prompt = "Generate a creative and concise name, a short description, and a list of detailed steps for a business process related to '" . htmlspecialchars($keyword) . "'. Each step should have a 'title' and a 'description'. Respond in JSON format with 'name', 'description', and 'steps' keys. Example: {"name": "Employee Onboarding Process", "description": "A systematic process to integrate new hires into the company.", "steps": [{"title": "Send welcome email", "description": "Automatically send a welcome email with company resources."}, {"title": "Setup IT access", "description": "Provision access to internal systems and tools."}]}";
|
||||
|
||||
$params = [
|
||||
'input' => [
|
||||
['role' => 'system', 'content' => 'You are a helpful assistant that generates process names and descriptions.'],
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
'model' => 'gpt-5-mini', // Using the default model from ai/config.php
|
||||
];
|
||||
|
||||
$response = LocalAIApi::createResponse($params);
|
||||
|
||||
if (!empty($response['success'])) {
|
||||
$decoded = LocalAIApi::decodeJsonFromResponse($response);
|
||||
if ($decoded && isset($decoded['name']) && isset($decoded['description']) && isset($decoded['steps'])) {
|
||||
echo json_encode(['success' => true, 'data' => $decoded]);
|
||||
} else {
|
||||
// Fallback if AI didn't return valid JSON or missing keys
|
||||
$text = LocalAIApi::extractText($response);
|
||||
echo json_encode(['success' => false, 'error' => 'AI response not in expected JSON format: ' . substr($text, 0, 200)]);
|
||||
}
|
||||
} else {
|
||||
$error_message = $response['error'] ?? 'Unknown AI error';
|
||||
echo json_encode(['success' => false, 'error' => $error_message]);
|
||||
}
|
||||
?>
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
ProcessFlow Optimizer Custom Stylesheet
|
||||
*/
|
||||
|
||||
:root {
|
||||
--bs-primary: #4A90E2;
|
||||
--bs-primary-rgb: 74, 144, 226;
|
||||
--bs-secondary: #50E3C2;
|
||||
--bs-secondary-rgb: 80, 227, 194;
|
||||
--bs-body-bg: #F8F9FA;
|
||||
--bs-body-font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
--bs-border-radius: 0.5rem;
|
||||
--bs-card-border-width: 0;
|
||||
--bs-card-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
body {
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--bs-primary);
|
||||
border-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
background-color: var(--bs-primary);
|
||||
border-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(90deg, #4A90E2 0%, #63A4F7 100%);
|
||||
color: white;
|
||||
padding: 2.5rem 0;
|
||||
margin-bottom: -1rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
}
|
||||
.header p {
|
||||
color: rgba(255,255,255,0.8) !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: var(--bs-border-radius);
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-bottom: 1px solid #eee !important;
|
||||
}
|
||||
.list-group-item:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #80bdff;
|
||||
box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
require_once 'config.php';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$sql = "
|
||||
CREATE TABLE IF NOT EXISTS processes (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);";
|
||||
$pdo->exec($sql);
|
||||
echo "Database migration successful. 'processes' table is ready.";
|
||||
} catch (PDOException $e) {
|
||||
die("Database migration failed: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
@ -1,9 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS process_steps (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
process_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
step_order INT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (process_id) REFERENCES processes(id) ON DELETE CASCADE
|
||||
);
|
||||
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['id'])) {
|
||||
$id = filter_var($_POST['id'], FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Delete associated steps first
|
||||
$stmtSteps = $pdo->prepare("DELETE FROM process_steps WHERE process_id = :id");
|
||||
$stmtSteps->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmtSteps->execute();
|
||||
|
||||
// Then delete the process
|
||||
$stmtProcess = $pdo->prepare("DELETE FROM processes WHERE id = :id");
|
||||
$stmtProcess->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
if ($stmtProcess->execute()) {
|
||||
$pdo->commit();
|
||||
header('Location: index.php?success=processdeleted');
|
||||
exit();
|
||||
} else {
|
||||
$pdo->rollBack();
|
||||
header('Location: index.php?error=deletionfailed');
|
||||
exit();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
error_log("DB Error: " . $e->getMessage());
|
||||
header('Location: index.php?error=dberror');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php?error=invalidrequest');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
173
edit_process.php
173
edit_process.php
@ -1,173 +0,0 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
$process = null;
|
||||
$error = '';
|
||||
|
||||
if (isset($_GET['id'])) {
|
||||
$id = $_GET['id'];
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT id, name, description FROM processes WHERE id = :id");
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$process = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($process) {
|
||||
$stmt_steps = $pdo->prepare("SELECT id, title, description, step_order FROM process_steps WHERE process_id = :process_id ORDER BY step_order ASC");
|
||||
$stmt_steps->bindParam(':process_id', $id, PDO::PARAM_INT);
|
||||
$stmt_steps->execute();
|
||||
$process['steps'] = $stmt_steps->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$error = "Process not found.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log("DB Error: " . $e->getMessage());
|
||||
$error = "Could not retrieve process details.";
|
||||
}
|
||||
} else {
|
||||
$error = "No process ID provided.";
|
||||
}
|
||||
|
||||
$project_name = htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'ProcessFlow Optimizer');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Process - <?php echo $project_name; ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1 class="h3 mb-0"><?php echo $project_name; ?></h1>
|
||||
<p class="text-muted mb-0">A business process analyzer and automated optimizer</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<?php echo $error; ?>
|
||||
</div>
|
||||
<a href="index.php" class="btn btn-primary">Go Back</a>
|
||||
<?php elseif ($process): ?>
|
||||
<h2 class="h4 card-title fw-bold">Edit Process: <?php echo htmlspecialchars($process['name']); ?></h2>
|
||||
<p class="card-subtitle mb-4 text-muted">Modify the details of your process.</p>
|
||||
<form action="update_process.php" method="POST">
|
||||
<input type="hidden" name="id" value="<?php echo $process['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Process Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($process['name']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="4" required><?php echo htmlspecialchars($process['description']); ?></textarea>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
<h5 class="fw-bold mb-3">Process Steps</h5>
|
||||
<div id="processStepsContainer">
|
||||
<?php if (!empty($process['steps'])): ?>
|
||||
<?php foreach ($process['steps'] as $index => $step): ?>
|
||||
<div class="mb-3 p-3 border rounded bg-light position-relative process-step-item">
|
||||
<input type="hidden" name="steps[<?php echo $index; ?>][id]" value="<?php echo htmlspecialchars($step['id']); ?>">
|
||||
<div class="mb-2">
|
||||
<label for="stepTitle_<?php echo $index; ?>" class="form-label fw-bold">Step <?php echo $index + 1; ?> Title</label>
|
||||
<input type="text" class="form-control" id="stepTitle_<?php echo $index; ?>" name="steps[<?php echo $index; ?>][title]" value="<?php echo htmlspecialchars($step['title']); ?>" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="stepDescription_<?php echo $index; ?>" class="form-label">Step <?php echo $index + 1; ?> Description</label>
|
||||
<textarea class="form-control" id="stepDescription_<?php echo $index; ?>" name="steps[<?php echo $index; ?>][description]" rows="2" required><?php echo htmlspecialchars($step['description']); ?></textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-danger position-absolute top-0 end-0 mt-2 me-2 remove-step-btn" title="Remove Step">
|
||||
<i data-feather="x" style="width: 16px; height: 16px;"></i>
|
||||
</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="button" id="addStepBtn" class="btn btn-outline-secondary btn-sm mb-4">
|
||||
<i data-feather="plus" style="width: 16px; height: 16px;"></i> Add New Step
|
||||
</button>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update Process</button>
|
||||
<a href="index.php" class="btn btn-link">Cancel</a>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="text-center py-4 text-muted">
|
||||
<p>© <?php echo date("Y"); ?> <?php echo $project_name; ?>. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
feather.replace()
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const processStepsContainer = document.getElementById('processStepsContainer');
|
||||
const addStepBtn = document.getElementById('addStepBtn');
|
||||
|
||||
let stepIndex = processStepsContainer.children.length; // Start index after existing steps
|
||||
|
||||
function updateStepNumbers() {
|
||||
Array.from(processStepsContainer.children).forEach((stepItem, index) => {
|
||||
stepItem.querySelector('label[for^="stepTitle_"]').textContent = `Step ${index + 1} Title`;
|
||||
stepItem.querySelector('label[for^="stepDescription_"]').textContent = `Step ${index + 1} Description`;
|
||||
|
||||
// Update name attributes for proper form submission
|
||||
stepItem.querySelector('input[name$="[title]"]').name = `steps[${index}][title]`;
|
||||
stepItem.querySelector('textarea[name$="[description]"]').name = `steps[${index}][description]`;
|
||||
const hiddenIdInput = stepItem.querySelector('input[name$="[id]"]');
|
||||
if (hiddenIdInput) {
|
||||
hiddenIdInput.name = `steps[${index}][id]`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addStepBtn.addEventListener('click', function() {
|
||||
const newStepHtml = `
|
||||
<div class="mb-3 p-3 border rounded bg-light position-relative process-step-item">
|
||||
<div class="mb-2">
|
||||
<label for="stepTitle_${stepIndex}" class="form-label fw-bold">Step ${stepIndex + 1} Title</label>
|
||||
<input type="text" class="form-control" id="stepTitle_${stepIndex}" name="steps[${stepIndex}][title]" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="stepDescription_${stepIndex}" class="form-label">Step ${stepIndex + 1} Description</label>
|
||||
<textarea class="form-control" id="stepDescription_${stepIndex}" name="steps[${stepIndex}][description]" rows="2" required></textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-danger position-absolute top-0 end-0 mt-2 me-2 remove-step-btn" title="Remove Step">
|
||||
<i data-feather="x" style="width: 16px; height: 16px;"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
processStepsContainer.insertAdjacentHTML('beforeend', newStepHtml);
|
||||
feather.replace(); // Re-render feather icons for new button
|
||||
stepIndex++;
|
||||
updateStepNumbers();
|
||||
});
|
||||
|
||||
processStepsContainer.addEventListener('click', function(event) {
|
||||
if (event.target.closest('.remove-step-btn')) {
|
||||
event.target.closest('.process-step-item').remove();
|
||||
updateStepNumbers();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
541
index.php
541
index.php
@ -1,407 +1,150 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
|
||||
// Fetch all processes from the database
|
||||
// Pagination settings
|
||||
$items_per_page = 5;
|
||||
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
$offset = ($current_page - 1) * $items_per_page;
|
||||
|
||||
$processes = [];
|
||||
$search_query = $_GET['search'] ?? '';
|
||||
$total_processes = 0;
|
||||
|
||||
// Sorting settings
|
||||
$allowed_sort_columns = ['name', 'created_at'];
|
||||
$sort_by = $_GET['sort_by'] ?? 'created_at';
|
||||
$sort_order = $_GET['sort_order'] ?? 'DESC';
|
||||
|
||||
// Validate sort_by column
|
||||
if (!in_array($sort_by, $allowed_sort_columns)) {
|
||||
$sort_by = 'created_at'; // Default to created_at if invalid
|
||||
}
|
||||
|
||||
// Validate sort_order
|
||||
$sort_order = strtoupper($sort_order);
|
||||
if (!in_array($sort_order, ['ASC', 'DESC'])) {
|
||||
$sort_order = 'DESC'; // Default to DESC if invalid
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// First, get the total number of processes (with search filter if applied)
|
||||
$count_sql = "SELECT COUNT(*) FROM processes";
|
||||
if (!empty($search_query)) {
|
||||
$count_sql .= " WHERE name LIKE :search_query OR description LIKE :search_query";
|
||||
}
|
||||
$count_stmt = $pdo->prepare($count_sql);
|
||||
if (!empty($search_query)) {
|
||||
$count_stmt->bindValue(':search_query', '%' . $search_query . '%', PDO::PARAM_STR);
|
||||
}
|
||||
$count_stmt->execute();
|
||||
$total_processes = $count_stmt->fetchColumn();
|
||||
|
||||
// Then, fetch the processes for the current page (with search filter and pagination)
|
||||
$sql = "SELECT id, name, description, created_at FROM processes";
|
||||
if (!empty($search_query)) {
|
||||
$sql .= " WHERE name LIKE :search_query OR description LIKE :search_query";
|
||||
}
|
||||
$sql .= " ORDER BY ".$sort_by." ".$sort_order." LIMIT :limit OFFSET :offset";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
if (!empty($search_query)) {
|
||||
$stmt->bindValue(':search_query', '%' . $search_query . '%', PDO::PARAM_STR);
|
||||
}
|
||||
$stmt->bindValue(':limit', $items_per_page, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$processes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Silently log error, or display a friendly message.
|
||||
error_log("DB Error: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$total_pages = ceil($total_processes / $items_per_page);
|
||||
|
||||
|
||||
$project_name = htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'ProcessFlow Optimizer');
|
||||
$project_description = htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'Analyze and optimize your business processes.');
|
||||
$project_image_url = htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '');
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo $project_name; ?></title>
|
||||
<meta name="description" content="<?php echo $project_description; ?>">
|
||||
<!-- Open Graph / Twitter Meta Tags -->
|
||||
<meta property="og:title" content="<?php echo $project_name; ?>">
|
||||
<meta property="og:description" content="<?php echo $project_description; ?>">
|
||||
<meta property="og:image" content="<?php echo $project_image_url; ?>">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="<?php echo $project_name; ?>">
|
||||
<meta name="twitter:description" content="<?php echo $project_description; ?>">
|
||||
<meta name="twitter:image" content="<?php echo $project_image_url; ?>">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
<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>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div id="message-area" class="container mt-4">
|
||||
<?php
|
||||
if (isset($_GET['success'])) {
|
||||
$message = '';
|
||||
switch ($_GET['success']) {
|
||||
case 'processadded':
|
||||
$message = 'Process added successfully!';
|
||||
break;
|
||||
case 'processupdated':
|
||||
$message = 'Process updated successfully!';
|
||||
break;
|
||||
case 'processdeleted':
|
||||
$message = 'Process deleted successfully!';
|
||||
break;
|
||||
default:
|
||||
$message = 'Action successful!';
|
||||
}
|
||||
echo '<div class="alert alert-success alert-dismissible fade show" role="alert">' . $message . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
}
|
||||
|
||||
if (isset($_GET['error'])) {
|
||||
$message = '';
|
||||
switch ($_GET['error']) {
|
||||
case 'emptyfields':
|
||||
$message = 'Please fill in all fields.';
|
||||
break;
|
||||
case 'dberror':
|
||||
$message = 'A database error occurred. Please try again.';
|
||||
break;
|
||||
case 'deletionfailed':
|
||||
$message = 'Failed to delete process. Please try again.';
|
||||
break;
|
||||
case 'invalidrequest':
|
||||
$message = 'Invalid request.';
|
||||
break;
|
||||
case 'processnotfound':
|
||||
$message = 'Process not found.';
|
||||
break;
|
||||
default:
|
||||
$message = 'An error occurred. Please try again.';
|
||||
}
|
||||
echo '<div class="alert alert-danger alert-dismissible fade show" role="alert">' . $message . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
}
|
||||
?>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1 class="h3 mb-0"><?php echo $project_name; ?></h1>
|
||||
<p class="text-muted mb-0">A business process analyzer and automated optimizer</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container py-5">
|
||||
<div class="row g-5">
|
||||
<!-- Left Column: Form -->
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="h4 card-title fw-bold">Map a New Process</h2>
|
||||
<p class="card-subtitle mb-4 text-muted">Define a process to begin analysis.</p>
|
||||
<form action="add_process.php" method="POST" id="addProcessForm">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Process Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="4" required></textarea>
|
||||
</div>
|
||||
<div id="aiGeneratedSteps" class="mb-3 border p-3 rounded" style="display:none;">
|
||||
<h6>AI Suggested Steps:</h6>
|
||||
<div id="stepsContainer"></div>
|
||||
</div>
|
||||
<div class="mb-3 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="aiSuggestBtn">
|
||||
<span id="aiSuggestSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
Suggest with AI
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Add Process</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Process List -->
|
||||
<div class="col-lg-7">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="h4 card-title fw-bold">Existing Processes</h2>
|
||||
<form action="index.php" method="GET" class="mb-4">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search by name or description" name="search" value="<?php echo htmlspecialchars($_GET['search'] ?? ''); ?>">
|
||||
<button class="btn btn-outline-secondary" type="submit" id="button-search">Search</button>
|
||||
<?php if (isset($_GET['search'])): ?>
|
||||
<a href="index.php" class="btn btn-outline-secondary" type="button">Clear</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
<?php if (empty($processes)): ?>
|
||||
<div class="text-center py-5">
|
||||
<i data-feather="search" class="text-muted mb-3" style="width: 48px; height: 48px;"></i>
|
||||
<p class="text-muted">No processes defined yet. <br> Use the form to add your first process.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item px-0 d-flex justify-content-between align-items-center bg-light fw-bold">
|
||||
<div class="col-4">
|
||||
<a href="?<?php echo http_build_query(array_merge($_GET, ['sort_by' => 'name', 'sort_order' => ($sort_by == 'name' && $sort_order == 'ASC') ? 'DESC' : 'ASC'])); ?>" class="text-decoration-none text-dark">
|
||||
Process Name
|
||||
<?php if ($sort_by == 'name'): ?>
|
||||
<i data-feather="<?php echo ($sort_order == 'ASC') ? 'arrow-up' : 'arrow-down'; ?>" style="width: 14px; height: 14px;"></i>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-5">Description</div>
|
||||
<div class="col-3 text-end">
|
||||
<a href="?<?php echo http_build_query(array_merge($_GET, ['sort_by' => 'created_at', 'sort_order' => ($sort_by == 'created_at' && $sort_order == 'ASC') ? 'DESC' : 'ASC'])); ?>" class="text-decoration-none text-dark">
|
||||
Created At
|
||||
<?php if ($sort_by == 'created_at'): ?>
|
||||
<i data-feather="<?php echo ($sort_order == 'ASC') ? 'arrow-up' : 'arrow-down'; ?>" style="width: 14px; height: 14px;"></i>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php foreach ($processes as $process): ?>
|
||||
<div class="list-group-item px-0">
|
||||
<div class="d-flex w-100 justify-content-between align-items-start">
|
||||
<div>
|
||||
<h5 class="mb-1 fw-bold"><?php echo htmlspecialchars($process['name']); ?></h5>
|
||||
<small class="text-muted"><?php echo date("M d, Y", strtotime($process['created_at'])); ?></small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="view_process.php?id=<?php echo $process['id']; ?>" class="btn btn-sm btn-outline-info me-2" title="View Process">
|
||||
<i data-feather="eye" style="width: 16px; height: 16px;"></i>
|
||||
</a>
|
||||
<form action="edit_process.php" method="GET" class="me-2">
|
||||
<input type="hidden" name="id" value="<?php echo $process['id']; ?>">
|
||||
<button type="submit" class="btn btn-sm btn-outline-primary" title="Edit Process">
|
||||
<i data-feather="edit" style="width: 16px; height: 16px;"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form id="deleteForm-<?php echo $process['id']; ?>" action="delete_process.php" method="POST" style="display:none;">
|
||||
<input type="hidden" name="id" value="<?php echo $process['id']; ?>">
|
||||
</form>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" title="Delete Process" data-bs-toggle="modal" data-bs-target="#confirmDeleteModal" data-bs-id="<?php echo $process['id']; ?>">
|
||||
<i data-feather="trash-2" style="width: 16px; height: 16px;"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-1 text-muted"><?php echo htmlspecialchars($process['description']); ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if ($total_pages > 1): ?>
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination justify-content-center mt-4">
|
||||
<li class="page-item <?php echo ($current_page <= 1) ? 'disabled' : ''; ?>">
|
||||
<a class="page-link" href="?<?php echo http_build_query(array_merge($_GET, ['page' => $current_page - 1])); ?>" tabindex="-1" aria-disabled="true">Previous</a>
|
||||
</li>
|
||||
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
|
||||
<li class="page-item <?php echo ($current_page == $i) ? 'active' : ''; ?>">
|
||||
<a class="page-link" href="?<?php echo http_build_query(array_merge($_GET, ['page' => $i])); ?>"><?php echo $i; ?></a>
|
||||
</li>
|
||||
<?php endfor; ?>
|
||||
<li class="page-item <?php echo ($current_page >= $total_pages) ? 'disabled' : ''; ?>">
|
||||
<a class="page-link" href="?<?php echo http_build_query(array_merge($_GET, ['page' => $current_page + 1])); ?>">Next</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="text-center py-4 text-muted">
|
||||
<p>© <?php echo date("Y"); ?> <?php echo $project_name; ?>. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="confirmDeleteModalLabel">Confirm Deletion</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to delete this process? This action cannot be undone.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function htmlspecialchars(str) {
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
''': '''
|
||||
};
|
||||
return str.replace(/[&<>'"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
// Client-side validation for the Add Process form
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var form = document.getElementById('addProcessForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
feather.replace()
|
||||
|
||||
// AI Suggestion Feature
|
||||
const aiSuggestBtn = document.getElementById('aiSuggestBtn');
|
||||
const aiSuggestSpinner = document.getElementById('aiSuggestSpinner');
|
||||
const processNameInput = document.getElementById('name');
|
||||
const processDescriptionInput = document.getElementById('description');
|
||||
const messageArea = document.getElementById('message-area');
|
||||
|
||||
if (aiSuggestBtn) {
|
||||
aiSuggestBtn.addEventListener('click', async () => {
|
||||
aiSuggestBtn.disabled = true;
|
||||
aiSuggestSpinner.classList.remove('d-none');
|
||||
messageArea.innerHTML = ''; // Clear previous messages
|
||||
|
||||
const keyword = processNameInput.value.trim();
|
||||
const queryParam = keyword ? `?keyword=${encodeURIComponent(keyword)}` : '';
|
||||
|
||||
try {
|
||||
const response = await fetch(`api/generate_process_suggestion.php${queryParam}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
processNameInput.value = data.data.name;
|
||||
processDescriptionInput.value = data.data.description;
|
||||
|
||||
const aiGeneratedStepsDiv = document.getElementById('aiGeneratedSteps');
|
||||
const stepsContainer = document.getElementById('stepsContainer');
|
||||
stepsContainer.innerHTML = ''; // Clear previous steps
|
||||
|
||||
if (data.data.steps && data.data.steps.length > 0) {
|
||||
aiGeneratedStepsDiv.style.display = 'block';
|
||||
data.data.steps.forEach((step, index) => {
|
||||
const stepHtml = `
|
||||
<div class="mb-3 p-2 border rounded bg-light">
|
||||
<div class="mb-2">
|
||||
<label for="stepTitle_${index}" class="form-label fw-bold">Step ${index + 1} Title</label>
|
||||
<input type="text" class="form-control" id="stepTitle_${index}" name="steps[${index}][title]" value="${htmlspecialchars(step.title)}" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="stepDescription_${index}" class="form-label">Step ${index + 1} Description</label>
|
||||
<textarea class="form-control" id="stepDescription_${index}" name="steps[${index}][description]" rows="2" required>${htmlspecialchars(step.description)}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
stepsContainer.insertAdjacentHTML('beforeend', stepHtml);
|
||||
});
|
||||
} else {
|
||||
aiGeneratedStepsDiv.style.display = 'none';
|
||||
}
|
||||
|
||||
messageArea.innerHTML = '<div class="alert alert-success alert-dismissible fade show" role="alert">AI suggestion generated successfully!<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
} else {
|
||||
messageArea.innerHTML = `<div class="alert alert-danger alert-dismissible fade show" role="alert">Failed to get AI suggestion: ${data.error}<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching AI suggestion:', error);
|
||||
messageArea.innerHTML = '<div class="alert alert-danger alert-dismissible fade show" role="alert">An error occurred while fetching AI suggestion. Please try again.<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
} finally {
|
||||
aiSuggestBtn.disabled = false;
|
||||
aiSuggestSpinner.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
// JavaScript for handling the delete confirmation modal
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var confirmDeleteModal = document.getElementById('confirmDeleteModal');
|
||||
var confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
|
||||
|
||||
confirmDeleteModal.addEventListener('show.bs.modal', function (event) {
|
||||
// Button that triggered the modal
|
||||
var button = event.relatedTarget;
|
||||
// Extract info from data-bs-id attributes
|
||||
var processId = button.getAttribute('data-bs-id');
|
||||
|
||||
// Update the modal's delete button to point to the correct delete_process.php with the processId
|
||||
confirmDeleteBtn.onclick = function() {
|
||||
document.getElementById('deleteForm-' + processId).submit();
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$id = $_POST['id'] ?? null;
|
||||
$name = $_POST['name'] ?? null;
|
||||
$description = $_POST['description'] ?? null;
|
||||
|
||||
if ($id && $name && $description) {
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("UPDATE processes SET name = :name, description = :description WHERE id = :id");
|
||||
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||
$stmt->bindParam(':description', $description, PDO::PARAM_STR);
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
// Handle process steps
|
||||
$submitted_steps = $_POST['steps'] ?? [];
|
||||
$existing_step_ids = [];
|
||||
|
||||
// Fetch existing steps to identify deletions
|
||||
$stmt_fetch_existing = $pdo->prepare("SELECT id FROM process_steps WHERE process_id = :process_id");
|
||||
$stmt_fetch_existing->bindParam(':process_id', $id, PDO::PARAM_INT);
|
||||
$stmt_fetch_existing->execute();
|
||||
$db_existing_step_ids = $stmt_fetch_existing->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$steps_to_keep_ids = [];
|
||||
|
||||
foreach ($submitted_steps as $order => $step) {
|
||||
$step_id = $step['id'] ?? null;
|
||||
$step_title = $step['title'] ?? '';
|
||||
$step_description = $step['description'] ?? '';
|
||||
|
||||
if (!empty($step_title) && !empty($step_description)) {
|
||||
if ($step_id) {
|
||||
// Update existing step
|
||||
$stmt_update_step = $pdo->prepare("UPDATE process_steps SET title = :title, description = :description, step_order = :step_order WHERE id = :id AND process_id = :process_id");
|
||||
$stmt_update_step->bindParam(':title', $step_title, PDO::PARAM_STR);
|
||||
$stmt_update_step->bindParam(':description', $step_description, PDO::PARAM_STR);
|
||||
$stmt_update_step->bindParam(':step_order', $order, PDO::PARAM_INT);
|
||||
$stmt_update_step->bindParam(':id', $step_id, PDO::PARAM_INT);
|
||||
$stmt_update_step->bindParam(':process_id', $id, PDO::PARAM_INT);
|
||||
$stmt_update_step->execute();
|
||||
$steps_to_keep_ids[] = $step_id;
|
||||
} else {
|
||||
// Insert new step
|
||||
$stmt_insert_step = $pdo->prepare("INSERT INTO process_steps (process_id, title, description, step_order) VALUES (:process_id, :title, :description, :step_order)");
|
||||
$stmt_insert_step->bindParam(':process_id', $id, PDO::PARAM_INT);
|
||||
$stmt_insert_step->bindParam(':title', $step_title, PDO::PARAM_STR);
|
||||
$stmt_insert_step->bindParam(':description', $step_description, PDO::PARAM_STR);
|
||||
$stmt_insert_step->bindParam(':step_order', $order, PDO::PARAM_INT);
|
||||
$stmt_insert_step->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete steps that were removed from the form
|
||||
$steps_to_delete = array_diff($db_existing_step_ids, $steps_to_keep_ids);
|
||||
if (!empty($steps_to_delete)) {
|
||||
$placeholders = implode(',', array_fill(0, count($steps_to_delete), '?'));
|
||||
$stmt_delete_steps = $pdo->prepare("DELETE FROM process_steps WHERE process_id = ? AND id IN ($placeholders)");
|
||||
$stmt_delete_steps->execute(array_merge([$id], $steps_to_delete));
|
||||
}
|
||||
header('Location: index.php?success=processupdated');
|
||||
exit();
|
||||
} else {
|
||||
header('Location: index.php?error=updatefailed');
|
||||
exit();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log("DB Error: " . $e->getMessage());
|
||||
header('Location: index.php?error=dberror');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php?error=emptyfields');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php?error=invalidrequest');
|
||||
exit();
|
||||
}
|
||||
181
view_process.php
181
view_process.php
@ -1,181 +0,0 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
$process = null;
|
||||
$error_message = '';
|
||||
|
||||
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
|
||||
$id = (int)$_GET['id'];
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT id, name, description, created_at FROM processes WHERE id = :id");
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$process = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($process) {
|
||||
$stmt_steps = $pdo->prepare("SELECT title, description FROM process_steps WHERE process_id = :process_id ORDER BY step_order ASC");
|
||||
$stmt_steps->bindParam(':process_id', $id, PDO::PARAM_INT);
|
||||
$stmt_steps->execute();
|
||||
$process['steps'] = $stmt_steps->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$error_message = 'Process not found.';
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("DB Error: " . $e->getMessage());
|
||||
$error_message = 'A database error occurred. Please try again later.';
|
||||
}
|
||||
} else {
|
||||
$error_message = 'Invalid process ID.';
|
||||
}
|
||||
|
||||
$page_title = $process ? htmlspecialchars($process['name']) . ' Details' : 'Process Details';
|
||||
$page_description = $process ? 'Details for process: ' . htmlspecialchars($process['name']) . '. ' . htmlspecialchars($process['description']) : 'View details of a specific process.';
|
||||
|
||||
$project_name = htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'ProcessFlow Optimizer');
|
||||
$project_description = htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'Analyze and optimize your business processes.');
|
||||
$project_image_url = htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo $page_title; ?></title>
|
||||
<meta name="description" content="<?php echo $page_description; ?>">
|
||||
<!-- Open Graph / Twitter Meta Tags -->
|
||||
<meta property="og:title" content="<?php echo $page_title; ?>">
|
||||
<meta property="og:description" content="<?php echo $page_description; ?>">
|
||||
<meta property="og:image" content="<?php echo $project_image_url; ?>">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="<?php echo $page_title; ?>">
|
||||
<meta name="twitter:description" content="<?php echo $page_description; ?>">
|
||||
<meta name="twitter:image" content="<?php echo $project_image_url; ?>">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://unpkg.com/feather-icons"></script>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1 class="h3 mb-0"><?php echo $project_name; ?></h1>
|
||||
<p class="text-muted mb-0">A business process analyzer and automated optimizer</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container py-5">
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2 class="h4 card-title fw-bold mb-0"><?php echo $page_title; ?></h2>
|
||||
<a href="index.php" class="btn btn-outline-secondary btn-sm">
|
||||
<i data-feather="arrow-left" style="width: 16px; height: 16px;"></i> Back to List
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<?php echo htmlspecialchars($error_message); ?>
|
||||
</div>
|
||||
<?php elseif ($process): ?>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">ID:</dt>
|
||||
<dd class="col-sm-9"><?php echo htmlspecialchars($process['id']); ?></dd>
|
||||
|
||||
<dt class="col-sm-3">Name:</dt>
|
||||
<dd class="col-sm-9"><?php echo htmlspecialchars($process['name']); ?></dd>
|
||||
|
||||
<dt class="col-sm-3">Description:</dt>
|
||||
<dd class="col-sm-9"><?php echo nl2br(htmlspecialchars($process['description'])); ?></dd>
|
||||
|
||||
<dt class="col-sm-3">Created At:</dt>
|
||||
<dd class="col-sm-9"><?php echo date("M d, Y H:i:s", strtotime($process['created_at'])); ?></dd>
|
||||
</dl>
|
||||
|
||||
<?php if (!empty($process['steps'])): ?>
|
||||
<h5 class="mt-4">Process Steps:</h5>
|
||||
<ul class="list-group mb-4">
|
||||
<?php foreach ($process['steps'] as $index => $step): ?>
|
||||
<li class="list-group-item">
|
||||
<h6 class="mb-1">Step <?php echo $index + 1; ?>: <?php echo htmlspecialchars($step['title']); ?></h6>
|
||||
<p class="mb-0 text-muted"><?php echo nl2br(htmlspecialchars($step['description'])); ?></p>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mt-4 text-end">
|
||||
<a href="edit_process.php?id=<?php echo $process['id']; ?>" class="btn btn-primary me-2">
|
||||
<i data-feather="edit" style="width: 16px; height: 16px;"></i> Edit Process
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#confirmDeleteModal" data-bs-id="<?php echo $process['id']; ?>">
|
||||
<i data-feather="trash-2" style="width: 16px; height: 16px;"></i> Delete Process
|
||||
</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="text-center py-4 text-muted">
|
||||
<p>© <?php echo date("Y"); ?> <?php echo $project_name; ?>. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<!-- Delete Confirmation Modal (identical to index.php for consistency) -->
|
||||
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="confirmDeleteModalLabel">Confirm Deletion</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to delete this process? This action cannot be undone.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
feather.replace()
|
||||
|
||||
// JavaScript for handling the delete confirmation modal
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var confirmDeleteModal = document.getElementById('confirmDeleteModal');
|
||||
var confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
|
||||
|
||||
confirmDeleteModal.addEventListener('show.bs.modal', function (event) {
|
||||
// Button that triggered the modal
|
||||
var button = event.relatedTarget;
|
||||
// Extract info from data-bs-id attributes
|
||||
var processId = button.getAttribute('data-bs-id');
|
||||
|
||||
// Update the modal's delete button to point to the correct delete_process.php with the processId
|
||||
confirmDeleteBtn.onclick = function() {
|
||||
// Create a form dynamically to submit POST request
|
||||
var form = document.createElement('form');
|
||||
form.setAttribute('method', 'POST');
|
||||
form.setAttribute('action', 'delete_process.php');
|
||||
|
||||
var hiddenField = document.createElement('input');
|
||||
hiddenField.setAttribute('type', 'hidden');
|
||||
hiddenField.setAttribute('name', 'id');
|
||||
hiddenField.setAttribute('value', processId);
|
||||
|
||||
form.appendChild(hiddenField);
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user