<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Entry & Management System</title>
<!-- Thai Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Font Awesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #4a6fdc;
--secondary-color: #f8f9fa;
--accent-color: #ff6b6b;
--success-color: #28a745;
--warning-color: #ffc107;
--danger-color: #dc3545;
--text-color: #333;
--border-color: #ddd;
--shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Sarabun', sans-serif;
background-color: #f5f7fa;
color: var(--text-color);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: white;
box-shadow: var(--shadow);
padding: 20px 0;
margin-bottom: 30px;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
h1 {
color: var(--primary-color);
font-weight: 600;
}
.tabs {
display: flex;
border-bottom: 2px solid var(--border-color);
margin-bottom: 30px;
}
.tab {
padding: 15px 25px;
cursor: pointer;
background: none;
border: none;
font-size: 16px;
font-weight: 500;
color: #777;
transition: var(--transition);
position: relative;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
}
.tab.active::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background-color: var(--primary-color);
}
.tab-content {
display: none;
animation: fadeIn 0.5s;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.card {
background-color: white;
border-radius: 8px;
box-shadow: var(--shadow);
padding: 25px;
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border-color);
}
.card-title {
font-size: 20px;
font-weight: 600;
color: var(--primary-color);
}
.btn {
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: var(--transition);
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: #3a5fc7;
}
.btn-success {
background-color: var(--success-color);
color: white;
}
.btn-success:hover {
background-color: #218838;
}
.btn-danger {
background-color: var(--danger-color);
color: white;
}
.btn-danger:hover {
background-color: #c82333;
}
.btn-secondary {
background-color: var(--secondary-color);
color: var(--text-color);
}
.btn-secondary:hover {
background-color: #e9ecef;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
input,
select,
textarea {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 4px;
font-family: inherit;
font-size: 14px;
transition: var(--transition);
}
input:focus,
select:focus,
textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(74, 111, 220, 0.2);
}
.form-row {
display: flex;
gap: 20px;
}
.form-col {
flex: 1;
}
.json-output {
background-color: #f8f9fa;
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 15px;
font-family: monospace;
font-size: 14px;
white-space: pre-wrap;
max-height: 400px;
overflow-y: auto;
}
.form-element {
background-color: var(--secondary-color);
border-radius: 4px;
padding: 15px;
margin-bottom: 15px;
position: relative;
}
.element-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.element-type {
font-weight: 600;
color: var(--primary-color);
}
.element-actions {
display: flex;
gap: 10px;
}
.icon-btn {
background: none;
border: none;
cursor: pointer;
color: #777;
font-size: 16px;
transition: var(--transition);
}
.icon-btn:hover {
color: var(--primary-color);
}
.icon-btn.delete:hover {
color: var(--danger-color);
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th,
.data-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.data-table th {
background-color: var(--secondary-color);
font-weight: 600;
}
.data-table tr:hover {
background-color: #f8f9fa;
}
.table-actions {
display: flex;
gap: 10px;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 4px;
color: white;
font-weight: 500;
box-shadow: var(--shadow);
z-index: 1000;
transform: translateX(120%);
transition: transform 0.3s ease;
}
.notification.show {
transform: translateX(0);
}
.notification.success {
background-color: var(--success-color);
}
.notification.error {
background-color: var(--danger-color);
}
.notification.warning {
background-color: var(--warning-color);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background-color: white;
border-radius: 8px;
padding: 25px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-title {
font-size: 20px;
font-weight: 600;
color: var(--primary-color);
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #777;
}
.close-btn:hover {
color: var(--danger-color);
}
.validation-message {
color: var(--danger-color);
font-size: 12px;
margin-top: 5px;
display: none;
}
.validation-message.show {
display: block;
}
.checkbox-group,
.radio-group {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.checkbox-item,
.radio-item {
display: flex;
align-items: center;
gap: 8px;
}
.file-upload {
position: relative;
display: inline-block;
cursor: pointer;
width: 100%;
}
.file-upload input[type=file] {
position: absolute;
left: -9999px;
}
.file-upload-label {
display: block;
padding: 10px 15px;
border: 1px dashed var(--border-color);
border-radius: 4px;
text-align: center;
transition: var(--transition);
}
.file-upload-label:hover {
border-color: var(--primary-color);
background-color: rgba(74, 111, 220, 0.05);
}
@media (max-width: 768px) {
.form-row {
flex-direction: column;
gap: 0;
}
.tabs {
overflow-x: auto;
}
.table-actions {
flex-direction: column;
gap: 5px;
}
.data-table {
font-size: 14px;
}
.data-table th,
.data-table td {
padding: 8px 10px;
}
}
</style>
</head>
<body>
<header>
<div class="header-content">
<h1><i class="fas fa-database"></i> Data Entry & Management System</h1>
<div class="header-actions">
<button class="btn btn-secondary" onclick="exportData()">
<i class="fas fa-download"></i> Export Data
</button>
</div>
</div>
</header>
<div class="container">
<div class="tabs">
<button class="tab active" onclick="switchTab('form-builder')">
<i class="fas fa-wrench"></i> Form Builder
</button>
<button class="tab" onclick="switchTab('form-renderer')">
<i class="fas fa-file-alt"></i> Form Renderer
</button>
<button class="tab" onclick="switchTab('data-management')">
<i class="fas fa-table"></i> Data Management
</button>
</div>
<!-- Form Builder Tab -->
<div id="form-builder" class="tab-content active">
<div class="card">
<div class="card-header">
<h2 class="card-title">Form Builder</h2>
<button class="btn btn-primary" onclick="addElement()">
<i class="fas fa-plus"></i> Add Element
</button>
</div>
<div id="form-elements-container">
<!-- Form elements will be added here dynamically -->
</div>
<div class="form-group">
<button class="btn btn-success" onclick="generateJSON()">
<i class="fas fa-code"></i> Generate JSON Schema
</button>
<button class="btn btn-secondary" onclick="loadSampleForm()">
<i class="fas fa-file-import"></i> Load Sample Form
</button>
</div>
<div id="json-output-container" style="display: none;">
<h3>Generated JSON Schema:</h3>
<div id="json-output" class="json-output"></div>
</div>
</div>
</div>
<!-- Form Renderer Tab -->
<div id="form-renderer" class="tab-content">
<div class="card">
<div class="card-header">
<h2 class="card-title">Form Renderer</h2>
<button class="btn btn-secondary" onclick="loadSampleSchema()">
<i class="fas fa-file-import"></i> Load Sample Schema
</button>
</div>
<div class="form-group">
<label for="schema-input">Form Schema (JSON):</label>
<textarea id="schema-input" rows="10" placeholder="Paste your form schema JSON here..."></textarea>
</div>
<div class="form-group">
<button class="btn btn-primary" onclick="renderForm()">
<i class="fas fa-play"></i> Render Form
</button>
</div>
<div id="rendered-form-container" style="display: none;">
<h3>Rendered Form:</h3>
<form id="rendered-form">
<!-- Form will be rendered here -->
</form>
<div class="form-group">
<button type="button" class="btn btn-success" onclick="submitForm()">
<i class="fas fa-save"></i> Submit Form
</button>
<button type="button" class="btn btn-secondary" onclick="resetForm()">
<i class="fas fa-redo"></i> Reset
</button>
</div>
</div>
</div>
</div>
<!-- Data Management Tab -->
<div id="data-management" class="tab-content">
<div class="card">
<div class="card-header">
<h2 class="card-title">Data Management</h2>
<button class="btn btn-primary" onclick="refreshData()">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
<div id="data-table-container">
<table class="data-table">
<thead id="table-header">
<!-- Table headers will be generated here -->
</thead>
<tbody id="table-body">
<!-- Table rows will be generated here -->
</tbody>
</table>
<div id="no-data-message" style="text-align: center; padding: 30px; display: none;">
<i class="fas fa-inbox" style="font-size: 48px; color: #ccc; margin-bottom: 15px;"></i>
<p>No data available. Submit a form to see data here.</p>
</div>
</div>
</div>
</div>
</div>
<!-- Edit Modal -->
<div id="edit-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Edit Data</h3>
<button class="close-btn" onclick="closeEditModal()">×</button>
</div>
<div class="modal-body">
<form id="edit-form">
<!-- Edit form will be generated here -->
</form>
<div class="form-group">
<button type="button" class="btn btn-success" onclick="saveEdit()">
<i class="fas fa-save"></i> Save Changes
</button>
<button type="button" class="btn btn-secondary" onclick="closeEditModal()">
<i class="fas fa-times"></i> Cancel
</button>
</div>
</div>
</div>
</div>
<!-- Notification -->
<div id="notification" class="notification"></div>
<script>
// Global variables
let formElements = [];
let currentSchema = null;
let formData = [];
let editingIndex = -1;
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
// Load sample data on page load
loadSampleData();
});
// Tab switching
function switchTab(tabId) {
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// Remove active class from all tabs
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Show selected tab content
document.getElementById(tabId).classList.add('active');
// Add active class to clicked tab
event.target.classList.add('active');
// Refresh data if switching to data management tab
if (tabId === 'data-management') {
refreshData();
}
}
// Form Builder Functions
function addElement() {
const elementId = 'element-' + Date.now();
const element = {
id: elementId,
type: 'text',
label: 'New Field',
name: 'new_field',
placeholder: 'Enter value',
required: false,
validation: {}
};
formElements.push(element);
renderFormElements();
}
function renderFormElements() {
const container = document.getElementById('form-elements-container');
container.innerHTML = '';
formElements.forEach((element, index) => {
const elementDiv = document.createElement('div');
elementDiv.className = 'form-element';
elementDiv.id = element.id;
elementDiv.innerHTML = `
<div class="element-header">
<span class="element-type">${element.type.charAt(0).toUpperCase() + element.type.slice(1)} Field</span>
<div class="element-actions">
<button class="icon-btn" onclick="moveElement(${index}, 'up')">
<i class="fas fa-arrow-up"></i>
</button>
<button class="icon-btn" onclick="moveElement(${index}, 'down')">
<i class="fas fa-arrow-down"></i>
</button>
<button class="icon-btn delete" onclick="deleteElement(${index})">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label>Field Type</label>
<select onchange="updateElementType(${index}, this.value)">
<option value="text" ${element.type === 'text' ? 'selected' : ''}>Text</option>
<option value="textarea" ${element.type === 'textarea' ? 'selected' : ''}>Textarea</option>
<option value="number" ${element.type === 'number' ? 'selected' : ''}>Number</option>
<option value="date" ${element.type === 'date' ? 'selected' : ''}>Date</option>
<option value="select" ${element.type === 'select' ? 'selected' : ''}>Select</option>
<option value="checkbox" ${element.type === 'checkbox' ? 'selected' : ''}>Checkbox</option>
<option value="radio" ${element.type === 'radio' ? 'selected' : ''}>Radio</option>
<option value="file" ${element.type === 'file' ? 'selected' : ''}>File Upload</option>
</select>
</div>
</div>
<div class="form-col">
<div class="form-group">
<label>Field Label</label>
<input type="text" value="${element.label}" onchange="updateElementProperty(${index}, 'label', this.value)">
</div>
</div>
</div>
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label>Field Name</label>
<input type="text" value="${element.name}" onchange="updateElementProperty(${index}, 'name', this.value)">
</div>
</div>
<div class="form-col">
<div class="form-group">
<label>Placeholder</label>
<input type="text" value="${element.placeholder}" onchange="updateElementProperty(${index}, 'placeholder', this.value)">
</div>
</div>
</div>
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label>
<input type="checkbox" ${element.required ? 'checked' : ''} onchange="updateElementProperty(${index}, 'required', this.checked)">
Required
</label>
</div>
</div>
<div class="form-col">
<div class="form-group">
<button class="btn btn-secondary" onclick="toggleValidationOptions(${index})">
<i class="fas fa-cog"></i> Validation Options
</button>
</div>
</div>
</div>
<div id="validation-${index}" class="validation-options" style="display: none; margin-top: 15px;">
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label>Min Length</label>
<input type="number" value="${element.validation.minLength || ''}" onchange="updateValidationProperty(${index}, 'minLength', this.value)">
</div>
</div>
<div class="form-col">
<div class="form-group">
<label>Max Length</label>
<input type="number" value="${element.validation.maxLength || ''}" onchange="updateValidationProperty(${index}, 'maxLength', this.value)">
</div>
</div>
</div>
<div class="form-row">
<div class="form-col">
<div class="form-group">
<label>Min Value</label>
<input type="number" value="${element.validation.min || ''}" onchange="updateValidationProperty(${index}, 'min', this.value)">
</div>
</div>
<div class="form-col">
<div class="form-group">
<label>Max Value</label>
<input type="number" value="${element.validation.max || ''}" onchange="updateValidationProperty(${index}, 'max', this.value)">
</div>
</div>
</div>
<div class="form-group">
<label>Pattern (Regex)</label>
<input type="text" value="${element.validation.pattern || ''}" onchange="updateValidationProperty(${index}, 'pattern', this.value)">
</div>
<div class="form-group">
<label>
<input type="checkbox" ${element.validation.email ? 'checked' : ''} onchange="updateValidationProperty(${index}, 'email', this.checked)">
Email Validation
</label>
</div>
${element.type === 'select' || element.type === 'radio' ? `
<div class="form-group">
<label>Options (comma-separated)</label>
<input type="text" value="${element.options ? element.options.join(', ') : ''}" onchange="updateElementProperty(${index}, 'options', this.value.split(',').map(opt => opt.trim()))">
</div>
` : ''}
</div>
`;
container.appendChild(elementDiv);
});
}
function updateElementType(index, type) {
formElements[index].type = type;
renderFormElements();
}
function updateElementProperty(index, property, value) {
formElements[index][property] = value;
}
function updateValidationProperty(index, property, value) {
if (!formElements[index].validation) {
formElements[index].validation = {};
}
if (value === '') {
delete formElements[index].validation[property];
} else {
formElements[index].validation[property] = property === 'minLength' || property === 'maxLength' || property === 'min' || property === 'max' ?
(value ? parseInt(value) : undefined) : value;
}
}
function toggleValidationOptions(index) {
const validationDiv = document.getElementById(`validation-${index}`);
validationDiv.style.display = validationDiv.style.display === 'none' ? 'block' : 'none';
}
function moveElement(index, direction) {
if (direction === 'up' && index > 0) {
[formElements[index], formElements[index - 1]] = [formElements[index - 1], formElements[index]];
} else if (direction === 'down' && index < formElements.length - 1) {
[formElements[index], formElements[index + 1]] = [formElements[index + 1], formElements[index]];
}
renderFormElements();
}
function deleteElement(index) {
formElements.splice(index, 1);
renderFormElements();
}
function generateJSON() {
const schema = {
formTitle: "Generated Form",
formDescription: "This form was generated using the Form Builder",
elements: formElements
};
const jsonOutput = document.getElementById('json-output');
jsonOutput.textContent = JSON.stringify(schema, null, 2);
document.getElementById('json-output-container').style.display = 'block';
showNotification('Form schema generated successfully!', 'success');
}
function loadSampleForm() {
formElements = [
{
id: 'element-1',
type: 'text',
label: 'First Name',
name: 'first_name',
placeholder: 'Enter your first name',
required: true,
validation: {
minLength: 2,
maxLength: 50
}
},
{
id: 'element-2',
type: 'text',
label: 'Last Name',
name: 'last_name',
placeholder: 'Enter your last name',
required: true,
validation: {
minLength: 2,
maxLength: 50
}
},
{
id: 'element-3',
type: 'email',
label: 'Email',
name: 'email',
placeholder: 'Enter your email',
required: true,
validation: {
email: true
}
},
{
id: 'element-4',
type: 'number',
label: 'Age',
name: 'age',
placeholder: 'Enter your age',
required: true,
validation: {
min: 18,
max: 120
}
},
{
id: 'element-5',
type: 'date',
label: 'Birth Date',
name: 'birth_date',
placeholder: '',
required: false,
validation: {}
},
{
id: 'element-6',
type: 'select',
label: 'Country',
name: 'country',
placeholder: 'Select your country',
required: true,
validation: {},
options: ['Thailand', 'United States', 'United Kingdom', 'Canada', 'Australia', 'Other']
},
{
id: 'element-7',
type: 'checkbox',
label: 'Interests',
name: 'interests',
placeholder: '',
required: false,
validation: {},
options: ['Sports', 'Music', 'Travel', 'Reading', 'Technology']
},
{
id: 'element-8',
type: 'textarea',
label: 'Comments',
name: 'comments',
placeholder: 'Enter your comments',
required: false,
validation: {
maxLength: 500
}
}
];
renderFormElements();
showNotification('Sample form loaded successfully!', 'success');
}
// Form Renderer Functions
function loadSampleSchema() {
const sampleSchema = {
formTitle: "User Registration Form",
formDescription: "Please fill out this form to register",
elements: [
{
id: "element-1",
type: "text",
label: "First Name",
name: "first_name",
placeholder: "Enter your first name",
required: true,
validation: {
minLength: 2,
maxLength: 50
}
},
{
id: "element-2",
type: "text",
label: "Last Name",
name: "last_name",
placeholder: "Enter your last name",
required: true,
validation: {
minLength: 2,
maxLength: 50
}
},
{
id: "element-3",
type: "email",
label: "Email",
name: "email",
placeholder: "Enter your email",
required: true,
validation: {
email: true
}
},
{
id: "element-4",
type: "number",
label: "Age",
name: "age",
placeholder: "Enter your age",
required: true,
validation: {
min: 18,
max: 120
}
},
{
id: "element-5",
type: "date",
label: "Birth Date",
name: "birth_date",
placeholder: "",
required: false,
validation: {}
},
{
id: "element-6",
type: "select",
label: "Country",
name: "country",
placeholder: "Select your country",
required: true,
validation: {},
options: ["Thailand", "United States", "United Kingdom", "Canada", "Australia", "Other"]
},
{
id: "element-7",
type: "checkbox",
label: "Interests",
name: "interests",
placeholder: "",
required: false,
validation: {},
options: ["Sports", "Music", "Travel", "Reading", "Technology"]
},
{
id: "element-8",
type: "textarea",
label: "Comments",
name: "comments",
placeholder: "Enter your comments",
required: false,
validation: {
maxLength: 500
}
}
]
};
document.getElementById('schema-input').value = JSON.stringify(sampleSchema, null, 2);
showNotification('Sample schema loaded!', 'success');
}
function renderForm() {
const schemaInput = document.getElementById('schema-input').value;
try {
currentSchema = JSON.parse(schemaInput);
const formContainer = document.getElementById('rendered-form');
formContainer.innerHTML = '';
currentSchema.elements.forEach(element => {
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
let fieldHtml = '';
switch (element.type) {
case 'text':
case 'email':
case 'number':
case 'date':
fieldHtml = `
<label for="${element.name}">${element.label}${element.required ? ' *' : ''}</label>
<input type="${element.type}" id="${element.name}" name="${element.name}" placeholder="${element.placeholder}" ${element.required ? 'required' : ''}>
<div id="${element.name}-error" class="validation-message"></div>
`;
break;
case 'textarea':
fieldHtml = `
<label for="${element.name}">${element.label}${element.required ? ' *' : ''}</label>
<textarea id="${element.name}" name="${element.name}" placeholder="${element.placeholder}" ${element.required ? 'required' : ''}></textarea>
<div id="${element.name}-error" class="validation-message"></div>
`;
break;
case 'select':
fieldHtml = `
<label for="${element.name}">${element.label}${element.required ? ' *' : ''}</label>
<select id="${element.name}" name="${element.name}" ${element.required ? 'required' : ''}>
<option value="">${element.placeholder}</option>
${element.options.map(option => `<option value="${option}">${option}</option>`).join('')}
</select>
<div id="${element.name}-error" class="validation-message"></div>
`;
break;
case 'checkbox':
fieldHtml = `
<label>${element.label}${element.required ? ' *' : ''}</label>
<div class="checkbox-group">
${element.options.map(option => `
<div class="checkbox-item">
<input type="checkbox" id="${element.name}_${option}" name="${element.name}" value="${option}">
<label for="${element.name}_${option}">${option}</label>
</div>
`).join('')}
</div>
<div id="${element.name}-error" class="validation-message"></div>
`;
break;
case 'radio':
fieldHtml = `
<label>${element.label}${element.required ? ' *' : ''}</label>
<div class="radio-group">
${element.options.map(option => `
<div class="radio-item">
<input type="radio" id="${element.name}_${option}" name="${element.name}" value="${option}">
<label for="${element.name}_${option}">${option}</label>
</div>
`).join('')}
</div>
<div id="${element.name}-error" class="validation-message"></div>
`;
break;
case 'file':
fieldHtml = `
<label for="${element.name}">${element.label}${element.required ? ' *' : ''}</label>
<div class="file-upload">
<input type="file" id="${element.name}" name="${element.name}" ${element.required ? 'required' : ''}>
<label for="${element.name}" class="file-upload-label">
<i class="fas fa-cloud-upload-alt"></i> Choose File
</label>
</div>
<div id="${element.name}-error" class="validation-message"></div>
`;
break;
}
formGroup.innerHTML = fieldHtml;
formContainer.appendChild(formGroup);
});
document.getElementById('rendered-form-container').style.display = 'block';
showNotification('Form rendered successfully!', 'success');
} catch (error) {
showNotification('Invalid JSON schema: ' + error.message, 'error');
}
}
function validateForm() {
if (!currentSchema) return false;
let isValid = true;
currentSchema.elements.forEach(element => {
const field = document.getElementsByName(element.name)[0];
const errorElement = document.getElementById(`${element.name}-error`);
if (!field) return;
let value = '';
if (element.type === 'checkbox') {
const checkboxes = document.getElementsByName(element.name);
value = Array.from(checkboxes)
.filter(cb => cb.checked)
.map(cb => cb.value)
.join(', ');
} else if (element.type === 'file') {
value = field.files.length > 0 ? field.files[0].name : '';
} else {
value = field.value.trim();
}
// Reset error message
errorElement.textContent = '';
errorElement.classList.remove('show');
// Check if required
if (element.required && !value) {
errorElement.textContent = `${element.label} is required`;
errorElement.classList.add('show');
isValid = false;
return;
}
// Skip validation if field is empty and not required
if (!value) return;
// Apply validation rules
if (element.validation) {
// Min length
if (element.validation.minLength && value.length < element.validation.minLength) {
errorElement.textContent = `${element.label} must be at least ${element.validation.minLength} characters`;
errorElement.classList.add('show');
isValid = false;
return;
}
// Max length
if (element.validation.maxLength && value.length > element.validation.maxLength) {
errorElement.textContent = `${element.label} must be no more than ${element.validation.maxLength} characters`;
errorElement.classList.add('show');
isValid = false;
return;
}
// Min value
if (element.validation.min && parseFloat(value) < element.validation.min) {
errorElement.textContent = `${element.label} must be at least ${element.validation.min}`;
errorElement.classList.add('show');
isValid = false;
return;
}
// Max value
if (element.validation.max && parseFloat(value) > element.validation.max) {
errorElement.textContent = `${element.label} must be no more than ${element.validation.max}`;
errorElement.classList.add('show');
isValid = false;
return;
}
// Pattern
if (element.validation.pattern) {
const regex = new RegExp(element.validation.pattern);
if (!regex.test(value)) {
errorElement.textContent = `${element.label} format is invalid`;
errorElement.classList.add('show');
isValid = false;
return;
}
}
// Email
if (element.validation.email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
errorElement.textContent = `${element.label} must be a valid email address`;
errorElement.classList.add('show');
isValid = false;
return;
}
}
}
});
return isValid;
}
function submitForm() {
if (!validateForm()) {
showNotification('Please fix the errors in the form', 'error');
return;
}
const formDataObj = {};
currentSchema.elements.forEach(element => {
const field = document.getElementsByName(element.name)[0];
if (!field) return;
let value = '';
if (element.type === 'checkbox') {
const checkboxes = document.getElementsByName(element.name);
value = Array.from(checkboxes)
.filter(cb => cb.checked)
.map(cb => cb.value)
.join(', ');
} else if (element.type === 'file') {
value = field.files.length > 0 ? field.files[0].name : '';
} else {
value = field.value.trim();
}
formDataObj[element.name] = value;
});
// Add timestamp
formDataObj.id = Date.now();
formDataObj.submittedAt = new Date().toISOString();
// Save to mock database
formData.push(formDataObj);
localStorage.setItem('formData', JSON.stringify(formData));
showNotification('Form submitted successfully!', 'success');
// Reset form
document.getElementById('rendered-form').reset();
}
function resetForm() {
document.getElementById('rendered-form').reset();
// Clear all validation messages
document.querySelectorAll('.validation-message').forEach(element => {
element.textContent = '';
element.classList.remove('show');
});
}
// Data Management Functions
function loadSampleData() {
const storedData = localStorage.getItem('formData');
if (storedData) {
formData = JSON.parse(storedData);
} else {
// Load sample data if no data is stored
formData = [
{
id: 1,
first_name: "John",
last_name: "Doe",
email: "john.doe@example.com",
age: "30",
birth_date: "1993-05-15",
country: "United States",
interests: "Sports, Music",
comments: "Looking forward to using this system!",
submittedAt: "2023-07-15T10:30:00.000Z"
},
{
id: 2,
first_name: "Jane",
last_name: "Smith",
email: "jane.smith@example.com",
age: "28",
birth_date: "1995-09-22",
country: "United Kingdom",
interests: "Travel, Reading",
comments: "Great form design!",
submittedAt: "2023-07-16T14:45:00.000Z"
}
];
localStorage.setItem('formData', JSON.stringify(formData));
}
}
function refreshData() {
const tableHeader = document.getElementById('table-header');
const tableBody = document.getElementById('table-body');
const noDataMessage = document.getElementById('no-data-message');
// Clear existing table
tableHeader.innerHTML = '';
tableBody.innerHTML = '';
if (formData.length === 0) {
noDataMessage.style.display = 'block';
return;
}
noDataMessage.style.display = 'none';
// Get column names from the first data object
const columns = Object.keys(formData[0]);
// Create table headers
const headerRow = document.createElement('tr');
// Add ID column
const idHeader = document.createElement('th');
idHeader.textContent = 'ID';
headerRow.appendChild(idHeader);
// Add other columns (excluding submittedAt for cleaner view)
columns.forEach(column => {
if (column !== 'id' && column !== 'submittedAt') {
const th = document.createElement('th');
th.textContent = column.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
headerRow.appendChild(th);
}
});
// Add actions column
const actionsHeader = document.createElement('th');
actionsHeader.textContent = 'Actions';
headerRow.appendChild(actionsHeader);
tableHeader.appendChild(headerRow);
// Create table rows
formData.forEach((item, index) => {
const row = document.createElement('tr');
// Add ID cell
const idCell = document.createElement('td');
idCell.textContent = item.id;
row.appendChild(idCell);
// Add other cells
columns.forEach(column => {
if (column !== 'id' && column !== 'submittedAt') {
const cell = document.createElement('td');
cell.textContent = item[column] || '-';
row.appendChild(cell);
}
});
// Add actions cell
const actionsCell = document.createElement('td');
actionsCell.innerHTML = `
<div class="table-actions">
<button class="btn btn-secondary" onclick="viewData(${index})">
<i class="fas fa-eye"></i> View
</button>
<button class="btn btn-primary" onclick="editData(${index})">
<i class="fas fa-edit"></i> Edit
</button>
<button class="btn btn-danger" onclick="deleteData(${index})">
<i class="fas fa-trash"></i> Delete
</button>
</div>
`;
row.appendChild(actionsCell);
tableBody.appendChild(row);
});
}
function viewData(index) {
const item = formData[index];
let content = '<div style="max-height: 400px; overflow-y: auto;">';
Object.keys(item).forEach(key => {
if (key !== 'id') {
content += `
<div class="form-group">
<label>${key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:</label>
<div style="padding: 10px; background-color: #f8f9fa; border-radius: 4px;">${item[key] || '-'}</div>
</div>
`;
}
});
content += '</div>';
document.getElementById('edit-form').innerHTML = content;
document.getElementById('edit-modal').classList.add('show');
}
function editData(index) {
editingIndex = index;
const item = formData[index];
let formHtml = '';
Object.keys(item).forEach(key => {
if (key !== 'id' && key !== 'submittedAt') {
formHtml += `
<div class="form-group">
<label for="edit-${key}">${key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:</label>
<input type="text" id="edit-${key}" value="${item[key] || ''}">
</div>
`;
}
});
document.getElementById('edit-form').innerHTML = formHtml;
document.getElementById('edit-modal').classList.add('show');
}
function saveEdit() {
if (editingIndex === -1) return;
const item = formData[editingIndex];
Object.keys(item).forEach(key => {
if (key !== 'id' && key !== 'submittedAt') {
const field = document.getElementById(`edit-${key}`);
if (field) {
item[key] = field.value;
}
}
});
// Update the data in localStorage
localStorage.setItem('formData', JSON.stringify(formData));
closeEditModal();
refreshData();
showNotification('Data updated successfully!', 'success');
}
function closeEditModal() {
document.getElementById('edit-modal').classList.remove('show');
editingIndex = -1;
}
function deleteData(index) {
if (confirm('Are you sure you want to delete this data?')) {
formData.splice(index, 1);
localStorage.setItem('formData', JSON.stringify(formData));
refreshData();
showNotification('Data deleted successfully!', 'success');
}
}
function exportData() {
const dataStr = JSON.stringify(formData, null, 2);
const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
const exportFileDefaultName = 'form_data_' + new Date().toISOString().slice(0, 10) + '.json';
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
showNotification('Data exported successfully!', 'success');
}
function showNotification(message, type) {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = 'notification ' + type;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
</script>
</body>
</html>