This commit is contained in:
Flatlogic Bot 2026-01-02 12:15:48 +00:00
parent 3776a03a8f
commit 953eb569ce
7 changed files with 216 additions and 4 deletions

View File

@ -17,6 +17,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$sql = "INSERT INTO processes (name, description) VALUES (?, ?)"; $sql = "INSERT INTO processes (name, description) VALUES (?, ?)";
$stmt = $pdo->prepare($sql); $stmt = $pdo->prepare($sql);
$stmt->execute([$name, $description]); $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) { } catch (PDOException $e) {
// In a real app, log this error. For now, we'll just die. // In a real app, log this error. For now, we'll just die.
error_log("DB Error: " . $e->getMessage()); error_log("DB Error: " . $e->getMessage());

View File

@ -5,7 +5,7 @@ require_once __DIR__ . '/../ai/LocalAIApi.php';
$keyword = $_GET['keyword'] ?? 'new process'; $keyword = $_GET['keyword'] ?? 'new process';
$prompt = "Generate a creative and concise name and a short description for a process related to '" . htmlspecialchars($keyword) . "'. Respond in JSON format with 'name' and 'description' keys. Example: {"name": "Automated Workflow Orchestrator", "description": "Manages and streamlines complex business workflows."}"; $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 = [ $params = [
'input' => [ 'input' => [
@ -19,7 +19,7 @@ $response = LocalAIApi::createResponse($params);
if (!empty($response['success'])) { if (!empty($response['success'])) {
$decoded = LocalAIApi::decodeJsonFromResponse($response); $decoded = LocalAIApi::decodeJsonFromResponse($response);
if ($decoded && isset($decoded['name']) && isset($decoded['description'])) { if ($decoded && isset($decoded['name']) && isset($decoded['description']) && isset($decoded['steps'])) {
echo json_encode(['success' => true, 'data' => $decoded]); echo json_encode(['success' => true, 'data' => $decoded]);
} else { } else {
// Fallback if AI didn't return valid JSON or missing keys // Fallback if AI didn't return valid JSON or missing keys

View File

@ -0,0 +1,9 @@
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
);

View File

@ -13,7 +13,12 @@ if (isset($_GET['id'])) {
$stmt->execute(); $stmt->execute();
$process = $stmt->fetch(PDO::FETCH_ASSOC); $process = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$process) { 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."; $error = "Process not found.";
} }
} catch (PDOException $e) { } catch (PDOException $e) {
@ -68,6 +73,33 @@ $project_name = htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'ProcessFlow Optimi
<label for="description" class="form-label">Description</label> <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> <textarea class="form-control" id="description" name="description" rows="4" required><?php echo htmlspecialchars($process['description']); ?></textarea>
</div> </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> <button type="submit" class="btn btn-primary">Update Process</button>
<a href="index.php" class="btn btn-link">Cancel</a> <a href="index.php" class="btn btn-link">Cancel</a>
</form> </form>
@ -85,6 +117,57 @@ $project_name = htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'ProcessFlow Optimi
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
feather.replace() 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> </script>
</body> </body>
</html> </html>

View File

@ -161,6 +161,10 @@ $project_image_url = htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '');
<label for="description" class="form-label">Description</label> <label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="4" required></textarea> <textarea class="form-control" id="description" name="description" rows="4" required></textarea>
</div> </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"> <div class="mb-3 text-end">
<button type="button" class="btn btn-sm btn-outline-secondary" id="aiSuggestBtn"> <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> <span id="aiSuggestSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
@ -290,6 +294,17 @@ $project_image_url = htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '');
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
function htmlspecialchars(str) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
''': '&#039;'
};
return str.replace(/[&<>'"']/g, function(m) { return map[m]; });
}
// Client-side validation for the Add Process form // Client-side validation for the Add Process form
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var form = document.getElementById('addProcessForm'); var form = document.getElementById('addProcessForm');
@ -329,6 +344,32 @@ $project_image_url = htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '');
if (data.success) { if (data.success) {
processNameInput.value = data.data.name; processNameInput.value = data.data.name;
processDescriptionInput.value = data.data.description; 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>'; 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 { } 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>`; 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>`;

View File

@ -15,6 +15,53 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt->bindParam(':id', $id, PDO::PARAM_INT); $stmt->bindParam(':id', $id, PDO::PARAM_INT);
if ($stmt->execute()) { 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'); header('Location: index.php?success=processupdated');
exit(); exit();
} else { } else {

View File

@ -13,7 +13,12 @@ if (isset($_GET['id']) && is_numeric($_GET['id'])) {
$stmt->execute(); $stmt->execute();
$process = $stmt->fetch(PDO::FETCH_ASSOC); $process = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$process) { 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.'; $error_message = 'Process not found.';
} }
@ -88,6 +93,19 @@ $project_image_url = htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '');
<dt class="col-sm-3">Created At:</dt> <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> <dd class="col-sm-9"><?php echo date("M d, Y H:i:s", strtotime($process['created_at'])); ?></dd>
</dl> </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"> <div class="mt-4 text-end">
<a href="edit_process.php?id=<?php echo $process['id']; ?>" class="btn btn-primary me-2"> <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 <i data-feather="edit" style="width: 16px; height: 16px;"></i> Edit Process