kaauh_ats/templates/forms/form_builder.html
2025-10-07 13:39:44 +03:00

1807 lines
70 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ATS Form Builder - Vanilla JS</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Updated CSS styles with a new Teal/Aqua theme */
:root {
/* New Color Palette (Teal/Aqua/Deep Blue) */
--primary: #0081a7; /* Deep Teal/Cyan for main actions */
--primary-light: #00b4d8; /* Brighter Aqua/Cyan */
--secondary: #005a78; /* Darker Teal for hover/accent */
--success: #00cc99; /* Bright Greenish-Teal for success */
/* Neutral Colors (Kept for consistency) */
--light: #f4fcfc; /* Very light off-white (slightly blue tinted) */
--dark: #212529; /* Near black text */
--gray: #6c757d; /* Standard gray text */
--light-gray: #e0f0f4; /* Lighter background for hover/disabled */
--border: #c4d7e0; /* Lighter, softer border color */
/* Structural Variables (Kept exactly the same) */
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--radius: 8px;
--transition: all 0.3s ease;
}
/* All other structural and component styles below remain the same,
but will automatically adopt the new colors defined above. */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f7f9fb; /* Slightly adjusted body background to match new light tones */
color: var(--dark);
line-height: 1.6;
height: 100vh;
overflow: hidden;
}
.container {
display: flex;
height: 100vh;
}
/* Sidebar Styles */
.sidebar {
width: 280px;
background: white;
border-right: 1px solid var(--border);
padding: 20px;
overflow-y: auto;
box-shadow: var(--shadow);
z-index: 10;
display: flex;
flex-direction: column;
}
.sidebar-header {
padding-bottom: 20px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.sidebar-header h2 {
font-size: 1.5rem;
color: var(--primary);
display: flex;
align-items: center;
gap: 10px;
}
.field-categories {
flex: 1;
overflow-y: auto;
}
.field-category {
margin-bottom: 25px;
}
.category-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 12px;
color: var(--gray);
display: flex;
align-items: center;
gap: 8px;
}
.field-item {
background: var(--light);
border: 1px dashed var(--border);
border-radius: var(--radius);
padding: 12px 15px;
margin-bottom: 10px;
cursor: grab;
transition: var(--transition);
display: flex;
align-items: center;
gap: 10px;
}
.field-item:hover {
background: var(--light-gray);
transform: translateY(-2px);
border-color: var(--primary-light);
}
.field-item i {
color: var(--primary);
font-size: 1.2rem;
}
.field-item span {
font-weight: 500;
}
/* Main Content Styles */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
padding: 20px;
overflow: hidden;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.toolbar h1 {
font-size: 1.8rem;
color: var(--primary);
}
.btn {
padding: 10px 20px;
border-radius: var(--radius);
border: none;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-primary:hover {
background: var(--secondary);
}
.btn-outline {
background: transparent;
border: 1px solid var(--primary);
color: var(--primary);
}
.btn-outline:hover {
background: var(--primary);
color: white;
}
/* Stage Navigation - Fixed Height with Scroll */
.stage-nav-container {
height: 60px;
overflow-x: scroll;
overflow-y: hidden;
padding: 5px;
margin-bottom: 15px;
flex-shrink: 0;
background: white;
border-radius: var(--radius);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.stage-nav {
display: flex;
min-width: 100%;
padding: 0 10px;
white-space: nowrap;
}
.stage-tab {
padding: 12px 20px;
background: var(--light);
border: 1px solid var(--border);
border-radius: var(--radius) var(--radius) 0 0;
margin-right: 5px;
cursor: pointer;
white-space: nowrap;
transition: var(--transition);
position: relative;
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.stage-tab.active {
background: white;
border-bottom: 1px solid transparent;
z-index: 2;
}
.stage-tab:hover:not(.active) {
background: var(--light-gray);
}
.stage-tab .rename-btn {
opacity: 0;
transition: opacity 0.2s;
}
.stage-tab:hover .rename-btn {
opacity: 1;
}
.stage-tab .remove-btn {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
opacity: 0;
transition: opacity 0.2s;
}
.stage-tab:hover .remove-btn {
opacity: 1;
}
/* Form Builder Area */
.form-builder-container {
display: flex;
gap: 20px;
flex: 1;
overflow: hidden;
}
.form-builder {
background: white;
border-radius: var(--radius);
box-shadow: var(--shadow);
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.form-header {
padding: 20px;
border-bottom: 1px solid var(--border);
background: var(--light);
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.form-header h3 {
font-size: 1.3rem;
color: var(--primary);
display: flex;
align-items: center;
gap: 10px;
}
.rename-stage-btn {
background: none;
border: none;
color: var(--gray);
cursor: pointer;
padding: 5px;
border-radius: 4px;
transition: var(--transition);
}
.rename-stage-btn:hover {
background: var(--light-gray);
color: var(--primary);
}
.form-stage {
padding: 20px;
min-height: 300px;
flex: 1;
overflow-y: auto;
}
.form-field {
background: white;
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 15px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
position: relative;
transition: var(--transition);
cursor: pointer;
}
.form-field:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-color: var(--primary-light);
}
.form-field.selected {
border: 2px solid var(--primary);
box-shadow: 0 0 0 3px rgba(0, 129, 167, 0.2); /* Used a hardcoded rgba of the new primary color for the shadow */
}
.field-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.field-title {
font-weight: 600;
color: var(--dark);
display: flex;
align-items: center;
gap: 10px;
}
.required-indicator {
color: #e53935; /* Kept red for error/required status */
font-weight: bold;
}
.field-actions {
display: flex;
gap: 8px;
}
.action-btn {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: var(--light);
border: 1px solid var(--border);
cursor: pointer;
transition: var(--transition);
}
.action-btn:hover {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.field-content {
margin-top: 10px;
}
.field-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.field-input {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 1rem;
}
.field-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(0, 129, 167, 0.2); /* Used a hardcoded rgba of the new primary color for the shadow */
}
.field-options {
margin-top: 10px;
}
.option-item {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.option-item input[type="text"] {
flex: 1;
margin-left: 10px;
}
.add-option {
color: var(--primary);
cursor: pointer;
font-size: 0.9rem;
display: inline-flex;
align-items: center;
gap: 5px;
margin-top: 5px;
}
.add-option:hover {
text-decoration: underline;
}
/* File Upload Specific Styles */
.file-upload-area {
border: 2px dashed var(--border);
border-radius: var(--radius);
padding: 30px 20px;
text-align: center;
background: var(--light);
transition: var(--transition);
cursor: pointer;
}
.file-upload-area:hover {
border-color: var(--primary);
background: rgba(0, 129, 167, 0.05); /* Used a hardcoded rgba of the new primary color for the background */
}
.file-upload-icon {
font-size: 3rem;
color: var(--primary);
margin-bottom: 15px;
}
.file-upload-text {
margin-bottom: 15px;
}
.file-upload-text strong {
color: var(--primary);
}
.file-upload-info {
font-size: 0.9rem;
color: var(--gray);
margin-top: 10px;
}
.uploaded-file {
display: flex;
align-items: center;
justify-content: space-between;
background: white;
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 10px 15px;
margin-top: 15px;
}
.file-info {
display: flex;
align-items: center;
gap: 10px;
}
.file-icon {
color: var(--primary);
}
.file-name {
font-weight: 500;
}
.file-size {
font-size: 0.85rem;
color: var(--gray);
}
.remove-file-btn {
background: none;
border: none;
color: #e53935;
cursor: pointer;
font-size: 1.2rem;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: var(--transition);
}
.remove-file-btn:hover {
background: #ffebee;
}
/* Field Editor Panel */
.field-editor {
width: 320px;
background: white;
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 20px;
display: flex;
flex-direction: column;
transition: var(--transition);
flex-shrink: 0;
}
.editor-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.editor-header h3 {
font-size: 1.3rem;
color: var(--primary);
}
.editor-section {
margin-bottom: 20px;
}
.editor-section h4 {
font-size: 1.1rem;
margin-bottom: 12px;
color: var(--gray);
display: flex;
align-items: center;
gap: 8px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 6px;
font-weight: 500;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 1rem;
}
.form-control:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(0, 129, 167, 0.2); /* Used a hardcoded rgba of the new primary color for the shadow */
}
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
}
.checkbox-group input {
width: auto;
}
.options-list {
margin-top: 10px;
}
.option-input {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.option-input input {
flex: 1;
}
.remove-option {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: #ffebee; /* Kept light red for negative action */
color: #e53935; /* Kept red for negative action */
border: none;
cursor: pointer;
transition: var(--transition);
}
.remove-option:hover {
background: #e53935;
color: white;
}
/* Drag and Drop Styles */
.drag-over {
background-color: rgba(0, 129, 167, 0.1); /* Used a hardcoded rgba of the new primary color */
border: 2px dashed var(--primary);
}
.drag-ghost {
opacity: 0.5;
}
/* Empty State */
.empty-state {
text-align: center;
padding: 40px 20px;
color: var(--gray);
}
.empty-state i {
font-size: 3rem;
margin-bottom: 15px;
color: var(--light-gray);
}
/* Stage Rename Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background: white;
border-radius: var(--radius);
padding: 25px;
width: 400px;
max-width: 90%;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
transform: scale(0.9);
transition: transform 0.3s ease;
}
.modal-overlay.active .modal {
transform: scale(1);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-header h3 {
font-size: 1.4rem;
color: var(--primary);
}
.close-modal {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--gray);
transition: var(--transition);
}
.close-modal:hover {
color: var(--dark);
}
.modal-body {
margin-bottom: 20px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* Predefined Stage Indicator */
.predefined-badge {
background: var(--success);
color: white;
font-size: 0.7rem;
padding: 2px 6px;
border-radius: 10px;
margin-left: 5px;
}
/* Form Settings Modal */
.form-settings-modal .modal {
width: 500px;
max-width: 90%;
}
/* Responsive */
@media (max-width: 1100px) {
.field-editor {
display: none;
}
.form-builder-container {
flex-direction: column;
}
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
.sidebar {
width: 100%;
border-right: none;
border-bottom: 1px solid var(--border);
height: auto;
max-height: 40vh;
}
.stage-nav-container {
height: auto;
max-height: 120px;
}
.stage-nav {
flex-wrap: wrap;
}
.stage-tab {
margin-bottom: 5px;
}
}
.stage-tab.drag-over {
background-color: rgba(0, 129, 167, 0.1); /* Used a hardcoded rgba of the new primary color */
border: 2px dashed var(--primary);
}
</style>
</head>
<body>
<!-- Pass Django CSRF token and other data -->
<script>
// Django template variables - these will be processed by Django
const djangoConfig = {
csrfToken: "{{ csrf_token }}",
saveUrl: "{% url 'save_form_template' %}",
loadUrl: {% if template_id %}"{% url 'load_form_template' template_id %}"{% else %}null{% endif %},
templateId: {% if template_id %}{{ template_id }}{% else %}null{% endif %}
};
</script>
<div class="container">
<!-- Sidebar with form elements -->
<div class="sidebar">
<div class="sidebar-header">
<h2><i class="fas fa-cube"></i> Form Elements</h2>
</div>
<div class="field-categories">
<div class="field-category">
<div class="category-title">
<i class="fas fa-user"></i> Personal Information
</div>
<div class="field-item" draggable="true" data-type="text" data-label="First Name">
<i class="fas fa-font"></i>
<span>Text Input</span>
</div>
<div class="field-item" draggable="true" data-type="email" data-label="Email">
<i class="fas fa-envelope"></i>
<span>Email</span>
</div>
<div class="field-item" draggable="true" data-type="phone" data-label="Phone">
<i class="fas fa-phone"></i>
<span>Phone</span>
</div>
<div class="field-item" draggable="true" data-type="text" data-label="Address">
<i class="fas fa-map-marker-alt"></i>
<span>Address</span>
</div>
</div>
<div class="field-category">
<div class="category-title">
<i class="fas fa-file-alt"></i> Application Details
</div>
<div class="field-item" draggable="true" data-type="textarea" data-label="Cover Letter">
<i class="fas fa-align-left"></i>
<span>Text Area</span>
</div>
<div class="field-item" draggable="true" data-type="file" data-label="Resume">
<i class="fas fa-file-upload"></i>
<span>File Upload</span>
</div>
<div class="field-item" draggable="true" data-type="date" data-label="Available Date">
<i class="fas fa-calendar"></i>
<span>Date Picker</span>
</div>
</div>
<div class="field-category">
<div class="category-title">
<i class="fas fa-list"></i> Selection Fields
</div>
<div class="field-item" draggable="true" data-type="select" data-label="Position">
<i class="fas fa-caret-square-down"></i>
<span>Dropdown</span>
</div>
<div class="field-item" draggable="true" data-type="radio" data-label="Experience Level">
<i class="fas fa-dot-circle"></i>
<span>Radio Buttons</span>
</div>
<div class="field-item" draggable="true" data-type="checkbox" data-label="Skills">
<i class="fas fa-check-square"></i>
<span>Checkboxes</span>
</div>
</div>
<div class="field-category">
<div class="category-title">
<i class="fas fa-briefcase"></i> Professional Fields
</div>
<div class="field-item" draggable="true" data-type="text" data-label="Company">
<i class="fas fa-building"></i>
<span>Company</span>
</div>
<div class="field-item" draggable="true" data-type="text" data-label="Position">
<i class="fas fa-user-tie"></i>
<span>Position</span>
</div>
<div class="field-item" draggable="true" data-type="text" data-label="Degree">
<i class="fas fa-graduation-cap"></i>
<span>Degree</span>
</div>
<div class="field-item" draggable="true" data-type="text" data-label="Institution">
<i class="fas fa-school"></i>
<span>Institution</span>
</div>
</div>
</div>
</div>
<!-- Main Content Area -->
<div class="main-content">
<div class="toolbar">
<h1 id="formTitle">Resume Application Form</h1>
<div>
<button class="btn btn-outline" id="formSettingsBtn">
<i class="fas fa-cog"></i> Settings
</button>
<button class="btn btn-outline" id="addStageBtn">
<i class="fas fa-plus"></i> Add Stage
</button>
<button class="btn btn-primary" id="saveFormBtn">
<i class="fas fa-save"></i> Save Form
</button>
</div>
</div>
<!-- Stage Navigation - Fixed Height with Scroll -->
<div class="stage-nav-container">
<div class="stage-nav" id="stageNav"></div>
</div>
<!-- Form Builder and Editor Container -->
<div class="form-builder-container">
<!-- Form Builder Area -->
<div class="form-builder">
<div class="form-header">
<h3 id="currentStageTitle">
<i class="fas fa-list"></i>
<span id="stageNameDisplay">Contact Information</span> Stage
<span class="required-indicator" id="stageRequiredIndicator" style="display: none;"> *</span>
<span class="predefined-badge" id="stagePredefinedBadge" style="display: none;">Default</span>
</h3>
<button class="rename-stage-btn" id="renameStageBtn" title="Rename stage">
<i class="fas fa-edit"></i>
</button>
</div>
<div
class="form-stage"
id="formStage"
>
<div class="empty-state" id="emptyState">
<i class="fas fa-cloud-upload-alt"></i>
<p>Drag form elements here to build your stage</p>
</div>
</div>
</div>
<!-- Field Editor Panel -->
<div class="field-editor" id="fieldEditor" style="display: none;">
<div class="editor-header">
<h3><i class="fas fa-sliders-h"></i> Field Properties</h3>
<button class="btn btn-outline" id="closeEditorBtn">Close</button>
</div>
<div class="editor-section">
<div class="form-group">
<label for="fieldLabel">Field Label</label>
<input
type="text"
id="fieldLabel"
class="form-control"
placeholder="Enter field label"
>
</div>
<div class="form-group" id="placeholderGroup">
<label for="fieldPlaceholder">Placeholder</label>
<input
type="text"
id="fieldPlaceholder"
class="form-control"
placeholder="Enter placeholder text"
>
</div>
<div class="form-group">
<div class="checkbox-group">
<input
type="checkbox"
id="requiredField"
>
<label for="requiredField">Required Field</label>
</div>
</div>
</div>
<!-- Options Editor for Select, Radio, Checkbox -->
<div class="editor-section" id="optionsEditor" style="display: none;">
<h4><i class="fas fa-list"></i> Options</h4>
<div class="options-list" id="optionsList"></div>
<button class="add-option" id="addOptionBtn">
<i class="fas fa-plus"></i> Add Option
</button>
</div>
<!-- File Type Specific Settings -->
<div class="editor-section" id="fileSettings" style="display: none;">
<h4><i class="fas fa-file"></i> File Settings</h4>
<div class="form-group">
<label for="fileTypes">Allowed File Types</label>
<input
type="text"
id="fileTypes"
class="form-control"
placeholder=".pdf, .doc, .docx"
>
</div>
<div class="form-group">
<label for="maxFileSize">Max File Size (MB)</label>
<input
type="number"
id="maxFileSize"
class="form-control"
min="1"
max="100"
value="5"
>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Stage Rename Modal -->
<div class="modal-overlay" id="renameModal">
<div class="modal">
<div class="modal-header">
<h3>Rename Stage</h3>
<button class="close-modal" id="closeRenameModal">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="stageName">Stage Name</label>
<input
type="text"
id="stageName"
class="form-control"
placeholder="Enter stage name"
>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-outline" id="cancelRenameBtn">Cancel</button>
<button class="btn btn-primary" id="saveRenameBtn">Save</button>
</div>
</div>
</div>
<!-- Form Settings Modal -->
<div class="modal-overlay form-settings-modal" id="formSettingsModal">
<div class="modal">
<div class="modal-header">
<h3>Form Settings</h3>
<button class="close-modal" id="closeFormSettingsModal">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="formName" class="form-label">Form Name</label>
<input type="text" id="formName" class="form-control" placeholder="Enter form name">
</div>
<div class="form-group">
<label for="formDescription" class="form-label">Form Description</label>
<textarea id="formDescription" class="form-control" rows="3" placeholder="Enter form description"></textarea>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="formActive">
<label for="formActive">Active Form</label>
</div>
<small class="form-text text-muted">When active, this form will be available for applicants to fill out.</small>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-outline" id="cancelFormSettingsBtn">Cancel</button>
<button class="btn btn-primary" id="saveFormSettingsBtn">Save</button>
</div>
</div>
</div>
<script>
// Application State
const state = {
draggedStageIndex: null,
formName: 'Resume Application Form',
formDescription: '',
formActive: true,
stages: [
{
id: 1,
name: 'Contact Information',
predefined: true,
fields: [
{ id: 1, type: 'text', label: 'Full Name', placeholder: 'Enter your full name', required: true, predefined: true },
{ id: 2, type: 'email', label: 'Email Address', placeholder: 'Enter your email', required: true, predefined: true },
{ id: 3, type: 'phone', label: 'Phone Number', placeholder: 'Enter your phone number', required: true, predefined: true },
{ id: 4, type: 'text', label: 'Address', placeholder: 'Enter your address', required: false, predefined: true },
{
id: 26,
type: 'file',
label: 'Resume Upload',
required: true,
predefined: true,
fileTypes: '.pdf,.doc,.docx',
maxFileSize: 5,
uploadedFile: null
}
]
},
{
id: 2,
name: 'Resume Objective',
predefined: true,
fields: [
{ id: 5, type: 'textarea', label: 'Career Objective', placeholder: 'Describe your career goals and what you hope to achieve', required: false, predefined: true }
]
},
{
id: 3,
name: 'Education',
predefined: true,
fields: [
{ id: 6, type: 'text', label: 'Degree', placeholder: 'e.g., Bachelor of Science in Computer Science', required: true, predefined: true },
{ id: 7, type: 'text', label: 'Institution', placeholder: 'Name of your university or school', required: true, predefined: true },
{ id: 8, type: 'text', label: 'Location', placeholder: 'City, State', required: false, predefined: true },
{ id: 9, type: 'date', label: 'Graduation Date', placeholder: 'MM/YYYY', required: false, predefined: true }
]
},
{
id: 4,
name: 'Experience',
predefined: true,
fields: [
{ id: 10, type: 'text', label: 'Position Title', placeholder: 'e.g., Software Engineer', required: true, predefined: true },
{ id: 11, type: 'text', label: 'Company Name', placeholder: 'Name of the company', required: true, predefined: true },
{ id: 12, type: 'text', label: 'Location', placeholder: 'City, State', required: false, predefined: true },
{ id: 13, type: 'date', label: 'Start Date', placeholder: 'MM/YYYY', required: true, predefined: true },
{ id: 14, type: 'date', label: 'End Date', placeholder: 'MM/YYYY or Present', required: true, predefined: true },
{ id: 15, type: 'textarea', label: 'Responsibilities & Achievements', placeholder: 'Describe your key responsibilities and achievements', required: false, predefined: true }
]
},
{
id: 5,
name: 'Skills',
predefined: true,
fields: [
{ id: 16, type: 'checkbox', label: 'Technical Skills', options: ['Programming Languages', 'Frameworks', 'Tools & Technologies'], required: false, predefined: true }
]
},
{
id: 6,
name: 'Summary',
predefined: true,
fields: [
{ id: 17, type: 'textarea', label: 'Professional Summary', placeholder: 'Brief overview of your professional background and key qualifications', required: false, predefined: true }
]
},
{
id: 7,
name: 'Certifications',
predefined: true,
fields: [
{ id: 18, type: 'text', label: 'Certification Name', placeholder: 'e.g., AWS Certified Solutions Architect', required: false, predefined: true },
{ id: 19, type: 'text', label: 'Issuing Organization', placeholder: 'Name of the certifying body', required: false, predefined: true },
{ id: 20, type: 'date', label: 'Issue Date', placeholder: 'MM/YYYY', required: false, predefined: true },
{ id: 21, type: 'date', label: 'Expiration Date', placeholder: 'MM/YYYY or N/A', required: false, predefined: true }
]
},
{
id: 8,
name: 'Awards and Recognitions',
predefined: true,
fields: [
{ id: 22, type: 'text', label: 'Award Name', placeholder: 'Name of the award or recognition', required: false, predefined: true },
{ id: 23, type: 'text', label: 'Issuing Organization', placeholder: 'Who gave you this award?', required: false, predefined: true },
{ id: 24, type: 'date', label: 'Date Received', placeholder: 'MM/YYYY', required: false, predefined: true },
{ id: 25, type: 'textarea', label: 'Description', placeholder: 'Brief description of the award and why you received it', required: false, predefined: true }
]
}
],
currentStage: 0,
nextFieldId: 27,
nextStageId: 9,
selectedField: null,
draggedField: null,
draggedFieldIndex: null,
templateId: djangoConfig.templateId // Initialize with Django template ID if editing
};
function allowStageDrop(event) {
event.preventDefault();
}
function dragStageEnter(event) {
event.target.classList.add('drag-over');
}
function dragStageLeave(event) {
event.target.classList.remove('drag-over');
}
function startStageDrag(event, index) {
state.draggedStageIndex = index;
event.dataTransfer.setData('text/plain', 'stage-reorder');
event.dataTransfer.effectAllowed = 'move';
}
function dropStage(event, targetIndex) {
event.preventDefault();
event.target.classList.remove('drag-over');
if (state.draggedStageIndex !== null && state.draggedStageIndex !== targetIndex) {
const draggedStage = state.stages[state.draggedStageIndex];
state.stages.splice(state.draggedStageIndex, 1);
// Adjust target index if we removed an element before it
const adjustedIndex = state.draggedStageIndex < targetIndex ? targetIndex - 1 : targetIndex;
state.stages.splice(adjustedIndex, 0, draggedStage);
// Update current stage if needed
if (state.currentStage === state.draggedStageIndex) {
state.currentStage = adjustedIndex;
} else if (state.currentStage > state.draggedStageIndex && state.currentStage <= adjustedIndex) {
state.currentStage--;
} else if (state.currentStage < state.draggedStageIndex && state.currentStage >= adjustedIndex) {
state.currentStage++;
}
renderStageNavigation();
renderCurrentStage();
}
state.draggedStageIndex = null;
}
// DOM Elements
const elements = {
stageNav: document.getElementById('stageNav'),
formStage: document.getElementById('formStage'),
emptyState: document.getElementById('emptyState'),
fieldEditor: document.getElementById('fieldEditor'),
currentStageTitle: document.getElementById('currentStageTitle'),
stageNameDisplay: document.getElementById('stageNameDisplay'),
stageRequiredIndicator: document.getElementById('stageRequiredIndicator'),
stagePredefinedBadge: document.getElementById('stagePredefinedBadge'),
addStageBtn: document.getElementById('addStageBtn'),
saveFormBtn: document.getElementById('saveFormBtn'),
renameStageBtn: document.getElementById('renameStageBtn'),
renameModal: document.getElementById('renameModal'),
stageName: document.getElementById('stageName'),
closeRenameModal: document.getElementById('closeRenameModal'),
cancelRenameBtn: document.getElementById('cancelRenameBtn'),
saveRenameBtn: document.getElementById('saveRenameBtn'),
fieldLabel: document.getElementById('fieldLabel'),
fieldPlaceholder: document.getElementById('fieldPlaceholder'),
placeholderGroup: document.getElementById('placeholderGroup'),
requiredField: document.getElementById('requiredField'),
optionsEditor: document.getElementById('optionsEditor'),
optionsList: document.getElementById('optionsList'),
addOptionBtn: document.getElementById('addOptionBtn'),
fileSettings: document.getElementById('fileSettings'),
fileTypes: document.getElementById('fileTypes'),
maxFileSize: document.getElementById('maxFileSize'),
closeEditorBtn: document.getElementById('closeEditorBtn'),
// Form settings elements
formTitle: document.getElementById('formTitle'),
formSettingsBtn: document.getElementById('formSettingsBtn'),
formSettingsModal: document.getElementById('formSettingsModal'),
formName: document.getElementById('formName'),
formDescription: document.getElementById('formDescription'),
formActive: document.getElementById('formActive'),
closeFormSettingsModal: document.getElementById('closeFormSettingsModal'),
cancelFormSettingsBtn: document.getElementById('cancelFormSettingsBtn'),
saveFormSettingsBtn: document.getElementById('saveFormSettingsBtn')
};
// Utility Functions
function getFieldIcon(type) {
const icons = {
'text': 'fas fa-font',
'email': 'fas fa-envelope',
'phone': 'fas fa-phone',
'textarea': 'fas fa-align-left',
'file': 'fas fa-file-upload',
'date': 'fas fa-calendar',
'select': 'fas fa-caret-square-down',
'radio': 'fas fa-dot-circle',
'checkbox': 'fas fa-check-square'
};
return icons[type] || 'fas fa-question';
}
function hasRequiredFields(stage) {
return stage.fields.some(field => field.required);
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// API Functions
async function saveFormTemplate() {
const formData = {
name: state.formName,
description: state.formDescription,
is_active: state.formActive,
template_id: state.templateId, // Include template_id for updates
stages: state.stages.map(stage => ({
name: stage.name,
predefined: stage.predefined,
fields: stage.fields.map(field => ({
type: field.type,
label: field.label,
placeholder: field.placeholder || '',
required: field.required || false,
options: field.options || [],
fileTypes: field.fileTypes || '',
maxFileSize: field.maxFileSize || 5,
predefined: field.predefined
}))
}))
};
try {
const response = await fetch(djangoConfig.saveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': djangoConfig.csrfToken,
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(formData)
});
const result = await response.json();
if (result.success) {
alert('Form template saved successfully! Template ID: ' + result.template_id);
// Update templateId for future saves (important for new templates)
state.templateId = result.template_id;
} else {
alert('Error saving form template: ' + result.error);
}
} catch (error) {
console.error('Error:', error);
alert('Error saving form template. Please try again.');
}
}
// Load existing template if editing
async function loadExistingTemplate() {
if (djangoConfig.loadUrl) {
try {
const response = await fetch(djangoConfig.loadUrl);
const result = await response.json();
if (result.success) {
const templateData = result.template;
// Set form settings
state.formName = templateData.name || 'Untitled Form';
state.formDescription = templateData.description || '';
state.formActive = templateData.is_active !== false; // Default to true if not set
// Update form title
elements.formTitle.textContent = state.formName;
elements.formName.value = state.formName;
elements.formDescription.value = state.formDescription;
elements.formActive.checked = state.formActive;
// Set stages
state.stages = templateData.stages;
state.templateId = templateData.id;
// Update next IDs to avoid conflicts
let maxFieldId = 0;
let maxStageId = 0;
templateData.stages.forEach(stage => {
maxStageId = Math.max(maxStageId, stage.id);
stage.fields.forEach(field => {
maxFieldId = Math.max(maxFieldId, field.id);
});
});
state.nextFieldId = maxFieldId + 1;
state.nextStageId = maxStageId + 1;
state.currentStage = 0;
renderStageNavigation();
renderCurrentStage();
}
} catch (error) {
console.error('Error loading template:', error);
alert('Error loading template data.');
}
}
}
// DOM Rendering Functions (same as before)
function renderStageNavigation() {
elements.stageNav.innerHTML = '';
state.stages.forEach((stage, index) => {
const stageTab = document.createElement('div');
stageTab.className = `stage-tab ${index === state.currentStage ? 'active' : ''}`;
stageTab.dataset.index = index;
// Make stage tabs draggable
stageTab.draggable = true;
stageTab.addEventListener('dragstart', (e) => startStageDrag(e, index));
stageTab.addEventListener('dragover', allowStageDrop);
stageTab.addEventListener('dragenter', dragStageEnter);
stageTab.addEventListener('dragleave', dragStageLeave);
stageTab.addEventListener('drop', (e) => dropStage(e, index));
stageTab.innerHTML = `
${stage.name}
${hasRequiredFields(stage) ? '<span class="required-indicator"> *</span>' : ''}
${stage.predefined ? '<span class="predefined-badge">Default</span>' : ''}
<span class="rename-btn"><i class="fas fa-edit"></i></span>
${(!stage.predefined || state.stages.length > 1) ? '<span class="remove-btn"><i class="fas fa-times"></i></span>' : ''}
`;
stageTab.addEventListener('click', (e) => {
if (!e.target.closest('.rename-btn') && !e.target.closest('.remove-btn')) {
state.currentStage = index;
renderCurrentStage();
renderStageNavigation();
}
});
const renameBtn = stageTab.querySelector('.rename-btn');
if (renameBtn) {
renameBtn.addEventListener('click', (e) => {
e.stopPropagation();
openRenameModal(stage);
});
}
const removeBtn = stageTab.querySelector('.remove-btn');
if (removeBtn) {
removeBtn.addEventListener('click', (e) => {
e.stopPropagation();
removeStage(index);
});
}
elements.stageNav.appendChild(stageTab);
});
}
function renderCurrentStage() {
const currentStage = state.stages[state.currentStage];
elements.stageNameDisplay.textContent = currentStage.name;
elements.stageRequiredIndicator.style.display = hasRequiredFields(currentStage) ? 'inline' : 'none';
elements.stagePredefinedBadge.style.display = currentStage.predefined ? 'inline' : 'none';
elements.formStage.innerHTML = '';
if (currentStage.fields.length === 0) {
elements.emptyState.style.display = 'block';
elements.formStage.appendChild(elements.emptyState);
} else {
elements.emptyState.style.display = 'none';
currentStage.fields.forEach((field, index) => {
const fieldElement = createFieldElement(field, index);
elements.formStage.appendChild(fieldElement);
});
}
}
function createFieldElement(field, index) {
const fieldDiv = document.createElement('div');
fieldDiv.className = `form-field ${state.selectedField && state.selectedField.id === field.id ? 'selected' : ''}`;
fieldDiv.dataset.fieldId = field.id;
fieldDiv.dataset.fieldIndex = index;
const fieldHeader = document.createElement('div');
fieldHeader.className = 'field-header';
fieldHeader.innerHTML = `
<div class="field-title">
<i class="${getFieldIcon(field.type)}"></i>
${field.label || field.type.charAt(0).toUpperCase() + field.type.slice(1)}
${field.required ? '<span class="required-indicator"> *</span>' : ''}
</div>
<div class="field-actions">
<div class="action-btn edit-field" data-field-id="${field.id}">
<i class="fas fa-edit"></i>
</div>
${!field.predefined ? `<div class="action-btn remove-field" data-field-index="${index}">
<i class="fas fa-trash"></i>
</div>` : ''}
</div>
`;
const fieldContent = document.createElement('div');
fieldContent.className = 'field-content';
fieldContent.innerHTML = `
<label class="field-label">
${field.label || 'Field Label'}
${field.required ? '<span class="required-indicator"> *</span>' : ''}
</label>
`;
// Add field input based on type
if (field.type === 'text' || field.type === 'email' || field.type === 'phone' || field.type === 'date') {
const input = document.createElement('input');
input.type = 'text';
input.className = 'field-input';
input.placeholder = field.placeholder || 'Enter value';
input.disabled = true;
fieldContent.appendChild(input);
} else if (field.type === 'textarea') {
const textarea = document.createElement('textarea');
textarea.className = 'field-input';
textarea.rows = 3;
textarea.placeholder = field.placeholder || 'Enter text';
textarea.disabled = true;
fieldContent.appendChild(textarea);
} else if (field.type === 'file') {
const fileUpload = document.createElement('div');
fileUpload.className = 'file-upload-area';
fileUpload.innerHTML = `
<div class="file-upload-icon">
<i class="fas fa-cloud-upload-alt"></i>
</div>
<div class="file-upload-text">
<p>Drag & drop your resume here or <strong>click to browse</strong></p>
</div>
<div class="file-upload-info">
<p>Supported formats: ${field.fileTypes || '.pdf, .doc, .docx'} (Max ${field.maxFileSize || 5}MB)</p>
</div>
<input type="file" class="file-input" style="display: none;" accept="${field.fileTypes || '.pdf,.doc,.docx'}">
`;
if (field.uploadedFile) {
const uploadedFile = document.createElement('div');
uploadedFile.className = 'uploaded-file';
uploadedFile.innerHTML = `
<div class="file-info">
<i class="fas fa-file file-icon"></i>
<div>
<div class="file-name">${field.uploadedFile.name}</div>
<div class="file-size">${formatFileSize(field.uploadedFile.size)}</div>
</div>
</div>
<button class="remove-file-btn">
<i class="fas fa-times"></i>
</button>
`;
fileUpload.appendChild(uploadedFile);
}
fieldContent.appendChild(fileUpload);
} else if (field.type === 'select') {
const select = document.createElement('select');
select.className = 'field-input';
select.disabled = true;
field.options.forEach(option => {
const optionEl = document.createElement('option');
optionEl.textContent = option;
select.appendChild(optionEl);
});
fieldContent.appendChild(select);
} else if (field.type === 'radio' || field.type === 'checkbox') {
const optionsDiv = document.createElement('div');
optionsDiv.className = 'field-options';
field.options.forEach((option, idx) => {
const optionItem = document.createElement('div');
optionItem.className = 'option-item';
optionItem.innerHTML = `
<input type="${field.type === 'radio' ? 'radio' : 'checkbox'}"
id="${field.type}-${field.id}-${idx}"
name="${field.type}-${field.id}"
disabled>
<label for="${field.type}-${field.id}-${idx}">${option}</label>
`;
optionsDiv.appendChild(optionItem);
});
fieldContent.appendChild(optionsDiv);
}
fieldDiv.appendChild(fieldHeader);
fieldDiv.appendChild(fieldContent);
// Add event listeners
fieldDiv.addEventListener('click', (e) => {
if (!e.target.closest('.edit-field') && !e.target.closest('.remove-field') &&
!e.target.closest('.remove-file-btn')) {
selectField(field);
}
});
const editBtn = fieldDiv.querySelector('.edit-field');
if (editBtn) {
editBtn.addEventListener('click', (e) => {
e.stopPropagation();
selectField(field);
});
}
const removeBtn = fieldDiv.querySelector('.remove-field');
if (removeBtn) {
removeBtn.addEventListener('click', (e) => {
e.stopPropagation();
removeField(parseInt(removeBtn.dataset.fieldIndex));
});
}
const removeFileBtn = fieldDiv.querySelector('.remove-file-btn');
if (removeFileBtn) {
removeFileBtn.addEventListener('click', (e) => {
e.stopPropagation();
const fieldId = parseInt(fieldDiv.dataset.fieldId);
const stage = state.stages[state.currentStage];
const field = stage.fields.find(f => f.id === fieldId);
if (field) {
field.uploadedFile = null;
renderCurrentStage();
}
});
}
// Make draggable
fieldDiv.draggable = true;
fieldDiv.addEventListener('dragstart', (e) => {
state.draggedFieldIndex = parseInt(fieldDiv.dataset.fieldIndex);
e.dataTransfer.setData('text/plain', 'reorder');
e.dataTransfer.effectAllowed = 'move';
});
fieldDiv.addEventListener('dragover', (e) => {
e.preventDefault();
});
fieldDiv.addEventListener('drop', (e) => {
e.preventDefault();
const targetIndex = parseInt(fieldDiv.dataset.fieldIndex);
dropField(targetIndex);
});
return fieldDiv;
}
function showFieldEditor(field) {
elements.fieldEditor.style.display = 'flex';
elements.fieldLabel.value = field.label || '';
elements.fieldPlaceholder.value = field.placeholder || '';
elements.placeholderGroup.style.display = field.type !== 'file' ? 'block' : 'none';
elements.requiredField.checked = field.required || false;
// Show/hide options editor
const showOptions = field.type === 'select' || field.type === 'radio' || field.type === 'checkbox';
elements.optionsEditor.style.display = showOptions ? 'block' : 'none';
if (showOptions) {
renderOptionsEditor(field);
}
// Show/hide file settings
const showFileSettings = field.type === 'file';
elements.fileSettings.style.display = showFileSettings ? 'block' : 'none';
if (showFileSettings) {
elements.fileTypes.value = field.fileTypes || '.pdf,.doc,.docx';
elements.maxFileSize.value = field.maxFileSize || 5;
}
}
function renderOptionsEditor(field) {
elements.optionsList.innerHTML = '';
field.options.forEach((option, index) => {
const optionInput = document.createElement('div');
optionInput.className = 'option-input';
optionInput.innerHTML = `
<input type="text" class="form-control" value="${option}" placeholder="Option ${index + 1}">
<button class="remove-option">
<i class="fas fa-times"></i>
</button>
`;
elements.optionsList.appendChild(optionInput);
const input = optionInput.querySelector('input');
const removeBtn = optionInput.querySelector('.remove-option');
input.addEventListener('input', () => {
field.options[index] = input.value;
});
removeBtn.addEventListener('click', () => {
if (field.options.length > 1) {
field.options.splice(index, 1);
renderOptionsEditor(field);
}
});
});
}
// Event Handlers (same as before, but updated saveForm function)
function selectField(field) {
state.selectedField = field;
showFieldEditor(field);
// Update UI to show selected field
document.querySelectorAll('.form-field').forEach(el => {
el.classList.remove('selected');
});
const selectedEl = document.querySelector(`[data-field-id="${field.id}"]`);
if (selectedEl) {
selectedEl.classList.add('selected');
}
}
function clearSelection() {
state.selectedField = null;
elements.fieldEditor.style.display = 'none';
document.querySelectorAll('.form-field').forEach(el => {
el.classList.remove('selected');
});
}
function addStage() {
const newStage = {
id: state.nextStageId++,
name: `Custom Stage ${state.stages.filter(s => !s.predefined).length + 1}`,
predefined: false,
fields: []
};
state.stages.push(newStage);
state.currentStage = state.stages.length - 1;
renderStageNavigation();
renderCurrentStage();
}
function removeStage(index) {
const predefinedStages = state.stages.filter(s => s.predefined).length;
const isPredefined = state.stages[index].predefined;
if (isPredefined && predefinedStages <= 1 && state.stages.length === 1) {
return;
}
if (!isPredefined || state.stages.length > 1) {
state.stages.splice(index, 1);
if (state.currentStage >= state.stages.length) {
state.currentStage = state.stages.length - 1;
}
renderStageNavigation();
renderCurrentStage();
}
}
function removeField(index) {
const currentStage = state.stages[state.currentStage];
if (!currentStage.fields[index].predefined) {
currentStage.fields.splice(index, 1);
if (state.selectedField && state.selectedField.id === currentStage.fields[index]?.id) {
clearSelection();
}
renderCurrentStage();
}
}
function openRenameModal(stage) {
elements.stageName.value = stage.name;
elements.renameModal.classList.add('active');
elements.stageName.focus();
// Store reference to the stage being renamed
elements.renameModal.dataset.stageId = stage.id;
}
function closeRenameModal() {
elements.renameModal.classList.remove('active');
}
function saveStageName() {
const stageId = parseInt(elements.renameModal.dataset.stageId);
const newName = elements.stageName.value.trim();
if (newName === '') {
alert('Stage name cannot be empty');
return;
}
const stage = state.stages.find(s => s.id === stageId);
if (stage) {
stage.name = newName;
renderStageNavigation();
renderCurrentStage();
}
closeRenameModal();
}
// Form Settings Modal Handlers
function openFormSettingsModal() {
elements.formName.value = state.formName;
elements.formDescription.value = state.formDescription;
elements.formActive.checked = state.formActive;
elements.formSettingsModal.classList.add('active');
}
function closeFormSettingsModal() {
elements.formSettingsModal.classList.remove('active');
}
function saveFormSettings() {
const newName = elements.formName.value.trim();
if (newName === '') {
alert('Form name cannot be empty');
return;
}
state.formName = newName;
state.formDescription = elements.formDescription.value;
state.formActive = elements.formActive.checked;
// Update the form title in the header
elements.formTitle.textContent = state.formName;
closeFormSettingsModal();
}
// Updated saveForm function to use Django API
function saveForm() {
saveFormTemplate();
}
// Drag and Drop Handlers (same as before)
function startDrag(event, type, label) {
state.draggedField = { type, label };
event.dataTransfer.setData('text/plain', 'field');
event.dataTransfer.effectAllowed = 'copy';
}
function allowDrop(event) {
event.preventDefault();
}
function dragEnter(event) {
event.target.classList.add('drag-over');
}
function dragLeave(event) {
event.target.classList.remove('drag-over');
}
function drop(event) {
event.preventDefault();
event.target.classList.remove('drag-over');
if (state.draggedField) {
const newField = {
id: state.nextFieldId++,
type: state.draggedField.type,
label: state.draggedField.label,
placeholder: '',
required: false,
options: state.draggedField.type === 'select' || state.draggedField.type === 'radio' || state.draggedField.type === 'checkbox'
? ['Option 1', 'Option 2']
: [],
fileTypes: state.draggedField.type === 'file' ? '.pdf,.doc,.docx' : '',
maxFileSize: state.draggedField.type === 'file' ? 5 : 0,
predefined: false,
uploadedFile: null
};
state.stages[state.currentStage].fields.push(newField);
selectField(newField);
state.draggedField = null;
renderCurrentStage();
}
}
function dropField(targetIndex) {
if (state.draggedFieldIndex !== null && state.draggedFieldIndex !== targetIndex) {
const currentStage = state.stages[state.currentStage];
const draggedField = currentStage.fields[state.draggedFieldIndex];
currentStage.fields.splice(state.draggedFieldIndex, 1);
const adjustedIndex = state.draggedFieldIndex < targetIndex ? targetIndex - 1 : targetIndex;
currentStage.fields.splice(adjustedIndex, 0, draggedField);
renderCurrentStage();
}
state.draggedFieldIndex = null;
}
// Initialize Event Listeners
function initEventListeners() {
// Sidebar drag start
document.querySelectorAll('.field-item').forEach(item => {
item.addEventListener('dragstart', (e) => {
const type = item.dataset.type;
const label = item.dataset.label;
startDrag(e, type, label);
});
});
// Form stage drop zone
elements.formStage.addEventListener('drop', drop);
elements.formStage.addEventListener('dragover', allowDrop);
elements.formStage.addEventListener('dragenter', dragEnter);
elements.formStage.addEventListener('dragleave', dragLeave);
elements.formStage.addEventListener('click', clearSelection);
// Button events
elements.addStageBtn.addEventListener('click', addStage);
elements.saveFormBtn.addEventListener('click', saveForm);
elements.renameStageBtn.addEventListener('click', () => {
openRenameModal(state.stages[state.currentStage]);
});
// Form settings button
elements.formSettingsBtn.addEventListener('click', openFormSettingsModal);
// Modal events
elements.closeRenameModal.addEventListener('click', closeRenameModal);
elements.cancelRenameBtn.addEventListener('click', closeRenameModal);
elements.saveRenameBtn.addEventListener('click', saveStageName);
elements.renameModal.addEventListener('click', (e) => {
if (e.target === elements.renameModal) {
closeRenameModal();
}
});
// Form settings modal events
elements.closeFormSettingsModal.addEventListener('click', closeFormSettingsModal);
elements.cancelFormSettingsBtn.addEventListener('click', closeFormSettingsModal);
elements.saveFormSettingsBtn.addEventListener('click', saveFormSettings);
elements.formSettingsModal.addEventListener('click', (e) => {
if (e.target === elements.formSettingsModal) {
closeFormSettingsModal();
}
});
// Field editor events
elements.closeEditorBtn.addEventListener('click', clearSelection);
elements.fieldLabel.addEventListener('input', () => {
if (state.selectedField) {
state.selectedField.label = elements.fieldLabel.value;
renderCurrentStage();
}
});
elements.fieldPlaceholder.addEventListener('input', () => {
if (state.selectedField) {
state.selectedField.placeholder = elements.fieldPlaceholder.value;
}
});
elements.requiredField.addEventListener('change', () => {
if (state.selectedField) {
state.selectedField.required = elements.requiredField.checked;
renderStageNavigation();
renderCurrentStage();
}
});
elements.addOptionBtn.addEventListener('click', () => {
if (state.selectedField &&
(state.selectedField.type === 'select' ||
state.selectedField.type === 'radio' ||
state.selectedField.type === 'checkbox')) {
state.selectedField.options.push('New Option');
renderOptionsEditor(state.selectedField);
}
});
elements.fileTypes.addEventListener('input', () => {
if (state.selectedField && state.selectedField.type === 'file') {
state.selectedField.fileTypes = elements.fileTypes.value;
}
});
elements.maxFileSize.addEventListener('input', () => {
if (state.selectedField && state.selectedField.type === 'file') {
state.selectedField.maxFileSize = parseInt(elements.maxFileSize.value) || 5;
}
});
}
// Initialize Application
function init() {
// Initialize form title
elements.formTitle.textContent = state.formName;
renderStageNavigation();
renderCurrentStage();
initEventListeners();
// Load existing template if editing
if (djangoConfig.loadUrl) {
loadExistingTemplate();
}
}
// Start the application
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>