@@ -77,8 +77,16 @@
|
||||
locationDisplay = `📍 ${currentLoc}`;
|
||||
}
|
||||
|
||||
let manageProductsLink = '';
|
||||
if (data.permissions && data.permissions.manage_products) {
|
||||
manageProductsLink = ` | <a href="{{ url_for('products.product_manager') }}" style="color: #6a4c93; text-decoration: none; font-weight: 500; cursor: pointer;"
|
||||
onmouseover="this.style.textDecoration='underline'"
|
||||
onmouseout="this.style.textDecoration='none'"
|
||||
title="Manage Products">Manage Products</a>`;
|
||||
}
|
||||
|
||||
document.getElementById('userLocationInfo').innerHTML =
|
||||
`👤 ${data.username} | ${locationDisplay}`;
|
||||
`👤 ${data.username} | ${locationDisplay}${manageProductsLink}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading user info:', error);
|
||||
@@ -93,6 +101,8 @@
|
||||
window.APP_BASE_URL = '';
|
||||
// Set data path for the quiz
|
||||
window.DATA_PATH = '{{ url_for("products.serve_data", filename="") }}';
|
||||
// Set initial product if provided via URL
|
||||
window.INITIAL_PRODUCT = '{{ initial_product or "" }}';
|
||||
</script>
|
||||
<script src="{{ url_for('products.serve_js', filename='script.js') }}"></script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,762 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Product Manager - CGW</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 30px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.form-section:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
label .optional {
|
||||
color: #999;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.note {
|
||||
background: #fff9e6;
|
||||
border-left: 4px solid #ffd700;
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.product-codes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.type-group {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.radio-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.radio-option input[type="radio"] {
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-option label {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.dynamic-section {
|
||||
background: #f9f9f9;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dynamic-section.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 12px 30px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #5568d3;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #e0e0e0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #d0d0d0;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ff4757;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #e84052;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
margin-right: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.status-default {
|
||||
background: #4caf50;
|
||||
}
|
||||
|
||||
.status-discontinued {
|
||||
background: #ff4757;
|
||||
}
|
||||
|
||||
.status-hidden {
|
||||
background: #ffa502;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.type-group {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.product-codes-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.product-codes-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="nav-links">
|
||||
<a href="/">← Back to Home</a>
|
||||
<a href="/quiz/">Product Finder</a>
|
||||
</div>
|
||||
|
||||
<h1>Product Manager</h1>
|
||||
<p class="subtitle">Add or edit product information</p>
|
||||
|
||||
<form id="productForm">
|
||||
<!-- Product Location & Description -->
|
||||
<div class="form-section">
|
||||
<div class="form-group">
|
||||
<label for="location">Product Location:</label>
|
||||
<select id="location" name="location" required>
|
||||
<option value="">Select location...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" placeholder="Enter product description"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="status">Status</label>
|
||||
<select id="status" name="status">
|
||||
<option value="default">Default</option>
|
||||
<option value="discontinued">Discontinued</option>
|
||||
<option value="hidden">Hide</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Codes -->
|
||||
<div class="form-section">
|
||||
<div class="section-title">Product Codes</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="standardCode">Standardized Code <span class="optional">(auto-generated from selections below)</span></label>
|
||||
<input type="text" id="standardCode" name="standardCode" placeholder="Select type and modifiers to generate" readonly style="background-color: #f5f5f5;">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="baseCode">Base Code <span class="optional">(optional - e.g., 2000, 3000)</span></label>
|
||||
<input type="text" id="baseCode" name="baseCode" placeholder="Enter base product code">
|
||||
</div>
|
||||
|
||||
<div class="product-codes-grid" id="locationCodesContainer">
|
||||
<!-- Dynamic location code fields will be inserted here based on user's accessible locations -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Type -->
|
||||
<div class="form-section">
|
||||
<div class="section-title">Product Type</div>
|
||||
|
||||
<div class="type-group">
|
||||
<div class="form-group">
|
||||
<label for="productType">Type</label>
|
||||
<select id="productType" name="productType">
|
||||
<option value="">Select type...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="productGroup">Group [Category]</label>
|
||||
<input type="text" id="productGroup" name="productGroup" placeholder="" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Code Prefixes -->
|
||||
<div class="form-section dynamic-section hidden" id="prefixesSection">
|
||||
<div class="section-title">Code Prefixes</div>
|
||||
<div class="radio-group" id="prefixesContainer">
|
||||
<!-- Dynamic prefix options will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Code Suffixes -->
|
||||
<div class="form-section dynamic-section hidden" id="suffixesSection">
|
||||
<div class="section-title">Code Suffixes</div>
|
||||
<div style="margin-bottom: 10px; font-size: 13px; color: #666;">Select all that apply (multiple selections allowed)</div>
|
||||
<div class="radio-group" id="suffixesContainer">
|
||||
<!-- Dynamic suffix options will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Materials -->
|
||||
<div class="form-section dynamic-section hidden" id="materialsSection">
|
||||
<div class="section-title">Materials</div>
|
||||
<div class="radio-group" id="materialsContainer">
|
||||
<!-- Dynamic material options will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colors -->
|
||||
<div class="form-section dynamic-section hidden" id="colorsSection">
|
||||
<div class="section-title">Colors</div>
|
||||
<div class="radio-group" id="colorsContainer">
|
||||
<!-- Dynamic color options will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Additional Options -->
|
||||
<div class="form-section dynamic-section hidden" id="additionalOptionsSection">
|
||||
<div class="section-title">Additional Options</div>
|
||||
<div class="radio-group" id="additionalOptionsContainer">
|
||||
<!-- Dynamic additional options will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="button-group">
|
||||
<button type="submit" class="btn-primary">Save Product</button>
|
||||
<button type="button" class="btn-secondary" onclick="resetForm()">Clear Form</button>
|
||||
<button type="button" class="btn-danger" onclick="deleteProduct()">Delete Product</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let productAttributes = null;
|
||||
let userAccessibleLocations = [];
|
||||
|
||||
// Load product attributes and user locations
|
||||
async function loadProductAttributes() {
|
||||
try {
|
||||
const response = await fetch('/quiz/data/product_attributes.json');
|
||||
productAttributes = await response.json();
|
||||
|
||||
// Get user's accessible locations from session
|
||||
await fetchUserLocations();
|
||||
|
||||
initializeForm();
|
||||
} catch (error) {
|
||||
console.error('Error loading product attributes:', error);
|
||||
alert('Failed to load product attributes');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch user's accessible locations from session
|
||||
async function fetchUserLocations() {
|
||||
try {
|
||||
const response = await fetch('/quiz/api/user-session');
|
||||
const userData = await response.json();
|
||||
userAccessibleLocations = userData.accessibleLocations || [];
|
||||
|
||||
// If user has no accessible locations, default to their current location
|
||||
if (userAccessibleLocations.length === 0 && userData.currentLocation) {
|
||||
userAccessibleLocations = [userData.currentLocation];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching user locations:', error);
|
||||
// Fallback to all location codes if API fails
|
||||
userAccessibleLocations = productAttributes.locations.map(loc => loc.code);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize form with data
|
||||
function initializeForm() {
|
||||
// Populate locations based on user's access
|
||||
// Match user's accessible location codes with full location details
|
||||
const locationSelect = document.getElementById('location');
|
||||
|
||||
productAttributes.locations.forEach(loc => {
|
||||
// Check if user has access to this location code
|
||||
if (userAccessibleLocations.includes(loc.code)) {
|
||||
const option = document.createElement('option');
|
||||
option.value = loc.code;
|
||||
option.textContent = loc.name;
|
||||
locationSelect.appendChild(option);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize location code fields with user's accessible locations
|
||||
updateLocationCodes();
|
||||
|
||||
// Populate product types
|
||||
const typeSelect = document.getElementById('productType');
|
||||
productAttributes.productTypes.forEach(type => {
|
||||
const option = document.createElement('option');
|
||||
option.value = type.id;
|
||||
option.textContent = type.type;
|
||||
option.dataset.category = type.category;
|
||||
option.dataset.basetype = type.baseType;
|
||||
option.dataset.materials = JSON.stringify(type.availableMaterials);
|
||||
option.dataset.colors = JSON.stringify(type.availableColors);
|
||||
option.dataset.options = JSON.stringify(type.additionalOptions);
|
||||
typeSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// Setup event listeners
|
||||
document.getElementById('productType').addEventListener('change', updateDynamicSections);
|
||||
document.getElementById('baseCode').addEventListener('input', updateStandardizedCode);
|
||||
}
|
||||
|
||||
// Update location code fields based on user's accessible locations
|
||||
function updateLocationCodes() {
|
||||
const container = document.getElementById('locationCodesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
// Get full location details for each accessible code
|
||||
userAccessibleLocations.forEach(locCode => {
|
||||
const locationDetails = productAttributes.locations.find(loc => loc.code === locCode);
|
||||
if (locationDetails) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'form-group';
|
||||
div.innerHTML = `
|
||||
<label for="code_${locCode}">${locationDetails.name} code</label>
|
||||
<input type="text" id="code_${locCode}" name="code_${locCode}" placeholder="">
|
||||
`;
|
||||
container.appendChild(div);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update dynamic sections based on product type
|
||||
function updateDynamicSections() {
|
||||
const typeSelect = document.getElementById('productType');
|
||||
const selectedOption = typeSelect.options[typeSelect.selectedIndex];
|
||||
|
||||
if (!selectedOption.value) {
|
||||
hideAllDynamicSections();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update category
|
||||
document.getElementById('productGroup').value = selectedOption.dataset.category || '';
|
||||
|
||||
// Get the baseType from the selected option
|
||||
const baseType = selectedOption.dataset.basetype || '';
|
||||
|
||||
// Update code prefixes
|
||||
if (baseType && productAttributes.codeModifiers && productAttributes.codeModifiers.prefixes) {
|
||||
const applicablePrefixes = productAttributes.codeModifiers.prefixes.filter(
|
||||
p => p.appliesTo.includes(baseType)
|
||||
);
|
||||
if (applicablePrefixes.length > 0) {
|
||||
showPrefixesSection(applicablePrefixes);
|
||||
} else {
|
||||
hidePrefixesSection();
|
||||
}
|
||||
} else {
|
||||
hidePrefixesSection();
|
||||
}
|
||||
|
||||
// Update code suffixes
|
||||
if (baseType && productAttributes.codeModifiers && productAttributes.codeModifiers.suffixes) {
|
||||
const applicableSuffixes = productAttributes.codeModifiers.suffixes.filter(
|
||||
s => s.appliesTo.includes(baseType)
|
||||
);
|
||||
if (applicableSuffixes.length > 0) {
|
||||
showSuffixesSection(applicableSuffixes);
|
||||
} else {
|
||||
hideSuffixesSection();
|
||||
}
|
||||
} else {
|
||||
hideSuffixesSection();
|
||||
}
|
||||
|
||||
// Update materials
|
||||
const materials = JSON.parse(selectedOption.dataset.materials || '[]');
|
||||
if (materials.length > 0) {
|
||||
showMaterialsSection(materials);
|
||||
} else {
|
||||
hideMaterialsSection();
|
||||
}
|
||||
|
||||
// Update colors
|
||||
const colors = JSON.parse(selectedOption.dataset.colors || '[]');
|
||||
if (colors.length > 0) {
|
||||
showColorsSection(colors);
|
||||
} else {
|
||||
hideColorsSection();
|
||||
}
|
||||
|
||||
// Update additional options
|
||||
const options = JSON.parse(selectedOption.dataset.options || '[]');
|
||||
if (options.length > 0) {
|
||||
showAdditionalOptionsSection(options);
|
||||
} else {
|
||||
hideAdditionalOptionsSection();
|
||||
}
|
||||
}
|
||||
|
||||
// Show/hide dynamic sections
|
||||
function showMaterialsSection(materials) {
|
||||
const section = document.getElementById('materialsSection');
|
||||
const container = document.getElementById('materialsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
materials.forEach((material, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'radio-option';
|
||||
div.innerHTML = `
|
||||
<input type="radio" id="material_${index}" name="material" value="${material}" ${index === 0 ? 'checked' : ''}>
|
||||
<label for="material_${index}">${material}</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
section.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideMaterialsSection() {
|
||||
document.getElementById('materialsSection').classList.add('hidden');
|
||||
}
|
||||
|
||||
function showColorsSection(colors) {
|
||||
const section = document.getElementById('colorsSection');
|
||||
const container = document.getElementById('colorsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
colors.forEach((color, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'radio-option';
|
||||
div.innerHTML = `
|
||||
<input type="radio" id="color_${index}" name="color" value="${color}" ${index === 0 ? 'checked' : ''}>
|
||||
<label for="color_${index}">${color}</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
section.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideColorsSection() {
|
||||
document.getElementById('colorsSection').classList.add('hidden');
|
||||
}
|
||||
|
||||
function showAdditionalOptionsSection(options) {
|
||||
const section = document.getElementById('additionalOptionsSection');
|
||||
const container = document.getElementById('additionalOptionsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
options.forEach((option, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'radio-option';
|
||||
div.innerHTML = `
|
||||
<input type="radio" id="option_${index}" name="additionalOption" value="${option}" ${index === 0 ? 'checked' : ''}>
|
||||
<label for="option_${index}">${option}</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
section.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideAdditionalOptionsSection() {
|
||||
document.getElementById('additionalOptionsSection').classList.add('hidden');
|
||||
}
|
||||
|
||||
function showPrefixesSection(prefixes) {
|
||||
const section = document.getElementById('prefixesSection');
|
||||
const container = document.getElementById('prefixesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
// Add "None" option first
|
||||
const noneDiv = document.createElement('div');
|
||||
noneDiv.className = 'radio-option';
|
||||
noneDiv.innerHTML = `
|
||||
<input type="radio" id="prefix_none" name="codePrefix" value="" checked onchange="updateStandardizedCode()">
|
||||
<label for="prefix_none">None</label>
|
||||
`;
|
||||
container.appendChild(noneDiv);
|
||||
|
||||
prefixes.forEach((prefix, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'radio-option';
|
||||
div.innerHTML = `
|
||||
<input type="radio" id="prefix_${index}" name="codePrefix" value="${prefix.value}" onchange="updateStandardizedCode()" title="${prefix.description}">
|
||||
<label for="prefix_${index}">${prefix.label}</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
section.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hidePrefixesSection() {
|
||||
document.getElementById('prefixesSection').classList.add('hidden');
|
||||
}
|
||||
|
||||
function showSuffixesSection(suffixes) {
|
||||
const section = document.getElementById('suffixesSection');
|
||||
const container = document.getElementById('suffixesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
suffixes.forEach((suffix, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'radio-option';
|
||||
div.innerHTML = `
|
||||
<input type="checkbox" id="suffix_${index}" name="codeSuffix" value="${suffix.value}" onchange="updateStandardizedCode()" title="${suffix.description}">
|
||||
<label for="suffix_${index}">${suffix.label}</label>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
section.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideSuffixesSection() {
|
||||
document.getElementById('suffixesSection').classList.add('hidden');
|
||||
}
|
||||
|
||||
function updateStandardizedCode() {
|
||||
const baseCode = document.getElementById('baseCode').value.trim();
|
||||
|
||||
// Get selected prefix
|
||||
const prefixRadio = document.querySelector('input[name="codePrefix"]:checked');
|
||||
const prefix = prefixRadio ? prefixRadio.value : '';
|
||||
|
||||
// Get selected suffixes (checkboxes)
|
||||
const suffixCheckboxes = document.querySelectorAll('input[name="codeSuffix"]:checked');
|
||||
const suffixes = Array.from(suffixCheckboxes).map(cb => cb.value);
|
||||
|
||||
// Build the standardized code
|
||||
let standardizedCode = '';
|
||||
if (baseCode) {
|
||||
standardizedCode = prefix + baseCode;
|
||||
if (suffixes.length > 0) {
|
||||
standardizedCode += suffixes.join('');
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('standardCode').value = standardizedCode;
|
||||
}
|
||||
|
||||
function hideAllDynamicSections() {
|
||||
document.getElementById('prefixesSection').classList.add('hidden');
|
||||
document.getElementById('suffixesSection').classList.add('hidden');
|
||||
document.getElementById('materialsSection').classList.add('hidden');
|
||||
document.getElementById('colorsSection').classList.add('hidden');
|
||||
document.getElementById('additionalOptionsSection').classList.add('hidden');
|
||||
document.getElementById('additionalOptionsSection').classList.add('hidden');
|
||||
document.getElementById('productGroup').value = '';
|
||||
}
|
||||
|
||||
// Form submission
|
||||
document.getElementById('productForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
|
||||
// Get selected prefix
|
||||
const selectedPrefix = document.querySelector('input[name="codePrefix"]:checked');
|
||||
const codePrefix = selectedPrefix ? selectedPrefix.value : '';
|
||||
|
||||
// Get selected suffixes
|
||||
const selectedSuffixes = Array.from(document.querySelectorAll('input[name="codeSuffix"]:checked'))
|
||||
.map(cb => cb.value);
|
||||
|
||||
const productData = {
|
||||
location: formData.get('location'),
|
||||
description: formData.get('description'),
|
||||
status: formData.get('status'),
|
||||
baseCode: formData.get('baseCode'),
|
||||
standardCode: formData.get('standardCode'),
|
||||
codePrefix: codePrefix,
|
||||
codeSuffixes: selectedSuffixes,
|
||||
locationCodes: {},
|
||||
productType: formData.get('productType'),
|
||||
category: formData.get('productGroup'),
|
||||
material: formData.get('material'),
|
||||
color: formData.get('color'),
|
||||
additionalOption: formData.get('additionalOption')
|
||||
};
|
||||
|
||||
// Collect location codes for user's accessible locations
|
||||
userAccessibleLocations.forEach(locCode => {
|
||||
const code = formData.get(`code_${locCode}`);
|
||||
if (code) {
|
||||
productData.locationCodes[locCode] = code;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Product Data:', productData);
|
||||
|
||||
// Here you would send the data to your backend
|
||||
// For now, just show an alert
|
||||
alert('Product saved! (This is a demo - in production, this would save to the database)');
|
||||
});
|
||||
|
||||
// Utility functions
|
||||
function resetForm() {
|
||||
document.getElementById('productForm').reset();
|
||||
hideAllDynamicSections();
|
||||
}
|
||||
|
||||
function deleteProduct() {
|
||||
if (confirm('Are you sure you want to delete this product?')) {
|
||||
alert('Product deleted! (This is a demo)');
|
||||
resetForm();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', loadProductAttributes);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -269,6 +269,10 @@
|
||||
<input type="checkbox" id="perm_manage_users" value="manage_users">
|
||||
<label for="perm_manage_users">Manage Users</label>
|
||||
</div>
|
||||
<div class="location-item">
|
||||
<input type="checkbox" id="perm_manage_products" value="manage_products">
|
||||
<label for="perm_manage_products">Manage Products</label>
|
||||
</div>
|
||||
<div class="location-item">
|
||||
<input type="checkbox" id="perm_create_quotes" value="create_quotes">
|
||||
<label for="perm_create_quotes">Create Quotes</label>
|
||||
@@ -389,6 +393,9 @@
|
||||
if (document.getElementById('perm_manage_users').checked) {
|
||||
permissions.manage_users = true;
|
||||
}
|
||||
if (document.getElementById('perm_manage_products').checked) {
|
||||
permissions.manage_products = true;
|
||||
}
|
||||
if (document.getElementById('perm_create_quotes').checked) {
|
||||
permissions.create_quotes = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user