1495 lines
56 KiB
HTML
1495 lines
56 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 - Resume Upload</title>
|
|
<script src="https://unpkg.com/petite-vue@0.4.1/dist/petite-vue.iife.js"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
:root {
|
|
--primary: #4361ee;
|
|
--primary-light: #4895ef;
|
|
--secondary: #3f37c9;
|
|
--success: #4cc9f0;
|
|
--light: #f8f9fa;
|
|
--dark: #212529;
|
|
--gray: #6c757d;
|
|
--light-gray: #e9ecef;
|
|
--border: #dee2e6;
|
|
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
--radius: 8px;
|
|
--transition: all 0.3s ease;
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
}
|
|
|
|
body {
|
|
background-color: #f5f7fb;
|
|
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: auto;
|
|
padding: 5px 0;
|
|
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(67, 97, 238, 0.2);
|
|
}
|
|
|
|
.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;
|
|
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(67, 97, 238, 0.2);
|
|
}
|
|
|
|
.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(67, 97, 238, 0.05);
|
|
}
|
|
|
|
.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(67, 97, 238, 0.2);
|
|
}
|
|
|
|
.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;
|
|
color: #e53935;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.remove-option:hover {
|
|
background: #e53935;
|
|
color: white;
|
|
}
|
|
|
|
/* Drag and Drop Styles */
|
|
.drag-over {
|
|
background-color: rgba(67, 97, 238, 0.1);
|
|
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;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div v-scope="App" v-cloak>
|
|
<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" @dragstart="startDrag($event, 'text', 'First Name')">
|
|
<i class="fas fa-font"></i>
|
|
<span>Text Input</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'email', 'Email')">
|
|
<i class="fas fa-envelope"></i>
|
|
<span>Email</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'phone', 'Phone')">
|
|
<i class="fas fa-phone"></i>
|
|
<span>Phone</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'text', '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" @dragstart="startDrag($event, 'textarea', 'Cover Letter')">
|
|
<i class="fas fa-align-left"></i>
|
|
<span>Text Area</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'file', 'Resume')">
|
|
<i class="fas fa-file-upload"></i>
|
|
<span>File Upload</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'date', '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" @dragstart="startDrag($event, 'select', 'Position')">
|
|
<i class="fas fa-caret-square-down"></i>
|
|
<span>Dropdown</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'radio', 'Experience Level')">
|
|
<i class="fas fa-dot-circle"></i>
|
|
<span>Radio Buttons</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'checkbox', '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" @dragstart="startDrag($event, 'text', 'Company')">
|
|
<i class="fas fa-building"></i>
|
|
<span>Company</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'text', 'Position')">
|
|
<i class="fas fa-user-tie"></i>
|
|
<span>Position</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'text', 'Degree')">
|
|
<i class="fas fa-graduation-cap"></i>
|
|
<span>Degree</span>
|
|
</div>
|
|
<div class="field-item" draggable="true" @dragstart="startDrag($event, 'text', '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>Applicant Tracking System</h1>
|
|
<div>
|
|
<button class="btn btn-outline" @click="addStage">
|
|
<i class="fas fa-plus"></i> Add Stage
|
|
</button>
|
|
<button class="btn btn-primary" @click="saveForm">
|
|
<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">
|
|
<div
|
|
v-for="(stage, index) in stages"
|
|
:class="['stage-tab', { 'active': currentStage === index }]"
|
|
@click="currentStage = index"
|
|
>
|
|
{{ stage.name }}
|
|
<span class="required-indicator" v-if="hasRequiredFields(stage)"> *</span>
|
|
<span class="predefined-badge" v-if="stage.predefined">Default</span>
|
|
<span class="rename-btn" @click.stop="openRenameModal(stage)">
|
|
<i class="fas fa-edit"></i>
|
|
</span>
|
|
<span class="remove-btn" @click.stop="removeStage(index)" v-if="!stage.predefined || stages.length > 1">
|
|
<i class="fas fa-times"></i>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Builder and Editor Container -->
|
|
<div class="form-builder-container">
|
|
<!-- Form Builder Area -->
|
|
<div class="form-builder">
|
|
<div class="form-header">
|
|
<h3>
|
|
<i class="fas fa-list"></i>
|
|
{{ stages[currentStage].name }} Stage
|
|
<span class="required-indicator" v-if="hasRequiredFields(stages[currentStage])"> *</span>
|
|
<span class="predefined-badge" v-if="stages[currentStage].predefined">Default</span>
|
|
</h3>
|
|
<button class="rename-stage-btn" @click="openRenameModal(stages[currentStage])" title="Rename stage">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
class="form-stage"
|
|
@drop="drop($event)"
|
|
@dragover="allowDrop($event)"
|
|
@dragenter="dragEnter($event)"
|
|
@dragleave="dragLeave($event)"
|
|
@click="clearSelection"
|
|
>
|
|
<div v-if="stages[currentStage].fields.length === 0" class="empty-state">
|
|
<i class="fas fa-cloud-upload-alt"></i>
|
|
<p>Drag form elements here to build your stage</p>
|
|
</div>
|
|
|
|
<div
|
|
v-for="(field, fieldIndex) in stages[currentStage].fields"
|
|
:key="field.id"
|
|
:class="['form-field', { 'selected': selectedField && selectedField.id === field.id }]"
|
|
draggable="true"
|
|
@dragstart="startFieldDrag($event, fieldIndex)"
|
|
@dragover="allowFieldDrop($event)"
|
|
@drop="dropField($event, fieldIndex)"
|
|
@click.stop="selectField(field)"
|
|
>
|
|
<div class="field-header">
|
|
<div class="field-title">
|
|
<i :class="getFieldIcon(field.type)"></i>
|
|
{{ field.label || field.type.charAt(0).toUpperCase() + field.type.slice(1) }}
|
|
<span class="required-indicator" v-if="field.required"> *</span>
|
|
</div>
|
|
<div class="field-actions">
|
|
<div class="action-btn" @click.stop="selectField(field)">
|
|
<i class="fas fa-edit"></i>
|
|
</div>
|
|
<div class="action-btn" @click.stop="removeField(fieldIndex)" v-if="!field.predefined">
|
|
<i class="fas fa-trash"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field-content">
|
|
<label class="field-label">
|
|
{{ field.label || 'Field Label' }}
|
|
<span class="required-indicator" v-if="field.required"> *</span>
|
|
</label>
|
|
|
|
<!-- Render field based on type -->
|
|
<div v-if="field.type === 'text' || field.type === 'email' || field.type === 'phone' || field.type === 'date'">
|
|
<input
|
|
type="text"
|
|
class="field-input"
|
|
:placeholder="field.placeholder || 'Enter value'"
|
|
disabled
|
|
>
|
|
</div>
|
|
|
|
<div v-else-if="field.type === 'textarea'">
|
|
<textarea
|
|
class="field-input"
|
|
rows="3"
|
|
:placeholder="field.placeholder || 'Enter text'"
|
|
disabled
|
|
></textarea>
|
|
</div>
|
|
|
|
<div v-else-if="field.type === 'file'">
|
|
<div class="file-upload-area" @click.stop="triggerFileInput(field)">
|
|
<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: PDF, DOC, DOCX (Max 5MB)</p>
|
|
</div>
|
|
|
|
<!-- Hidden file input -->
|
|
<input
|
|
type="file"
|
|
:ref="'fileInput' + field.id"
|
|
style="display: none;"
|
|
@change="handleFileUpload($event, field)"
|
|
accept=".pdf,.doc,.docx"
|
|
>
|
|
|
|
<!-- Uploaded file preview -->
|
|
<div v-if="field.uploadedFile" class="uploaded-file">
|
|
<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" @click.stop="removeUploadedFile(field)">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else-if="field.type === 'select'">
|
|
<select class="field-input" disabled>
|
|
<option v-for="(option, idx) in field.options" :key="idx">
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div v-else-if="field.type === 'radio'">
|
|
<div class="field-options">
|
|
<div v-for="(option, idx) in field.options" :key="idx" class="option-item">
|
|
<input type="radio" :id="'radio-' + field.id + '-' + idx" :name="'radio-' + field.id" disabled>
|
|
<label :for="'radio-' + field.id + '-' + idx">{{ option }}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else-if="field.type === 'checkbox'">
|
|
<div class="field-options">
|
|
<div v-for="(option, idx) in field.options" :key="idx" class="option-item">
|
|
<input type="checkbox" :id="'check-' + field.id + '-' + idx" disabled>
|
|
<label :for="'check-' + field.id + '-' + idx">{{ option }}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Field Editor Panel -->
|
|
<div class="field-editor" v-if="selectedField">
|
|
<div class="editor-header">
|
|
<h3><i class="fas fa-sliders-h"></i> Field Properties</h3>
|
|
<button class="btn btn-outline" @click="clearSelection">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"
|
|
v-model="selectedField.label"
|
|
placeholder="Enter field label"
|
|
>
|
|
</div>
|
|
|
|
<div class="form-group" v-if="selectedField.type !== 'file'">
|
|
<label for="fieldPlaceholder">Placeholder</label>
|
|
<input
|
|
type="text"
|
|
id="fieldPlaceholder"
|
|
class="form-control"
|
|
v-model="selectedField.placeholder"
|
|
placeholder="Enter placeholder text"
|
|
>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<div class="checkbox-group">
|
|
<input
|
|
type="checkbox"
|
|
id="requiredField"
|
|
v-model="selectedField.required"
|
|
>
|
|
<label for="requiredField">Required Field</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Options Editor for Select, Radio, Checkbox -->
|
|
<div class="editor-section" v-if="selectedField.type === 'select' || selectedField.type === 'radio' || selectedField.type === 'checkbox'">
|
|
<h4><i class="fas fa-list"></i> Options</h4>
|
|
|
|
<div class="options-list">
|
|
<div class="option-input" v-for="(option, index) in selectedField.options" :key="index">
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
v-model="selectedField.options[index]"
|
|
:placeholder="'Option ' + (index + 1)"
|
|
>
|
|
<button class="remove-option" @click="removeOption(index)">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="add-option" @click="addOption">
|
|
<i class="fas fa-plus"></i> Add Option
|
|
</button>
|
|
</div>
|
|
|
|
<!-- File Type Specific Settings -->
|
|
<div class="editor-section" v-if="selectedField.type === 'file'">
|
|
<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"
|
|
v-model="selectedField.fileTypes"
|
|
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"
|
|
v-model="selectedField.maxFileSize"
|
|
min="1"
|
|
max="100"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage Rename Modal -->
|
|
<div class="modal-overlay" v-if="showRenameModal" @click="closeRenameModal">
|
|
<div class="modal" @click.stop>
|
|
<div class="modal-header">
|
|
<h3>Rename Stage</h3>
|
|
<button class="close-modal" @click="closeRenameModal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="stageName">Stage Name</label>
|
|
<input
|
|
type="text"
|
|
id="stageName"
|
|
class="form-control"
|
|
v-model="stageToRename.name"
|
|
placeholder="Enter stage name"
|
|
ref="stageNameInput"
|
|
>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-outline" @click="closeRenameModal">Cancel</button>
|
|
<button class="btn btn-primary" @click="saveStageName">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
PetiteVue.createApp({
|
|
delimiters: ['{{', '}}'],
|
|
App: {
|
|
// Reactive data
|
|
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,
|
|
draggedField: null,
|
|
draggedFieldIndex: null,
|
|
nextFieldId: 27,
|
|
nextStageId: 9,
|
|
selectedField: null,
|
|
showRenameModal: false,
|
|
stageToRename: null,
|
|
|
|
// Methods
|
|
startDrag(event, type, label) {
|
|
this.draggedField = { type, label };
|
|
event.dataTransfer.setData('text/plain', 'field');
|
|
event.dataTransfer.effectAllowed = 'copy';
|
|
},
|
|
|
|
allowDrop(event) {
|
|
event.preventDefault();
|
|
},
|
|
|
|
dragEnter(event) {
|
|
event.target.classList.add('drag-over');
|
|
},
|
|
|
|
dragLeave(event) {
|
|
event.target.classList.remove('drag-over');
|
|
},
|
|
|
|
drop(event) {
|
|
event.preventDefault();
|
|
event.target.classList.remove('drag-over');
|
|
|
|
if (this.draggedField) {
|
|
const newField = {
|
|
id: this.nextFieldId++,
|
|
type: this.draggedField.type,
|
|
label: this.draggedField.label,
|
|
placeholder: '',
|
|
required: false,
|
|
options: this.draggedField.type === 'select' || this.draggedField.type === 'radio' || this.draggedField.type === 'checkbox'
|
|
? ['Option 1', 'Option 2']
|
|
: [],
|
|
fileTypes: this.draggedField.type === 'file' ? '.pdf,.doc,.docx' : '',
|
|
maxFileSize: this.draggedField.type === 'file' ? 5 : 0,
|
|
predefined: false,
|
|
uploadedFile: null
|
|
};
|
|
|
|
this.stages[this.currentStage].fields.push(newField);
|
|
this.selectField(newField);
|
|
this.draggedField = null;
|
|
}
|
|
},
|
|
|
|
startFieldDrag(event, index) {
|
|
this.draggedFieldIndex = index;
|
|
event.dataTransfer.setData('text/plain', 'reorder');
|
|
event.dataTransfer.effectAllowed = 'move';
|
|
},
|
|
|
|
allowFieldDrop(event) {
|
|
event.preventDefault();
|
|
},
|
|
|
|
dropField(event, targetIndex) {
|
|
event.preventDefault();
|
|
|
|
if (this.draggedFieldIndex !== null && this.draggedFieldIndex !== targetIndex) {
|
|
const draggedField = this.stages[this.currentStage].fields[this.draggedFieldIndex];
|
|
this.stages[this.currentStage].fields.splice(this.draggedFieldIndex, 1);
|
|
|
|
// Adjust target index if we removed an element before it
|
|
const adjustedIndex = this.draggedFieldIndex < targetIndex ? targetIndex - 1 : targetIndex;
|
|
this.stages[this.currentStage].fields.splice(adjustedIndex, 0, draggedField);
|
|
}
|
|
|
|
this.draggedFieldIndex = null;
|
|
},
|
|
|
|
addStage() {
|
|
const newStage = {
|
|
id: this.nextStageId++,
|
|
name: `Custom Stage ${this.stages.filter(s => !s.predefined).length + 1}`,
|
|
predefined: false,
|
|
fields: []
|
|
};
|
|
this.stages.push(newStage);
|
|
this.currentStage = this.stages.length - 1;
|
|
},
|
|
|
|
removeStage(index) {
|
|
// Prevent removal of all predefined stages
|
|
const predefinedStages = this.stages.filter(s => s.predefined).length;
|
|
const isPredefined = this.stages[index].predefined;
|
|
|
|
if (isPredefined && predefinedStages <= 1 && this.stages.length === 1) {
|
|
return; // Can't remove the last predefined stage if it's the only stage
|
|
}
|
|
|
|
if (!isPredefined || this.stages.length > 1) {
|
|
this.stages.splice(index, 1);
|
|
if (this.currentStage >= this.stages.length) {
|
|
this.currentStage = this.stages.length - 1;
|
|
}
|
|
}
|
|
},
|
|
|
|
removeField(index) {
|
|
// Only allow removal of non-predefined fields
|
|
if (!this.stages[this.currentStage].fields[index].predefined) {
|
|
this.stages[this.currentStage].fields.splice(index, 1);
|
|
if (this.selectedField && this.selectedField.id === this.stages[this.currentStage].fields[index]?.id) {
|
|
this.selectedField = null;
|
|
}
|
|
}
|
|
},
|
|
|
|
selectField(field) {
|
|
this.selectedField = field;
|
|
},
|
|
|
|
clearSelection() {
|
|
this.selectedField = null;
|
|
},
|
|
|
|
addOption() {
|
|
if (this.selectedField && (this.selectedField.type === 'select' || this.selectedField.type === 'radio' || this.selectedField.type === 'checkbox')) {
|
|
this.selectedField.options.push('New Option');
|
|
}
|
|
},
|
|
|
|
removeOption(index) {
|
|
if (this.selectedField && this.selectedField.options.length > 1) {
|
|
this.selectedField.options.splice(index, 1);
|
|
}
|
|
},
|
|
|
|
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';
|
|
},
|
|
|
|
hasRequiredFields(stage) {
|
|
return stage.fields.some(field => field.required);
|
|
},
|
|
|
|
openRenameModal(stage) {
|
|
this.stageToRename = stage;
|
|
this.showRenameModal = true;
|
|
// Focus the input after modal opens
|
|
this.$nextTick(() => {
|
|
if (this.$refs.stageNameInput) {
|
|
this.$refs.stageNameInput.focus();
|
|
}
|
|
});
|
|
},
|
|
|
|
closeRenameModal() {
|
|
this.showRenameModal = false;
|
|
this.stageToRename = null;
|
|
},
|
|
|
|
saveStageName() {
|
|
if (this.stageToRename.name.trim() === '') {
|
|
alert('Stage name cannot be empty');
|
|
return;
|
|
}
|
|
this.closeRenameModal();
|
|
},
|
|
|
|
triggerFileInput(field) {
|
|
// Trigger the hidden file input click
|
|
this.$refs['fileInput' + field.id].click();
|
|
},
|
|
|
|
handleFileUpload(event, field) {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
// Validate file type
|
|
const allowedTypes = field.fileTypes.split(',').map(type => type.trim());
|
|
const fileType = '.' + file.name.split('.').pop().toLowerCase();
|
|
|
|
if (!allowedTypes.includes(fileType)) {
|
|
alert(`Invalid file type. Please upload one of these types: ${field.fileTypes}`);
|
|
return;
|
|
}
|
|
|
|
// Validate file size (in MB)
|
|
const fileSizeMB = file.size / (1024 * 1024);
|
|
if (fileSizeMB > field.maxFileSize) {
|
|
alert(`File size exceeds ${field.maxFileSize}MB limit.`);
|
|
return;
|
|
}
|
|
|
|
// Store the file in the field
|
|
field.uploadedFile = file;
|
|
}
|
|
},
|
|
|
|
removeUploadedFile(field) {
|
|
field.uploadedFile = null;
|
|
// Reset the file input
|
|
this.$refs['fileInput' + field.id].value = '';
|
|
},
|
|
|
|
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];
|
|
},
|
|
|
|
saveForm() {
|
|
// In a real app, this would send data to a server
|
|
// For demo purposes, show which files are uploaded
|
|
const uploadedFiles = [];
|
|
this.stages.forEach(stage => {
|
|
stage.fields.forEach(field => {
|
|
if (field.type === 'file' && field.uploadedFile) {
|
|
uploadedFiles.push({
|
|
stage: stage.name,
|
|
field: field.label,
|
|
file: field.uploadedFile.name
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
if (uploadedFiles.length > 0) {
|
|
let message = 'Form saved successfully!\n\nUploaded files:\n';
|
|
uploadedFiles.forEach(upload => {
|
|
message += `- ${upload.stage}: ${upload.field} (${upload.file})\n`;
|
|
});
|
|
alert(message);
|
|
} else {
|
|
alert('Form saved successfully! (No files uploaded)');
|
|
}
|
|
|
|
console.log('Form ', this.stages);
|
|
}
|
|
}
|
|
}).mount();
|
|
</script>
|
|
</body>
|
|
</html> |