344 lines
13 KiB
PHP
344 lines
13 KiB
PHP
<?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
|
|
|
|
// 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']],
|
|
];
|
|
|
|
$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>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<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="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>
|
|
|
|
<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>
|
|
|
|
<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>
|
|
|
|
<!-- 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>';
|
|
}
|
|
|
|
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>
|