Nov 19th, 2025 - V.4
This commit is contained in:
parent
ef6e573967
commit
9fff459bbd
@ -1,72 +1,24 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
// Mock data for demonstration, this would come from the database
|
||||
$workflow_id = $_GET['id'] ?? 1;
|
||||
$workflow_name = "Smart Candidate Intake"; // Fetched from DB based on $workflow_id
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_logged_in()) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
// Mock actions for this workflow
|
||||
$actions = [
|
||||
['type' => 'Create Task', 'params' => ['name' => 'Review new candidate', 'assign_to' => 'hiring_manager']],
|
||||
['type' => 'Send Email', 'params' => ['subject' => 'Welcome!', 'to' => '{candidate_email}']],
|
||||
['type' => 'AI Analysis', 'params' => ['prompt' => 'Summarize candidate resume']],
|
||||
];
|
||||
|
||||
if (!hasPermission('view_workflows')) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'db/config.php';
|
||||
|
||||
if (!isset($_GET['workflow_id'])) {
|
||||
header('Location: workflows.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$workflow_id = $_GET['workflow_id'];
|
||||
$pdo = db();
|
||||
|
||||
// Fetch workflow details
|
||||
$stmt = $pdo->prepare("SELECT * FROM workflows WHERE id = ?");
|
||||
$stmt->execute([$workflow_id]);
|
||||
$workflow = $stmt->fetch();
|
||||
|
||||
if (!$workflow) {
|
||||
header('Location: workflows.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle form submission for new action
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_action']) && hasPermission('manage_workflows')) {
|
||||
$action_type = $_POST['action_type'] ?? '';
|
||||
$config = [];
|
||||
|
||||
if ($action_type === 'send_email') {
|
||||
$config['to'] = $_POST['to'] ?? '';
|
||||
$config['subject'] = $_POST['subject'] ?? '';
|
||||
$config['message'] = $_POST['message'] ?? '';
|
||||
} elseif ($action_type === 'create_task') {
|
||||
$config['task_name'] = $_POST['task_name'] ?? '';
|
||||
$config['assign_to'] = $_POST['assign_to'] ?? '';
|
||||
} elseif ($action_type === 'send_slack_notification') {
|
||||
$config['webhook_url'] = $_POST['webhook_url'] ?? '';
|
||||
$config['message'] = $_POST['slack_message'] ?? '';
|
||||
} elseif ($action_type === 'update_candidate_status') {
|
||||
$config['new_status'] = $_POST['new_status'] ?? '';
|
||||
}
|
||||
|
||||
if (!empty($action_type)) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO workflow_actions (workflow_id, action_type, config) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$workflow_id, $action_type, json_encode($config)]);
|
||||
header("Location: " . $_SERVER['REQUEST_URI']);
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error adding action: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch actions for the workflow
|
||||
$stmt = $pdo->prepare("SELECT * FROM workflow_actions WHERE workflow_id = ? ORDER BY created_at DESC");
|
||||
$stmt->execute([$workflow_id]);
|
||||
$actions = $stmt->fetchAll();
|
||||
$available_actions = [
|
||||
['name' => 'Send Email', 'icon' => '📧'],
|
||||
['name' => 'Create Task', 'icon' => '✅'],
|
||||
['name' => 'Send Slack Notification', 'icon' => '💬'],
|
||||
['name' => 'Update Candidate Status', 'icon' => '👤'],
|
||||
['name' => 'AI Analysis', 'icon' => '🤖'],
|
||||
['name' => 'Add to Report', 'icon' => '📊'],
|
||||
['name' => 'Schedule Event', 'icon' => '📅'],
|
||||
];
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
@ -74,157 +26,319 @@ $actions = $stmt->fetchAll();
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Workflow Actions</title>
|
||||
<title>Workflow Builder - <?= htmlspecialchars($workflow_name) ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.workflow-builder-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.action-library {
|
||||
width: 250px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.action-library .card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.action-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.action-item:hover {
|
||||
background-color: #f1f3f5;
|
||||
border-color: #adb5bd;
|
||||
}
|
||||
.action-item-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.canvas {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.workflow-step {
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
position: relative;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||||
}
|
||||
.workflow-step .step-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.workflow-step .step-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
color: #0d6efd;
|
||||
}
|
||||
.workflow-step .step-description {
|
||||
color: #6c757d;
|
||||
margin-top: 5px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.step-connector {
|
||||
position: absolute;
|
||||
left: 35px;
|
||||
top: 100%;
|
||||
width: 2px;
|
||||
height: 15px;
|
||||
background-color: #dee2e6;
|
||||
}
|
||||
.start-node {
|
||||
border-color: #198754;
|
||||
}
|
||||
.start-node .step-icon {
|
||||
color: #198754;
|
||||
}
|
||||
.add-action-btn-container {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.add-action-btn-container .line {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -15px;
|
||||
width: 2px;
|
||||
height: 15px;
|
||||
background-color: #dee2e6;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.config-panel {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 400px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
box-shadow: -5px 0 15px rgba(0,0,0,0.1);
|
||||
z-index: 1050;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.config-panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.config-panel-header h4 {
|
||||
margin: 0;
|
||||
}
|
||||
.overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="chat.php" class="btn btn-outline-primary me-2">Chat</a>
|
||||
<a href="dashboard.php" class="btn btn-outline-primary me-2">Dashboard</a>
|
||||
<a href="workflows.php" class="btn btn-outline-primary me-3">Workflows</a>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<?php if (hasPermission('manage_roles')): ?>
|
||||
<li><a class="dropdown-item" href="roles.php">Manage Roles</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<?php endif; ?>
|
||||
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-fluid">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Actions for "<?php echo htmlspecialchars($workflow['name']); ?>"</h2>
|
||||
<?php if (hasPermission('manage_workflows')): ?>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addActionModal">Add Action</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action Type</th>
|
||||
<th>Configuration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($actions as $action): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($action['action_type']); ?></td>
|
||||
<td><pre><?php echo htmlspecialchars(json_encode(json_decode($action['config']), JSON_PRETTY_PRINT)); ?></pre></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Add Action Modal -->
|
||||
<div class="modal fade" id="addActionModal" tabindex="-1" aria-labelledby="addActionModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addActionModalLabel">Add New Action</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="add_action" value="1">
|
||||
<div class="mb-3">
|
||||
<label for="action_type" class="form-label">Action Type</label>
|
||||
<select class="form-select" id="action_type" name="action_type" required onchange="showActionConfig(this.value)">
|
||||
<option value="" disabled selected>Select an action</option>
|
||||
<option value="send_email">Send Email</option>
|
||||
<option value="create_task">Create Task</option>
|
||||
<option value="send_slack_notification">Send Slack Notification</option>
|
||||
<option value="update_candidate_status">Update Candidate Status</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="send_email_config" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="to" class="form-label">To</label>
|
||||
<input type="email" class="form-control" id="to" name="to">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="subject" class="form-label">Subject</label>
|
||||
<input type="text" class="form-control" id="subject" name="subject">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="create_task_config" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="task_name" class="form-label">Task Name</label>
|
||||
<input type="text" class="form-control" id="task_name" name="task_name">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="assign_to" class="form-label">Assign To</label>
|
||||
<input type="text" class="form-control" id="assign_to" name="assign_to" placeholder="Candidate ID or '{{candidate.id}}'">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="send_slack_notification_config" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="webhook_url" class="form-label">Slack Webhook URL</label>
|
||||
<input type="text" class="form-control" id="webhook_url" name="webhook_url">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="slack_message" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="slack_message" name="slack_message" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="update_candidate_status_config" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="new_status" class="form-label">New Status</label>
|
||||
<select class="form-select" id="new_status" name="new_status">
|
||||
<option value="Applied">Applied</option>
|
||||
<option value="Interviewing">Interviewing</option>
|
||||
<option value="Offered">Offered</option>
|
||||
<option value="Hired">Hired</option>
|
||||
<option value="Rejected">Rejected</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save Action</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">Actions for: <span class="fw-bold text-primary"><?= htmlspecialchars($workflow_name) ?></span></h1>
|
||||
<div>
|
||||
<button class="btn btn-success"><i class="fas fa-play me-2"></i>Test Workflow</button>
|
||||
<a href="workflows.php" class="btn btn-secondary"><i class="fas fa-arrow-left me-2"></i>Back to Workflows</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function showActionConfig(actionType) {
|
||||
document.getElementById('send_email_config').style.display = 'none';
|
||||
document.getElementById('create_task_config').style.display = 'none';
|
||||
document.getElementById('send_slack_notification_config').style.display = 'none';
|
||||
document.getElementById('update_candidate_status_config').style.display = 'none';
|
||||
<div class="workflow-builder-container">
|
||||
<!-- Action Library -->
|
||||
<div class="action-library">
|
||||
<div class="card">
|
||||
<div class="card-header fw-bold">
|
||||
Action Library
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php foreach ($available_actions as $action): ?>
|
||||
<div class="action-item" onclick="openConfigPanel('<?= htmlspecialchars($action['name']) ?>')">
|
||||
<span class="action-item-icon"><?= $action['icon'] ?></span>
|
||||
<span><?= htmlspecialchars($action['name']) ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (actionType) {
|
||||
document.getElementById(actionType + '_config').style.display = 'block';
|
||||
}
|
||||
<!-- Canvas -->
|
||||
<div class="canvas">
|
||||
<!-- Start Node -->
|
||||
<div class="workflow-step start-node">
|
||||
<div class="step-header">
|
||||
<span class="step-icon"><i class="fas fa-flag-checkered"></i></span>
|
||||
<span>Start Trigger</span>
|
||||
</div>
|
||||
<div class="step-description">When a candidate is created</div>
|
||||
<div class="step-connector"></div>
|
||||
</div>
|
||||
|
||||
<!-- Action Nodes -->
|
||||
<?php foreach ($actions as $index => $action): ?>
|
||||
<div class="workflow-step" onclick="openConfigPanel('<?= htmlspecialchars($action['type']) ?>', <?= htmlspecialchars(json_encode($action['params'])) ?>)">
|
||||
<div class="step-header">
|
||||
<span class="step-icon">
|
||||
<?php
|
||||
$icon = 'fas fa-question-circle';
|
||||
foreach ($available_actions as $avail_action) {
|
||||
if ($avail_action['name'] === $action['type']) {
|
||||
$icon = $avail_action['icon'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo $icon;
|
||||
?>
|
||||
</span>
|
||||
<span><?= htmlspecialchars($action['type']) ?></span>
|
||||
</div>
|
||||
<div class="step-description">
|
||||
<?php
|
||||
$desc = 'Configure this action';
|
||||
if ($action['type'] === 'Create Task') {
|
||||
$desc = 'Create task: "' . htmlspecialchars($action['params']['name'] ?? '') . '" for ' . htmlspecialchars($action['params']['assign_to'] ?? '');
|
||||
} elseif ($action['type'] === 'Send Email') {
|
||||
$desc = 'Send email with subject: "' . htmlspecialchars($action['params']['subject'] ?? '') . '"';
|
||||
} elseif ($action['type'] === 'AI Analysis') {
|
||||
$desc = 'Run AI analysis';
|
||||
}
|
||||
echo $desc;
|
||||
?>
|
||||
</div>
|
||||
<?php if ($index < count($actions)): ?>
|
||||
<div class="step-connector"></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<div class="add-action-btn-container">
|
||||
<div class="line"></div>
|
||||
<button class="btn btn-primary rounded-pill" onclick="showActionLibraryFocus()"><i class="fas fa-plus"></i> Add Action</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Panel -->
|
||||
<div class="overlay" onclick="closeConfigPanel()"></div>
|
||||
<div class="config-panel" id="config-panel">
|
||||
<!-- Content will be injected by JavaScript -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const configPanel = document.getElementById('config-panel');
|
||||
const overlay = document.querySelector('.overlay');
|
||||
const availableActions = <?= json_encode($available_actions) ?>;
|
||||
|
||||
function showActionLibraryFocus() {
|
||||
document.querySelector('.action-library .card').scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
document.querySelector('.action-library .card').classList.add('shadow-lg');
|
||||
setTimeout(() => {
|
||||
document.querySelector('.action-library .card').classList.remove('shadow-lg');
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function openConfigPanel(actionType, params = {}) {
|
||||
let formHtml = '';
|
||||
const action = availableActions.find(a => a.name === actionType);
|
||||
|
||||
formHtml += `
|
||||
<div class="config-panel-header">
|
||||
<h4>${action.icon} ${action.name}</h4>
|
||||
<button type="button" class="btn-close" onclick="closeConfigPanel()"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (actionType === 'Create Task') {
|
||||
formHtml += `
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Task Name</label>
|
||||
<input type="text" class="form-control" value="${params.name || ''}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Assign To</label>
|
||||
<select class="form-select">
|
||||
<option ${params.assign_to === 'hiring_manager' ? 'selected' : ''}>Hiring Manager</option>
|
||||
<option ${params.assign_to === 'recruiter' ? 'selected' : ''}>Recruiter</option>
|
||||
<option>Specific User...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Due Date</label>
|
||||
<input type="date" class="form-control" value="${params.due_date || ''}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Priority</label>
|
||||
<select class="form-select">
|
||||
<option>High</option>
|
||||
<option selected>Medium</option>
|
||||
<option>Low</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
} else if (actionType === 'Send Email') {
|
||||
formHtml += `
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">To</label>
|
||||
<input type="text" class="form-control" value="${params.to || '{candidate_email}'}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Subject</label>
|
||||
<input type="text" class="form-control" value="${params.subject || ''}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Body</label>
|
||||
<textarea class="form-control" rows="8"></textarea>
|
||||
<small class="form-text text-muted">Use variables like {candidate_name}, {role}, etc.</small>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
} else {
|
||||
formHtml += '<p>Configuration for this action is not yet implemented.</p>';
|
||||
}
|
||||
</script>
|
||||
|
||||
formHtml += `
|
||||
<div class="mt-4">
|
||||
<button class="btn btn-primary">Save Action</button> fostering a more intuitive and user-friendly experience.
|
||||
<button type="button" class="btn btn-danger ms-2">Delete</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
configPanel.innerHTML = formHtml;
|
||||
configPanel.style.display = 'block';
|
||||
overlay.style.display = 'block';
|
||||
}
|
||||
|
||||
function closeConfigPanel() {
|
||||
configPanel.style.display = 'none';
|
||||
overlay.style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
275
workflows.php
275
workflows.php
@ -8,6 +8,8 @@ if (!is_logged_in()) {
|
||||
}
|
||||
|
||||
if (!hasPermission('view_workflows')) {
|
||||
// Optionally, you can redirect to a generic page or show an error.
|
||||
// For now, redirecting to the main index page.
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
@ -16,6 +18,22 @@ require_once 'db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Handle form submission for adding a new workflow
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_workflow'])) {
|
||||
if (hasPermission('manage_workflows')) {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$trigger = $_POST['trigger'] ?? '';
|
||||
|
||||
if ($name && $trigger) {
|
||||
$stmt = $pdo->prepare("INSERT INTO workflows (name, trigger_event) VALUES (?, ?)");
|
||||
$stmt->execute([$name, $trigger]);
|
||||
$newWorkflowId = $pdo->lastInsertId();
|
||||
header("Location: workflow_actions.php?id=" . $newWorkflowId);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch workflows from the database
|
||||
$stmt = $pdo->query("SELECT * FROM workflows ORDER BY created_at DESC");
|
||||
$workflows = $stmt->fetchAll();
|
||||
@ -26,160 +44,129 @@ $workflows = $stmt->fetchAll();
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Workflows</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="assets/css/custom.css">
|
||||
<style>
|
||||
.workflow-card {
|
||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
.workflow-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.workflow-card .bi {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
.status-badge {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.3em 0.6em;
|
||||
}
|
||||
</style>
|
||||
<title>FinMox Workflows</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="chat.php" class="btn btn-outline-primary me-2">Chat</a>
|
||||
<a href="dashboard.php" class="btn btn-outline-primary me-2">Dashboard</a>
|
||||
<a href="workflows.php" class="btn btn-primary me-3">Workflows</a>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<?php echo htmlspecialchars($_SESSION['username']); ?>
|
||||
<body class="bg-gray-50">
|
||||
|
||||
<div id="workflows-page" class="min-h-screen p-8">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900">Your Automation Workflows</h1>
|
||||
<p class="text-gray-600 mt-1">Click any workflow to view and edit its actions</p>
|
||||
</div>
|
||||
<?php if (hasPermission('manage_workflows')): ?>
|
||||
<button onclick="openModal('addWorkflowModal')" class="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700 flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
|
||||
Create New Workflow
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<?php if (hasPermission('manage_roles')): ?>
|
||||
<li><a class="dropdown-item" href="roles.php">Manage Roles</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<?php endif; ?>
|
||||
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-fluid mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="h2">Your Automation Workflows</h2>
|
||||
<?php if (hasPermission('manage_workflows')): ?>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addWorkflowModal">Create New Workflow</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
|
||||
<!-- Smart Candidate Intake -->
|
||||
<div class="col">
|
||||
<div class="card h-100 workflow-card">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-inbox-fill text-primary"></i>
|
||||
<h5 class="card-title mt-3">Smart Candidate Intake</h5>
|
||||
<span class="badge bg-success status-badge">Active</span>
|
||||
<p class="card-text text-muted mt-2">"Automatically captures, enriches, and routes new candidates"</p>
|
||||
<p class="fw-bold">142 candidates processed this month</p>
|
||||
</div>
|
||||
<div class="card-footer bg-white text-center">
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
|
||||
<a href="#" class="btn btn-sm btn-outline-warning">Pause</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Interview Scheduling -->
|
||||
<div class="col">
|
||||
<div class="card h-100 workflow-card">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-calendar-check-fill text-success"></i>
|
||||
<h5 class="card-title mt-3">Interview Scheduling</h5>
|
||||
<p class="card-text text-muted mt-2">"Automates calendar checks and interview coordination"</p>
|
||||
<p class="fw-bold">28 interviews scheduled this month</p>
|
||||
</div>
|
||||
<div class="card-footer bg-white text-center">
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Onboarding Automation -->
|
||||
<div class="col">
|
||||
<div class="card h-100 workflow-card">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-person-check-fill text-info"></i>
|
||||
<h5 class="card-title mt-3">Onboarding Automation</h5>
|
||||
<p class="card-text text-muted mt-2">"Creates and tracks onboarding tasks automatically"</p>
|
||||
<p class="fw-bold">5 active onboardings</p>
|
||||
</div>
|
||||
<div class="card-footer bg-white text-center">
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Weekly Reports -->
|
||||
<div class="col">
|
||||
<div class="card h-100 workflow-card">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-bar-chart-line-fill text-warning"></i>
|
||||
<h5 class="card-title mt-3">Weekly Reports</h5>
|
||||
<p class="card-text text-muted mt-2">"AI-generated weekly summary for leadership"</p>
|
||||
<p class="fw-bold">Last report sent Friday 4pm</p>
|
||||
</div>
|
||||
<div class="card-footer bg-white text-center">
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Add Workflow Modal -->
|
||||
<div class="modal fade" id="addWorkflowModal" tabindex="-1" aria-labelledby="addWorkflowModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addWorkflowModalLabel">Create New Workflow</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="add_workflow" value="1">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
<!-- Workflow Cards Grid -->
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<?php if (empty($workflows)): ?>
|
||||
<!-- Static Example Cards when no workflows exist -->
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path></svg></div>
|
||||
<div><h3 class="text-lg font-bold text-gray-900">Smart Candidate Intake</h3></div>
|
||||
</div>
|
||||
<p class="text-gray-600">Automatically captures, enriches, and routes new candidates.</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="trigger" class="form-label">Trigger</label>
|
||||
<select class="form-select" id="trigger" name="trigger" required>
|
||||
<option value="" disabled selected>Select a trigger</option>
|
||||
<option value="candidate_created">New Candidate is Created</option>
|
||||
<option value="task_completed">Task is Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg></div>
|
||||
<div><h3 class="text-lg font-bold text-gray-900">Interview Scheduling</h3></div>
|
||||
</div>
|
||||
<p class="text-gray-600">Automates calendar checks and interview coordination.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save Workflow</button>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg></div>
|
||||
<div><h3 class="text-lg font-bold text-gray-900">Onboarding Automation</h3></div>
|
||||
</div>
|
||||
<p class="text-gray-600">Creates and tracks onboarding tasks for new hires.</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<?php foreach ($workflows as $workflow): ?>
|
||||
<a href="workflow_actions.php?id=<?php echo $workflow['id']; ?>" class="block bg-white rounded-xl shadow-sm border-2 border-gray-200 hover:border-blue-500 hover:shadow-lg transition-all">
|
||||
<div class="p-6">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h6m-6 4h6m-6 4h6"></path></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-gray-900"><?php echo htmlspecialchars($workflow['name']); ?></h3>
|
||||
<span class="inline-block px-2 py-1 bg-green-100 text-green-700 text-xs font-semibold rounded mt-1">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">
|
||||
Trigger: <strong><?php echo htmlspecialchars(str_replace('_', ' ', ucfirst($workflow['trigger_event']))); ?></strong>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Add Workflow Modal -->
|
||||
<div id="addWorkflowModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
|
||||
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-md bg-white">
|
||||
<div class="flex justify-between items-center pb-3">
|
||||
<p class="text-2xl font-bold">Create New Workflow</p>
|
||||
<div class="cursor-pointer z-50" onclick="closeModal('addWorkflowModal')">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="add_workflow" value="1">
|
||||
<div class="mb-4">
|
||||
<label for="name" class="block text-gray-700 text-sm font-bold mb-2">Workflow Name</label>
|
||||
<input type="text" id="name" name="name" required class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<label for="trigger" class="block text-gray-700 text-sm font-bold mb-2">Trigger</label>
|
||||
<select id="trigger" name="trigger" required class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
|
||||
<option value="" disabled selected>Select a trigger</option>
|
||||
<option value="candidate_created">New Candidate is Created</option>
|
||||
<option value="task_completed">Task is Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center justify-end">
|
||||
<button type="button" onclick="closeModal('addWorkflowModal')" class="bg-gray-200 text-gray-800 font-bold py-2 px-4 rounded hover:bg-gray-300 mr-2">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Save Workflow
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openModal(modalId) {
|
||||
document.getElementById(modalId).classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeModal(modalId) {
|
||||
document.getElementById(modalId).classList.add('hidden');
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user