- Renamed aem-naming-tool-updated.html to index.html for easier hosting - Created OLD/ folder for all legacy/deprecated files (17 files moved) - Added OLD/ to .gitignore to exclude from future commits - Updated all documentation references to use index.html - Cleaned root directory to contain only active project files - Updated README with new project structure and deployment-ready setup - Preserved BISSELL_AEM_Folder_Master.xlsx as standard Excel filename Project now deployment-ready with clean structure: - index.html + bissell-product-data.json = complete web app - All legacy files archived in OLD/ (excluded from git) - Comprehensive README for users and administrators 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1716 lines
No EOL
83 KiB
HTML
1716 lines
No EOL
83 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>BISSELL AEM DAM Naming Tool</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap');
|
|
|
|
:root {
|
|
--primary-color: #2c3e50;
|
|
--primary-hover-color: #1a252f;
|
|
--primary-btn-color: #2c3e50;
|
|
--primary-btn-hover-color: #1a252f;
|
|
--bissell-red: #e53e3e;
|
|
--bissell-red-hover: #c53030;
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Montserrat', sans-serif;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
|
min-height: 100vh;
|
|
transition: background-color 0.3s ease, color 0.3s ease;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
color: white;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.header p {
|
|
font-size: 1.2rem;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.main-content {
|
|
background: white;
|
|
border-radius: 10px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.nav-tabs {
|
|
display: flex;
|
|
background: #f8f9fa;
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
|
|
.nav-tab {
|
|
flex: 1;
|
|
padding: 15px 20px;
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.nav-tab:hover {
|
|
background: #e9ecef;
|
|
}
|
|
|
|
.nav-tab.active {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
padding: 30px;
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
|
|
.card {
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin: 20px 0;
|
|
border-left: 4px solid var(--primary-color);
|
|
}
|
|
|
|
.btn {
|
|
background: var(--primary-btn-color);
|
|
color: white;
|
|
border: none;
|
|
padding: 12px 24px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
font-family: 'Montserrat', sans-serif;
|
|
font-weight: 500;
|
|
transition: background 0.3s;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: var(--primary-btn-hover-color);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #6c757d;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #545b62;
|
|
}
|
|
|
|
.btn-success {
|
|
background: var(--bissell-red);
|
|
}
|
|
|
|
.btn-success:hover {
|
|
background: var(--bissell-red-hover);
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 500;
|
|
color: #495057;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid #ced4da;
|
|
border-radius: 4px;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.form-control:focus {
|
|
outline: none;
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 0 0.2rem rgba(44,62,80,.25);
|
|
}
|
|
|
|
select.form-control {
|
|
height: 42px;
|
|
}
|
|
|
|
.form-group {
|
|
position: relative;
|
|
}
|
|
|
|
.enter-hint {
|
|
position: absolute;
|
|
right: 10px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: #999;
|
|
font-size: 0.8rem;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
background: white;
|
|
padding: 2px 5px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.form-control:focus + .enter-hint,
|
|
.form-control:hover + .enter-hint {
|
|
opacity: 1;
|
|
}
|
|
|
|
.result-box {
|
|
background: #d4edda;
|
|
border: 1px solid #c3e6cb;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.result-filename {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 1.1rem;
|
|
font-weight: bold;
|
|
color: #155724;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.error-box {
|
|
background: #f8d7da;
|
|
border: 1px solid #f5c6cb;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
margin: 20px 0;
|
|
color: #721c24;
|
|
}
|
|
|
|
.step {
|
|
display: none;
|
|
}
|
|
|
|
.step.active {
|
|
display: block;
|
|
}
|
|
|
|
.step-indicator {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-bottom: 30px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.step-circle {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: #dee2e6;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 5px;
|
|
font-weight: bold;
|
|
transition: all 0.3s;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.step-circle.active {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
}
|
|
|
|
.step-circle.completed {
|
|
background: #28a745;
|
|
color: white;
|
|
}
|
|
|
|
.navigation-buttons {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
.help-section {
|
|
background: #fff3cd;
|
|
border: 1px solid #ffeaa7;
|
|
border-radius: 5px;
|
|
padding: 15px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.code {
|
|
background: #f8f9fa;
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 3px;
|
|
padding: 2px 6px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.nav-tabs {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.navigation-buttons {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.step-indicator {
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>🎯 BISSELL AEM DAM Naming Tool 3.0</h1>
|
|
<p>Create perfect filenames using the new consolidated structure</p>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<div class="nav-tabs">
|
|
<button class="nav-tab active" onclick="showTab('naming-wizard')">📝 Name a File</button>
|
|
<button class="nav-tab" onclick="showTab('decoder')">🔍 Decode Filename</button>
|
|
<button class="nav-tab" onclick="showTab('help')">❓ Help & How-To</button>
|
|
</div>
|
|
|
|
<!-- Naming Wizard Tab -->
|
|
<div id="naming-wizard" class="tab-content active">
|
|
<h2>File Naming Wizard</h2>
|
|
<p>Follow the steps to generate your filename using the complete BISSELL product hierarchy.</p>
|
|
<p style="color: #666; font-size: 0.9rem;"><strong>💡 Tip:</strong> Press <kbd style="background: #f1f1f1; padding: 2px 6px; border-radius: 3px; font-size: 0.8rem;">Enter</kbd> after each selection to quickly move to the next step!</p>
|
|
|
|
|
|
<div class="step-indicator">
|
|
<div class="step-circle active" id="indicator-1">1</div>
|
|
<div class="step-circle" id="indicator-2">2</div>
|
|
<div class="step-circle" id="indicator-3">3</div>
|
|
<div class="step-circle" id="indicator-4">4</div>
|
|
<div class="step-circle" id="indicator-5">5</div>
|
|
<div class="step-circle" id="indicator-6">6</div>
|
|
<div class="step-circle" id="indicator-7">7</div>
|
|
<div class="step-circle" id="indicator-8">8</div>
|
|
</div>
|
|
|
|
<!-- Step 1: OMG Job Number -->
|
|
<div class="step active" id="step-1">
|
|
<div class="card">
|
|
<h3>Step 1: OMG Job Number</h3>
|
|
<p>Enter the OMG job number that will prefix your filename.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="omgJobNumber">OMG Job Number:</label>
|
|
<input type="text" class="form-control" id="omgJobNumber" placeholder="5791356">
|
|
<span class="enter-hint">Press Enter ↵</span>
|
|
<small>This number will prefix your final filename</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Product Category -->
|
|
<div class="step" id="step-2">
|
|
<div class="card">
|
|
<h3>Step 2: Product Category</h3>
|
|
<p>Select the main product category.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="productCategory">Product Category:</label>
|
|
<select class="form-control" id="productCategory"
|
|
onchange="updateProductTypes()">
|
|
<option value="">-- Select Product Category --</option>
|
|
</select>
|
|
<span class="enter-hint">Press Enter ↵</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Product Type -->
|
|
<div class="step" id="step-3">
|
|
<div class="card">
|
|
<h3>Step 3: Product Type</h3>
|
|
<p>Select the specific product type.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="productType">Product Type:</label>
|
|
<select class="form-control" id="productType"
|
|
onchange="updateGPDNumbers()">
|
|
<option value="">-- Select Product Type --</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 4: GPD Number -->
|
|
<div class="step" id="step-4">
|
|
<div class="card">
|
|
<h3>Step 4: GPD Number</h3>
|
|
<p>Select the GPD product number.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="gpdNumber">GPD Number:</label>
|
|
<select class="form-control" id="gpdNumber"
|
|
onchange="updateProductNames()">
|
|
<option value="">-- Select GPD Number --</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 5: Product Name -->
|
|
<div class="step" id="step-5">
|
|
<div class="card">
|
|
<h3>Step 5: Product Name</h3>
|
|
<p>Select the specific product name.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="productName">Product Name:</label>
|
|
<select class="form-control" id="productName"
|
|
onchange="updateAssetTypeA()">
|
|
<option value="">-- Select Product Name --</option>
|
|
</select>
|
|
<span class="enter-hint">Press Enter ↵</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 6: Asset Type A -->
|
|
<div class="step" id="step-6">
|
|
<div class="card">
|
|
<h3>Step 6: Asset Type A</h3>
|
|
<p>Select the primary asset type.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="assetTypeA">Asset Type A:</label>
|
|
<select class="form-control" id="assetTypeA"
|
|
onchange="updateAssetTypeB()">
|
|
<option value="">-- Select Asset Type A --</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 7: Asset Type B -->
|
|
<div class="step" id="step-7">
|
|
<div class="card">
|
|
<h3>Step 7: Asset Type B</h3>
|
|
<p>Select the specific asset type.</p>
|
|
|
|
<div class="form-group">
|
|
<label for="assetTypeB">Asset Type B:</label>
|
|
<select class="form-control" id="assetTypeB"
|
|
onchange="updateAssetTypeC()">
|
|
<option value="">-- Select Asset Type B --</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 8: Final Details -->
|
|
<div class="step" id="step-8">
|
|
<div class="card">
|
|
<h3>Step 8: Final Details</h3>
|
|
<p>Complete the remaining details for your asset.</p>
|
|
|
|
<div class="form-group" id="assetTypeCGroup" style="display: none;">
|
|
<label for="assetTypeC">Color Profile:</label>
|
|
<select class="form-control" id="assetTypeC">
|
|
<option value="">-- Select Color Profile --</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group" id="colorwayGroup" style="display: none;">
|
|
<label for="colorway">Colorway:</label>
|
|
<select class="form-control" id="colorway">
|
|
<option value="">-- Select Colorway --</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="customDescriptor">Custom Descriptor (Optional):</label>
|
|
<input type="text" class="form-control" id="customDescriptor"
|
|
placeholder="e.g., 250x250banner1, 300x300, EcomAsset1">
|
|
<small>Custom text descriptor (no underscores allowed). Will be moved to front of DAM filename.</small>
|
|
</div>
|
|
|
|
|
|
<button class="btn btn-success" onclick="generateFilename()">🎯 Generate Filename</button>
|
|
|
|
<div id="result" style="display: none;">
|
|
<h4>Your Generated Filename:</h4>
|
|
<div class="result-box">
|
|
<div class="result-filename" id="generatedFilename"></div>
|
|
</div>
|
|
|
|
<h4>📁 AEM DAM Folder Path:</h4>
|
|
<div class="result-box">
|
|
<div class="result-filename" id="generatedPath" style="font-size: 0.9rem; color: #666;"></div>
|
|
</div>
|
|
|
|
<h4>📄 AEM DAM File Name:</h4>
|
|
<div class="result-box">
|
|
<div class="result-filename" id="damFilename" style="font-size: 1rem; color: #155724;"></div>
|
|
</div>
|
|
|
|
<h4>📋 Metadata (Removed from DAM filename but in DAM metadata):</h4>
|
|
<div class="result-box" style="background: #fff3cd; border-color: #ffeaa7;">
|
|
<div id="metadataInfo" style="font-size: 0.9rem; color: #856404;"></div>
|
|
</div>
|
|
|
|
<div style="margin-top: 15px;">
|
|
<button class="btn btn-secondary" onclick="copyToClipboard()">📋 Copy Original Filename</button>
|
|
<button class="btn btn-secondary" onclick="copyDAMFilenameToClipboard()" style="margin-left: 10px;">📋 Copy DAM Filename</button>
|
|
<button class="btn btn-secondary" onclick="copyPathToClipboard()" style="margin-left: 10px;">📁 Copy Path</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="navigation-buttons">
|
|
<button class="btn btn-secondary" id="prevBtn" onclick="previousStep()" style="display: none;">← Previous</button>
|
|
<button class="btn" id="nextBtn" onclick="nextStep()">Next →</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Decoder Tab -->
|
|
<div id="decoder" class="tab-content">
|
|
<h2>🔍 Filename Decoder</h2>
|
|
<p>Paste an existing filename to decode its components and see the folder structure.</p>
|
|
|
|
<div class="card">
|
|
<h3>Enter Filename to Decode</h3>
|
|
<div class="form-group">
|
|
<label for="filenameInput">Filename (with or without extension):</label>
|
|
<input type="text" class="form-control" id="filenameInput"
|
|
placeholder="5791356_dry_canister_p2829_pet-hair-eraser_imagery_hero_rgb_mambo-red.tif"
|
|
oninput="decodeFilename()">
|
|
</div>
|
|
|
|
<div id="decodedResult" style="margin-top: 20px;">
|
|
<!-- Results will appear here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Help Tab -->
|
|
<div id="help" class="tab-content">
|
|
<h2>Help & How-To Guide</h2>
|
|
|
|
<div class="help-section">
|
|
<h4>🎯 New Consolidated Structure</h4>
|
|
<p>This tool now uses the complete BISSELL product hierarchy from the Excel structure:</p>
|
|
<ol>
|
|
<li><strong>OMG Job Number:</strong> Prefixes production filename, moved to metadata for DAM</li>
|
|
<li><strong>Product Category:</strong> wet, dry, consumables, sanitaire, rug-doctor</li>
|
|
<li><strong>Product Type:</strong> canister, upright, stick, robot, etc.</li>
|
|
<li><strong>GPD Number:</strong> P#### product codes</li>
|
|
<li><strong>Product Name:</strong> crosswave, pet-hair-eraser, cleanview, etc.</li>
|
|
<li><strong>Asset Type A:</strong> digital, imagery, logo, parts</li>
|
|
<li><strong>Asset Type B:</strong> hero, lifestyle, banner, a-plus</li>
|
|
<li><strong>Color Profile & Colorway:</strong> rgb/cmyk + colorway (when applicable)</li>
|
|
<li><strong>Custom Descriptor:</strong> Optional free text (e.g., 250x250banner1, 300x300)</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>📝 Filename Formats</h4>
|
|
<p><strong>Production Format:</strong> <span class="code">[OMG-JOB-NUMBER]_[product-hierarchy]_[colorway]_[customDescriptor].ext</span></p>
|
|
<p><strong>DAM Format:</strong> <span class="code">[customDescriptor]_[gpd-number]_[product-name]_[asset-types]_[colorway]_[omg-job-number].ext</span></p>
|
|
|
|
<h5>Examples:</h5>
|
|
<p><strong>Production:</strong> <span class="code">12345678_dry_robot_p2990_crosswave-robot_imagery_hero_NCW_250x250banner1.ext</span></p>
|
|
<p><strong>DAM Renamed:</strong> <span class="code">250x250banner1_p2990_crosswave-robot_imagery_hero.ext</span></p>
|
|
|
|
<p><strong>Key Changes for DAM:</strong></p>
|
|
<ul>
|
|
<li>Custom descriptor moves to front</li>
|
|
<li>Job number, "dry", "robot" removed from filename</li>
|
|
<li>"NCW" colorway removed from filename</li>
|
|
<li>Removed data preserved in metadata</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>🏷️ Custom Descriptor Field</h4>
|
|
<p><strong>Purpose:</strong> Add custom text like "250x250banner1", "300x300", "EcomAsset1"</p>
|
|
<p><strong>Rules:</strong> No underscores allowed - use other characters only</p>
|
|
<p><strong>Behavior:</strong></p>
|
|
<ul>
|
|
<li>Appears at end of production filename</li>
|
|
<li>Moves to front of DAM filename</li>
|
|
<li>Visible in both versions (not removed)</li>
|
|
</ul>
|
|
<p><strong>Example:</strong> "250x250banner1" helps identify banner size/variant</p>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>🎨 Colorways & Folder Structure</h4>
|
|
<p><strong>Always Required:</strong> Must select a colorway for every asset</p>
|
|
<p><strong>Available Options:</strong> Product-specific colorways or "NCW" (No Colorway)</p>
|
|
<p><strong>Folder Behavior:</strong></p>
|
|
<ul>
|
|
<li>Real colorways create folders: <span class="code">/imagery/hero/rgb/mambo-red/</span></li>
|
|
<li>NCW does not create folder: <span class="code">/imagery/hero/rgb/</span></li>
|
|
</ul>
|
|
<p><strong>Filename Behavior:</strong></p>
|
|
<ul>
|
|
<li>Production filename always includes colorway</li>
|
|
<li>DAM filename excludes "NCW" but includes real colorways</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>🔧 Consumables Workflow</h4>
|
|
<p><strong>Consumables Only:</strong> When working with consumables, you have "Generic" options available.</p>
|
|
<p><strong>GPD Numbers:</strong> For consumables, select "Generic / No Specific GPD" when working with broad asset categories.</p>
|
|
<p><strong>Product Names:</strong> For consumables, select "Generic / No Specific Product" or choose from all available products.</p>
|
|
<p><strong>Other Categories:</strong> All other product categories (dry, wet, sanitaire, etc.) follow strict hierarchy matching.</p>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>🔄 Cascading Selections</h4>
|
|
<p>Each level filters the options for the next level based on the Excel structure relationships. This ensures all combinations are valid and follow the established hierarchy.</p>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>⌨️ Keyboard Navigation</h4>
|
|
<p><strong>Press Enter:</strong> After making any selection or entering text, press Enter to automatically move to the next step.</p>
|
|
<p><strong>Faster Workflow:</strong> No need to click "Next" - just type or select and press Enter!</p>
|
|
<p><strong>Smart Validation:</strong> Only advances if the current field has a valid value.</p>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>🔧 JSON Data Management</h4>
|
|
<p><strong>Dynamic Loading:</strong> This tool loads product data from <span class="code">bissell-product-data.json</span></p>
|
|
<p><strong>Easy Updates:</strong> To add new products, update the Excel file and run the conversion script:</p>
|
|
<ol>
|
|
<li>Replace <span class="code">BISSELL_AEM Folder_Hierarchy6-30.xlsx</span> with new version</li>
|
|
<li>Run: <span class="code">python excel-to-json-converter.py</span></li>
|
|
<li>Refresh this tool to see new products</li>
|
|
</ol>
|
|
<p><strong>No Code Changes:</strong> Product updates require no HTML modifications - just update the JSON file.</p>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>🔍 Using the Decoder</h4>
|
|
<p><strong>Purpose:</strong> Analyze existing filenames to understand their structure and folder paths.</p>
|
|
<p><strong>How to Use:</strong></p>
|
|
<ol>
|
|
<li>Click the "🔍 Decode Filename" tab</li>
|
|
<li>Paste any filename (with or without extension)</li>
|
|
<li>See the breakdown of components and folder structure</li>
|
|
</ol>
|
|
<p><strong>Supports:</strong></p>
|
|
<ul>
|
|
<li>Legacy filenames without custom descriptors</li>
|
|
<li>New filenames with custom descriptors at the end</li>
|
|
<li>NCW colorway handling (no folder created)</li>
|
|
<li>Automatic detection of custom descriptors vs colorways</li>
|
|
</ul>
|
|
<p><strong>Example Inputs:</strong></p>
|
|
<p><span class="code">12345678_dry_robot_p2990_crosswave-robot_imagery_hero_NCW_250x250banner1.ext</span></p>
|
|
<p><span class="code">5791356_wet_wash_p3084_crosswave_imagery_hero_rgb_mambo-red.tif</span></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Product hierarchy data - loaded from external JSON
|
|
let structureData = null;
|
|
|
|
// Load data from external JSON file
|
|
async function loadProductData() {
|
|
try {
|
|
const response = await fetch('bissell-product-data.json');
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
structureData = await response.json();
|
|
console.log('Product data loaded successfully:', structureData);
|
|
return structureData;
|
|
} catch (error) {
|
|
console.error('Error loading product data:', error);
|
|
// Fallback to hardcoded data if JSON fails to load
|
|
structureData = {
|
|
productCategories: ["consumables", "dry", "rug-doctor", "sanitaire", "wet"],
|
|
|
|
dropdownRelationships: {
|
|
"0": {
|
|
"1-product-assets": ["consumables", "dry", "rug-doctor", "sanitaire", "wet"]
|
|
},
|
|
"1": {
|
|
"consumables": ["boost", "common", "great-value", "manual", "obsolete", "pdc", "rental", "steam", "super-concentrate", "udc", "wash"],
|
|
"dry": ["air", "canister", "common", "hand-vac", "robot", "stick", "sweeper", "uvac"],
|
|
"sanitaire": ["air-mover", "backpack", "cordless", "professional", "steam", "sweeper", "upright", "wash"],
|
|
"wet": ["common", "pdc", "robot", "steam", "udc", "wash"]
|
|
},
|
|
"2": {
|
|
"common": ["accessories", "digital", "illustrations", "imagery", "parts", "print", "research"],
|
|
"air": ["p2963", "p3004", "p3019", "p3029", "p3083"],
|
|
"canister": ["p263", "p2829", "p2831", "p2850", "p2873", "p2886", "p2887", "p3189", "p461", "p475"],
|
|
"hand-vac": ["p2862", "p2923", "p3008", "p481", "p483"],
|
|
"robot": ["p2848", "p2949", "p2950", "p2990", "p3001", "p3011", "p3049", "p3097", "p413", "p472", "p488"],
|
|
"stick": ["p2822", "p2862", "p2863", "p2878", "p2900", "p2902", "p2922", "p2960", "p2961", "p2969-p3051", "p2983", "p3025", "p3043", "p3114", "p3152", "p3180", "p3192", "p3202", "p429"],
|
|
"sweeper": ["p179", "p280", "p2929", "p806", "p826"],
|
|
"uvac": ["p2861-p3010", "p2881", "p2882", "p2884", "p2955", "p2959-p3044", "p3005", "p3024", "p3027", "p3062", "p3093", "p3116", "p3145", "p3161", "p3178", "p460", "p482", "p486"],
|
|
"pdc": ["p250-p3059", "p291", "p2931-p410", "p2932", "p3000", "p3071", "p3090", "p3109", "p3110", "p3118", "p3126", "p3127-p3205", "p3146", "p3153", "p3157", "p3167", "p321-p459"],
|
|
"steam": ["p259", "p2818", "p3128", "p411", "p412", "p421", "p466", "p487"],
|
|
"udc": ["p266-p372-p386", "p2933", "p2965", "p2973", "p2989", "p3061", "p3106", "p3143", "p3159", "p3173", "p3213", "p451"],
|
|
"wash": ["p2817", "p2883", "p2915", "p2985", "p3016", "p3047", "p3084", "p3091", "p3094", "p3113", "p3121", "p3123", "p3134", "p3150", "p3154", "p3174", "p3199", "p432"]
|
|
},
|
|
"3": {
|
|
"p2963": ["air220-air320-replacement-carbon-filter", "air320-replacement-high-efficiency-filter", "myair-replacement-filter"],
|
|
"p3004": ["myair-mini", "myair-pro", "small-air-purifier-hepa-and-carbon-filter"],
|
|
"p3019": ["air280", "air280-max"],
|
|
"p3029": ["air320-filter"],
|
|
"p3083": ["air180"],
|
|
"p263": ["garage-pro"],
|
|
"p2829": ["pet-hair-eraser", "smartclean", "smartclean-pet"],
|
|
"p2831": ["cleanview"],
|
|
"p2850": ["multiclean-wet-and-dry"],
|
|
"p2873": ["premium-compact", "smartclean"],
|
|
"p2886": ["easy-vac-bagless", "zing-bagless"],
|
|
"p2887": ["aeroswift-bagged-canister", "zing-bagged-cylinder-vacuum", "zing-canister-vacuum"],
|
|
"p3189": ["multiclean-turbo"],
|
|
"p461": ["hard-floor-expert-multicyclonic"],
|
|
"p475": ["bagless-filter-replacement-kit"],
|
|
"p2862": ["airram", "multi-hand-vacuum"],
|
|
"p2923": ["pet-hair-eraser-lithium-ion"],
|
|
"p3008": ["aeroslim"],
|
|
"p481": ["bolt-2-0-18v", "bolt-lithium-max-professional", "bolt-lithium-pet"],
|
|
"p483": ["automate", "pet-hair-eraser", "pet-hair-eraser-plus"],
|
|
"p2848": ["smartclean-plus"],
|
|
"p2949": ["ev675"],
|
|
"p2950": ["smartclean-connected"],
|
|
"p2990": ["crosswave-robot"],
|
|
"p3001": ["cleanview-connect"],
|
|
"p3011": ["spinwave-robot"],
|
|
"p3049": ["spinwave-r5"],
|
|
"p3097": ["readyclean-a3"],
|
|
"p413": ["replacement-vacuum-filter"],
|
|
"p472": ["smartclean"],
|
|
"p488": ["iconpet"],
|
|
"p2822": ["iconpet"],
|
|
"p2863": ["3-in-1", "featherweight", "featherweight-btc"],
|
|
"p2878": ["powerclean-corded"],
|
|
"p2900": ["multireach-cordless"],
|
|
"p2902": ["multireach-ion-xl-36v"],
|
|
"p2960": ["3-in-1-turbo"],
|
|
"p2961": ["icon-edge-professional", "iconpet-edge"],
|
|
"p2969-p3051": ["cleanview-pet-slim-corded", "pet-hair-eraser-slim-corded", "powerglide-pet-slim-corded"],
|
|
"p2983": ["cleanview-pet-slim-cordless", "multireach-active-pet-cordless", "powerglide-pet-slim-cordless", "slim-cordless-stick-21v"],
|
|
"p3025": ["icon-turbo-essential-25v", "iconpet-turbo-25v-cordless", "iconpet-turbo-edge", "iconpet-turbo-edge-252v", "iconpetturbo-252v", "omnipet-turbo-25v-cordless"],
|
|
"p3043": ["featherweight-powerbrush"],
|
|
"p3114": ["cleanview-xr", "cleanview-xr-pet"],
|
|
"p3152": ["powerclean-furfinder", "powerclean-furguard"],
|
|
"p3180": ["powerclean-dualbrush"],
|
|
"p3192": ["powerclean"],
|
|
"p3202": ["powerclean-corded"],
|
|
"p429": ["airram"],
|
|
"p179": ["refresh-manual-sweeper"],
|
|
"p280": ["natural-sweep"],
|
|
"p2929": ["SW200", "natural-sweep", "refresh-manual-sweeper"],
|
|
"p806": ["perfect-sweep-turbo"],
|
|
"p826": ["SW100", "sturdy-sweep"],
|
|
"p2861-p3010": ["cleanview-swivel", "cleanview-swivel-pet-plus", "cleanview-swivel-pet-rewind", "cleanview-swivel-pet-rewind-deluxe", "cleanview-swivel-rewind-pet", "powerclean-swivel-rewind-pet", "powerlifter-swivel-pet", "powerlifter-swivel-pet-rewind"],
|
|
"p2881": ["pet-hair-eraser-turbo", "pet-hair-eraser-turbo-plus", "pet-hair-eraser-turbo-pro"],
|
|
"p2882": ["cleanview-rewind-pet-deluxe", "powerclean-rewind"],
|
|
"p2884": ["cleanview-compact", "powerforce-compact", "powerforce-compact-turbo"],
|
|
"p2955": ["replacement-vacuum-filter"],
|
|
"p2959-p3044": ["cleanview-allergen", "multiclean-allergen", "pet-hair-eraser-allergen", "powerclean-allergen", "powerlifter-swivel"],
|
|
"p3005": ["cleanview", "cleanview-rewind", "cleanview-rewind-pet", "powerforce-helix", "powerforce-pet-deluxe", "powerforce-rewind-pet", "powerforce-rewind-pet-deluxe", "powerforce-turbo-pet"],
|
|
"p3024": ["cleanview-poweredge-rewind-pet", "cleanview-swivel-pet-reach", "cleanview-swivel-rewind-pet-reach", "common", "powerlifter-swivel-pet-reach", "powerlifter-swivel-rewind-pet-reach"],
|
|
"p3027": ["multiclean-allergen-pet-rewind", "powerlifter-allergen-pet-rewind", "powerlifter-surfacesense-lift-off", "surfacesense-lift-off", "surfacesense-lift-off-pet"],
|
|
"p3062": ["replacement-vacuum-filter"],
|
|
"p3093": ["replacement-vacuum-filter"],
|
|
"p3116": ["cleanview-swivel-pet", "cleanview-swivel-rewind-pet", "powerlifter-swivel-pet", "powerlifter-swivel-rewind-pet"],
|
|
"p3145": ["cleanview-max-furguard", "cleanview-max-lift-off", "pet-hair-eraser-allergen-lift-off", "powerlifter-allergen-lift-off"],
|
|
"p3161": ["cleanview-max-tangle-free", "cleanview-max-tangle-free-rewind"],
|
|
"p3178": ["replacement-vacuum-filter"],
|
|
"p460": ["powerglide-cordless"],
|
|
"p482": ["replacement-vacuum-filter"],
|
|
"p486": ["replacement-vacuum-filter"],
|
|
"p250-p3059": ["little-green", "little-green-autocare", "little-green-pet", "little-green-pet-deluxe", "little-green-select"],
|
|
"p291": ["little-green", "spotclean-c2", "spotclean-c3", "spotclean-c3-essential", "spotclean-essential", "spotclean-stainlift"],
|
|
"p2931-p410": ["replacement-vacuum-filter"],
|
|
"p2932": ["replacement-vacuum-filter"],
|
|
"p3000": ["replacement-vacuum-filter"],
|
|
"p3071": ["replacement-vacuum-filter"],
|
|
"p3090": ["replacement-vacuum-filter"],
|
|
"p3109": ["little-green-proheat", "little-green-proheat-pet", "little-green-proheat-smartmix", "spotclean-proheat", "spotclean-proheat-advanced", "spotclean-truheat"],
|
|
"p3110": ["little-green-deluxe-pet", "little-green-max-pet", "little-green-max-pet-smartmix", "little-green-multiclean", "spotclean", "spotclean-c5", "spotclean-c5-pet", "spotclean-professional", "spotclean-select"],
|
|
"p3118": ["spotclean-hydrosteam"],
|
|
"p3126": ["little-green-auto-pro", "little-green-pet-pro", "spotclean-c9", "spotclean-pro", "spotclean-turbo"],
|
|
"p3127-p3205": ["little-green-hydrosteam", "little-green-hydrosteam-pet", "spotclean-hydrosteam", "spotclean-hydrosteam-auto", "spotclean-hydrosteam-pro", "spotclean-hydrosteam-select"],
|
|
"p3146": ["little-green-mini", "spotclean-mini"],
|
|
"p3153": ["little-green-mini-cordless", "spotclean-mini-cordless"],
|
|
"p3157": ["replacement-vacuum-filter"],
|
|
"p3167": ["replacement-vacuum-filter"],
|
|
"p321-p459": ["replacement-vacuum-filter"],
|
|
"p259": ["steam-shot", "steam-shot-deluxe", "steam-shot-extended-reach", "steam-shot-omni", "steam-shot-omnireach"],
|
|
"p2818": ["replacement-vacuum-filter"],
|
|
"p3128": ["spinwave-smartsteam"],
|
|
"p411": ["replacement-vacuum-filter"],
|
|
"p412": ["replacement-vacuum-filter"],
|
|
"p421": ["replacement-vacuum-filter"],
|
|
"p466": ["replacement-vacuum-filter"],
|
|
"p487": ["powerfresh", "powerfresh-deluxe", "powerfresh-deluxe-pet", "powerfresh-sanitize-professional", "powerfresh-v"],
|
|
"p266-p372-p386": ["replacement-vacuum-filter"],
|
|
"p2933": ["replacement-vacuum-filter"],
|
|
"p2965": ["replacement-vacuum-filter"],
|
|
"p2973": ["replacement-vacuum-filter"],
|
|
"p2989": ["replacement-vacuum-filter"],
|
|
"p3061": ["replacement-vacuum-filter"],
|
|
"p3106": ["powerclean-2x", "powerclean-max", "powerforce-pet-cl", "powerwash-pet", "quickwash-powerbrush", "turboclean-pet-xl"],
|
|
"p3143": ["replacement-vacuum-filter"],
|
|
"p3159": ["cleanview-hydrosteam", "powerwash-hydrosteam", "revolution-hydrosteam"],
|
|
"p3173": ["proheat-2x-revolution-pet-pro", "proheat-2x-revolution-pet-pro-advanced", "proheat-2x-revolution-pet-pro-deluxe", "proheat-2x-revolution-pet-pro-plus"],
|
|
"p3213": ["proheat-2x-revolution-freshstart"],
|
|
"p451": ["replacement-vacuum-filter"],
|
|
"p2817": ["spinwave", "spinwave-hard-floor-expert", "spinwave-pet", "spinwave-plus"],
|
|
"p2883": ["replacement-vacuum-filter"],
|
|
"p2915": ["spinwave-cordless", "spinwave-cordless-hard-floor-expert"],
|
|
"p2985": ["BGFW13", "crosswave-commercial"],
|
|
"p3016": ["crosswave-hydrosteam", "crosswave-hydrosteam-deluxe", "crosswave-hydrosteam-pet", "crosswave-hydrosteam-pet-pro"],
|
|
"p3047": ["spinwave-plus-vac", "spinwave-vac-pet", "spinwave-vac-pet-pro"],
|
|
"p3084": ["crosswave", "crosswave-c3-pro", "crosswave-c3-select", "crosswave-pet", "crosswave-pet-pro"],
|
|
"p3091": ["crosswave-hf3-cordless", "crosswave-hf3-cordless-plus", "crosswave-hf3-cordless-pro", "crosswave-hf3-cordless-professional", "crosswave-hf3-cordless-select"],
|
|
"p3094": ["turboclean-hard-floors"],
|
|
"p3113": ["crosswave-hf5"],
|
|
"p3121": ["crosswave-omniforce"],
|
|
"p3123": ["crosswave-hf2", "crosswave-opp-corded", "hf2-corded"],
|
|
"p3134": ["crosswave-omniforce-edge", "crosswave-omniforce-edge-pro"],
|
|
"p3150": ["crosswave-omniedge", "crosswave-omnifind", "crosswave-omnifind-pro"],
|
|
"p3154": ["crosswave-edge"],
|
|
"p3174": ["crosswave-edgefind-pro"],
|
|
"p3199": ["crosswave-edgefind", "crosswave-hydroscrub", "crosswave-omniforce-edgefind-pro", "crosswave-omniforce-edgefind-select"],
|
|
"p432": ["crosswave"]
|
|
},
|
|
"4": {
|
|
"multi-hand-vacuum": ["mambo-red"],
|
|
"aeroslim": ["mambo-red", "titanium"],
|
|
"iconpet": ["copper-harbor", "electric-blue"],
|
|
"airram": ["cha-cha-lime", "electric-blue", "gun-metal-gray", "mambo-red", "pacific-purple"],
|
|
"3-in-1": ["baha-blue"],
|
|
"featherweight": ["black", "bossanova-blue", "brite-white", "disco-teal", "euro-red"],
|
|
"featherweight-btc": ["cha-cha-lime", "grapevine-purple", "la-bomba-pink", "samba-orange"],
|
|
"multireach-ion-xl-36v": ["sparkle-silver"],
|
|
"cleanview-xr": ["black"],
|
|
"cleanview-xr-pet": ["grapevine-purple"],
|
|
"powerclean-furguard": ["cobalt", "midnight", "silver", "titanium"],
|
|
"powerclean-furfinder": ["cobalt", "grapevine", "lake", "mambo-red", "midnight", "silver"],
|
|
"powerclean-dualbrush": ["garnet-titanium", "vineyard-gold"],
|
|
"powerclean": ["dusk", "mambo-red", "sparkling-silver", "warm-white-lake-blue", "white-lake-blue"],
|
|
"refresh-manual-sweeper": ["light-blue"],
|
|
"perfect-sweep-turbo": ["innovate-orange"],
|
|
"pet-hair-eraser": ["electric-blue", "mambo-red", "cha-cha-lime"],
|
|
"crosswave-edge": ["dove-dusk", "frost-blue", "frost-green", "peri-blue", "warm-white-lake-blue"],
|
|
"crosswave": ["bossanova-blue", "cha-cha-lime", "cobalt-blue", "disco-teal", "electric-blue", "grapevine-purple", "mambo-red", "samba-orange"],
|
|
"spinwave": ["bossanova-blue", "cha-cha-lime", "disco-teal"],
|
|
"cleanview": ["cobalt-blue", "euro-red"],
|
|
"little-green": ["cha-cha-lime", "emerald-green", "md-green"],
|
|
"steam-shot": ["bossanova-blue", "electric-blue"],
|
|
"powerfresh": ["bossanova-blue"]
|
|
},
|
|
"5": {
|
|
"light-blue": ["imagery"],
|
|
"electric-blue": ["digital", "imagery"],
|
|
"mambo-red": ["digital", "imagery"],
|
|
"cha-cha-lime": ["digital", "imagery"],
|
|
"gun-metal-gray": ["imagery"],
|
|
"pacific-purple": ["imagery"],
|
|
"dove-dusk": ["imagery"],
|
|
"frost-blue": ["imagery"],
|
|
"cobalt": ["imagery"],
|
|
"grapevine": ["imagery"],
|
|
"lake": ["imagery"],
|
|
"midnight": ["imagery"],
|
|
"silver": ["imagery"],
|
|
"black": ["imagery"],
|
|
"bossanova-blue": ["imagery"],
|
|
"brite-white": ["imagery"],
|
|
"disco-teal": ["imagery"],
|
|
"euro-red": ["imagery"],
|
|
"titanium": ["imagery"],
|
|
"copper-harbor": ["imagery"],
|
|
"grapevine-purple": ["imagery"],
|
|
"la-bomba-pink": ["imagery"],
|
|
"samba-orange": ["imagery"],
|
|
"sparkle-silver": ["imagery"],
|
|
"garnet-titanium": ["imagery"],
|
|
"vineyard-gold": ["imagery"],
|
|
"dusk": ["imagery"],
|
|
"sparkling-silver": ["imagery"],
|
|
"warm-white-lake-blue": ["imagery"],
|
|
"white-lake-blue": ["imagery"],
|
|
"innovate-orange": ["imagery"],
|
|
"peri-blue": ["imagery"],
|
|
"frost-green": ["imagery"],
|
|
"cobalt-blue": ["imagery"],
|
|
"emerald-green": ["imagery"],
|
|
"md-green": ["imagery"],
|
|
"baha-blue": ["imagery"]
|
|
},
|
|
"6": {
|
|
"imagery": ["hero", "ls"],
|
|
"digital": ["a-plus", "banner"]
|
|
},
|
|
"7": {
|
|
"hero": ["cmyk", "rgb"],
|
|
"ls": ["cmyk", "rgb"],
|
|
"a-plus": ["cmyk", "rgb"],
|
|
"banner": ["cmyk", "rgb"],
|
|
"standard": ["cmyk", "rgb"],
|
|
"diagram": ["cmyk", "rgb"]
|
|
}
|
|
}
|
|
};
|
|
return structureData;
|
|
}
|
|
}
|
|
|
|
let currentStep = 1;
|
|
const totalSteps = 8;
|
|
|
|
// Initialize the app
|
|
async function init() {
|
|
await loadProductData();
|
|
populateProductCategories();
|
|
updateStepDisplay();
|
|
}
|
|
|
|
// Tab management
|
|
function showTab(tabName) {
|
|
document.querySelectorAll('.tab-content').forEach(tab => {
|
|
tab.classList.remove('active');
|
|
});
|
|
|
|
document.querySelectorAll('.nav-tab').forEach(tab => {
|
|
tab.classList.remove('active');
|
|
});
|
|
|
|
document.getElementById(tabName).classList.add('active');
|
|
event.target.classList.add('active');
|
|
}
|
|
|
|
// Step management
|
|
function nextStep() {
|
|
if (validateCurrentStep()) {
|
|
if (currentStep < totalSteps) {
|
|
currentStep++;
|
|
updateStepDisplay();
|
|
}
|
|
}
|
|
}
|
|
|
|
function previousStep() {
|
|
if (currentStep > 1) {
|
|
currentStep--;
|
|
updateStepDisplay();
|
|
}
|
|
}
|
|
|
|
function updateStepDisplay() {
|
|
document.querySelectorAll('.step').forEach(step => {
|
|
step.classList.remove('active');
|
|
});
|
|
|
|
document.getElementById(`step-${currentStep}`).classList.add('active');
|
|
|
|
for (let i = 1; i <= totalSteps; i++) {
|
|
const indicator = document.getElementById(`indicator-${i}`);
|
|
indicator.classList.remove('active', 'completed');
|
|
|
|
if (i < currentStep) {
|
|
indicator.classList.add('completed');
|
|
} else if (i === currentStep) {
|
|
indicator.classList.add('active');
|
|
}
|
|
}
|
|
|
|
const prevBtn = document.getElementById('prevBtn');
|
|
const nextBtn = document.getElementById('nextBtn');
|
|
|
|
prevBtn.style.display = currentStep > 1 ? 'inline-block' : 'none';
|
|
|
|
if (currentStep === totalSteps) {
|
|
nextBtn.style.display = 'none';
|
|
} else {
|
|
nextBtn.style.display = 'inline-block';
|
|
nextBtn.textContent = 'Next →';
|
|
}
|
|
|
|
// Auto-focus on the current step's input/select field for better keyboard navigation
|
|
setTimeout(() => {
|
|
const currentStepElement = document.getElementById(`step-${currentStep}`);
|
|
if (currentStepElement) {
|
|
const input = currentStepElement.querySelector('input, select');
|
|
if (input) {
|
|
input.focus();
|
|
}
|
|
}
|
|
}, 100); // Small delay to ensure step transition is complete
|
|
|
|
}
|
|
|
|
function validateCurrentStep() {
|
|
switch (currentStep) {
|
|
case 1:
|
|
const omgJobNumber = document.getElementById('omgJobNumber').value;
|
|
if (!omgJobNumber.trim()) {
|
|
alert('Please enter an OMG job number.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!document.getElementById('productCategory').value) {
|
|
alert('Please select a product category.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!document.getElementById('productType').value) {
|
|
alert('Please select a product type.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (!document.getElementById('gpdNumber').value) {
|
|
alert('Please select a GPD number.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (!document.getElementById('productName').value) {
|
|
alert('Please select a product name.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (!document.getElementById('assetTypeA').value) {
|
|
alert('Please select an asset type A.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (!document.getElementById('assetTypeB').value) {
|
|
alert('Please select an asset type B.');
|
|
return false;
|
|
}
|
|
break;
|
|
case 8:
|
|
// Final step - validation happens in generateFilename
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Populate dropdowns
|
|
function populateProductCategories() {
|
|
if (!structureData) {
|
|
console.error('Structure data not loaded yet');
|
|
return;
|
|
}
|
|
|
|
const select = document.getElementById('productCategory');
|
|
structureData.productCategories.forEach(category => {
|
|
const option = document.createElement('option');
|
|
option.value = category;
|
|
option.textContent = category.charAt(0).toUpperCase() + category.slice(1);
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function updateProductTypes() {
|
|
if (!structureData) return;
|
|
|
|
const category = document.getElementById('productCategory').value;
|
|
const select = document.getElementById('productType');
|
|
select.innerHTML = '<option value="">-- Select Product Type --</option>';
|
|
|
|
if (category && structureData.dropdownRelationships["1"][category]) {
|
|
structureData.dropdownRelationships["1"][category].forEach(type => {
|
|
const option = document.createElement('option');
|
|
option.value = type;
|
|
option.textContent = type.charAt(0).toUpperCase() + type.slice(1);
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
// Clear subsequent dropdowns
|
|
clearDropdowns(['gpdNumber', 'productName', 'assetTypeA', 'assetTypeB', 'assetTypeC']);
|
|
}
|
|
|
|
function updateGPDNumbers() {
|
|
if (!structureData) return;
|
|
|
|
const productType = document.getElementById('productType').value;
|
|
const productCategory = document.getElementById('productCategory').value;
|
|
const select = document.getElementById('gpdNumber');
|
|
select.innerHTML = '<option value="">-- Select GPD Number --</option>';
|
|
|
|
// Only add "Generic" option for consumables
|
|
if (productCategory === 'consumables') {
|
|
const genericOption = document.createElement('option');
|
|
genericOption.value = 'generic';
|
|
genericOption.textContent = 'Generic / No Specific GPD';
|
|
select.appendChild(genericOption);
|
|
}
|
|
|
|
if (productType && structureData.dropdownRelationships["2"][productType]) {
|
|
const items = structureData.dropdownRelationships["2"][productType];
|
|
|
|
// Check if this leads to asset types (like "common" consumables) or actual GPD numbers
|
|
const hasGPDNumbers = items.some(item => item.startsWith('p') && item.match(/p\d/));
|
|
|
|
items.forEach(item => {
|
|
const option = document.createElement('option');
|
|
option.value = item;
|
|
|
|
// Format display based on whether it's a GPD number or asset type
|
|
if (hasGPDNumbers || item.startsWith('p')) {
|
|
option.textContent = item.toUpperCase();
|
|
} else {
|
|
// This is probably an asset type, format it nicely
|
|
option.textContent = item.charAt(0).toUpperCase() + item.slice(1).replace(/-/g, ' ');
|
|
}
|
|
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
clearDropdowns(['productName', 'assetTypeA', 'assetTypeB', 'assetTypeC']);
|
|
}
|
|
|
|
function updateProductNames() {
|
|
if (!structureData) return;
|
|
|
|
const gpdNumber = document.getElementById('gpdNumber').value;
|
|
const productCategory = document.getElementById('productCategory').value;
|
|
const select = document.getElementById('productName');
|
|
select.innerHTML = '<option value="">-- Select Product Name --</option>';
|
|
|
|
// Only add "Generic" option for consumables
|
|
if (productCategory === 'consumables') {
|
|
const genericOption = document.createElement('option');
|
|
genericOption.value = 'generic';
|
|
genericOption.textContent = 'Generic / No Specific Product';
|
|
select.appendChild(genericOption);
|
|
}
|
|
|
|
// Collect product names based on category
|
|
const allProductNames = new Set();
|
|
|
|
// If we have a specific GPD selected, show its products
|
|
if (gpdNumber && gpdNumber !== 'generic' && structureData.dropdownRelationships["3"][gpdNumber]) {
|
|
structureData.dropdownRelationships["3"][gpdNumber].forEach(name => {
|
|
allProductNames.add(name);
|
|
});
|
|
}
|
|
|
|
// For consumables only, also show all available product names when using generic GPD
|
|
if (productCategory === 'consumables' && (gpdNumber === 'generic' || !gpdNumber)) {
|
|
// Add all product names from all GPD numbers
|
|
if (structureData.dropdownRelationships["3"]) {
|
|
Object.values(structureData.dropdownRelationships["3"]).forEach(names => {
|
|
if (Array.isArray(names)) {
|
|
names.forEach(name => allProductNames.add(name));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Add all collected product names to the dropdown
|
|
const sortedNames = Array.from(allProductNames).sort();
|
|
sortedNames.forEach(name => {
|
|
const option = document.createElement('option');
|
|
option.value = name;
|
|
option.textContent = name.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
select.appendChild(option);
|
|
});
|
|
|
|
clearDropdowns(['assetTypeA', 'assetTypeB', 'assetTypeC']);
|
|
hideColorwayGroup();
|
|
updateAssetTypeA();
|
|
}
|
|
|
|
function updateAssetTypeA() {
|
|
if (!structureData) return;
|
|
|
|
const productName = document.getElementById('productName').value;
|
|
const select = document.getElementById('assetTypeA');
|
|
select.innerHTML = '<option value="">-- Select Asset Type A --</option>';
|
|
|
|
if (productName) {
|
|
// Always show all basic asset types for any product
|
|
const basicAssetTypes = ['digital', 'imagery', 'logo', 'parts'];
|
|
|
|
basicAssetTypes.forEach(assetType => {
|
|
const option = document.createElement('option');
|
|
option.value = assetType;
|
|
option.textContent = assetType.charAt(0).toUpperCase() + assetType.slice(1);
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateAssetTypeB() {
|
|
if (!structureData) return;
|
|
|
|
const assetTypeA = document.getElementById('assetTypeA').value;
|
|
const select = document.getElementById('assetTypeB');
|
|
select.innerHTML = '<option value="">-- Select Asset Type B --</option>';
|
|
|
|
if (assetTypeA && structureData.dropdownRelationships["6"][assetTypeA]) {
|
|
structureData.dropdownRelationships["6"][assetTypeA].forEach(assetType => {
|
|
const option = document.createElement('option');
|
|
option.value = assetType;
|
|
option.textContent = assetType.charAt(0).toUpperCase() + assetType.slice(1);
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
// Always show colorway dropdown - it will show NCW if no colorways available
|
|
showColorwayGroup();
|
|
|
|
clearDropdowns(['assetTypeC']);
|
|
}
|
|
|
|
function updateAssetTypeC() {
|
|
if (!structureData) return;
|
|
|
|
const assetTypeB = document.getElementById('assetTypeB').value;
|
|
const select = document.getElementById('assetTypeC');
|
|
const group = document.getElementById('assetTypeCGroup');
|
|
|
|
select.innerHTML = '<option value="">-- Select Color Profile --</option>';
|
|
|
|
if (assetTypeB && structureData.dropdownRelationships["7"][assetTypeB]) {
|
|
if (structureData.dropdownRelationships["7"][assetTypeB].length > 0) {
|
|
group.style.display = 'block';
|
|
structureData.dropdownRelationships["7"][assetTypeB].forEach(colorProfile => {
|
|
const option = document.createElement('option');
|
|
option.value = colorProfile;
|
|
option.textContent = colorProfile.toUpperCase();
|
|
select.appendChild(option);
|
|
});
|
|
} else {
|
|
group.style.display = 'none';
|
|
}
|
|
} else {
|
|
group.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function showColorwayGroup() {
|
|
if (!structureData) return;
|
|
|
|
const productName = document.getElementById('productName').value;
|
|
const colorwayGroup = document.getElementById('colorwayGroup');
|
|
const colorwaySelect = document.getElementById('colorway');
|
|
|
|
if (productName) {
|
|
if (structureData.dropdownRelationships["4"][productName]) {
|
|
// Product has colorways - show them
|
|
colorwaySelect.innerHTML = '<option value="">-- Select Colorway (Optional) --</option>';
|
|
colorwaySelect.innerHTML += '<option value="NCW">NCW (No Colorway)</option>';
|
|
structureData.dropdownRelationships["4"][productName].forEach(color => {
|
|
const option = document.createElement('option');
|
|
option.value = color;
|
|
option.textContent = color.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
colorwaySelect.appendChild(option);
|
|
});
|
|
} else {
|
|
// Product has no colorways - default to NCW
|
|
colorwaySelect.innerHTML = '<option value="">-- Select Colorway --</option>';
|
|
colorwaySelect.innerHTML += '<option value="NCW">NCW (No Colorway)</option>';
|
|
}
|
|
colorwayGroup.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
function hideColorwayGroup() {
|
|
const colorwayGroup = document.getElementById('colorwayGroup');
|
|
const colorwaySelect = document.getElementById('colorway');
|
|
colorwayGroup.style.display = 'none';
|
|
colorwaySelect.innerHTML = '<option value="">-- Select Colorway (Optional) --</option>';
|
|
}
|
|
|
|
function clearDropdowns(dropdownIds) {
|
|
dropdownIds.forEach(id => {
|
|
const select = document.getElementById(id);
|
|
if (select) {
|
|
let placeholder = '';
|
|
switch(id) {
|
|
case 'productName':
|
|
placeholder = '-- Select Product Name --';
|
|
break;
|
|
case 'assetTypeA':
|
|
placeholder = '-- Select Asset Type A --';
|
|
break;
|
|
case 'assetTypeB':
|
|
placeholder = '-- Select Asset Type B --';
|
|
break;
|
|
case 'assetTypeC':
|
|
placeholder = '-- Select Color Profile --';
|
|
break;
|
|
case 'colorway':
|
|
placeholder = '-- Select Colorway (Optional) --';
|
|
break;
|
|
default:
|
|
placeholder = `-- Select ${id.replace(/([A-Z])/g, ' $1').trim()} --`;
|
|
}
|
|
select.innerHTML = `<option value="">${placeholder}</option>`;
|
|
}
|
|
});
|
|
|
|
// Hide groups when clearing
|
|
if (dropdownIds.includes('assetTypeC')) {
|
|
document.getElementById('assetTypeCGroup').style.display = 'none';
|
|
}
|
|
if (dropdownIds.includes('colorway')) {
|
|
hideColorwayGroup();
|
|
}
|
|
}
|
|
|
|
|
|
function generateFilename() {
|
|
const omgJobNumber = document.getElementById('omgJobNumber').value;
|
|
const productCategory = document.getElementById('productCategory').value;
|
|
const productType = document.getElementById('productType').value;
|
|
const gpdNumber = document.getElementById('gpdNumber').value;
|
|
const productName = document.getElementById('productName').value;
|
|
const assetTypeA = document.getElementById('assetTypeA').value;
|
|
const assetTypeB = document.getElementById('assetTypeB').value;
|
|
const assetTypeC = document.getElementById('assetTypeC').value;
|
|
const colorway = document.getElementById('colorway').value;
|
|
const customDescriptor = document.getElementById('customDescriptor').value;
|
|
|
|
// Validate required fields (with flexibility for generic options)
|
|
if (!omgJobNumber || !productCategory || !productType || !assetTypeA || !assetTypeB || !colorway) {
|
|
alert('Please fill in all required fields.');
|
|
return;
|
|
}
|
|
|
|
// Validate custom descriptor doesn't contain underscores
|
|
if (customDescriptor && customDescriptor.includes('_')) {
|
|
alert('Custom descriptor cannot contain underscores. Please use other characters only.');
|
|
return;
|
|
}
|
|
|
|
// Handle cases where GPD or Product Name might be generic/missing
|
|
const effectiveGPD = gpdNumber || 'generic';
|
|
const effectiveProductName = productName || 'generic';
|
|
|
|
// Build the path components for folder structure
|
|
const folderComponents = [
|
|
'1-product-assets',
|
|
productCategory,
|
|
productType,
|
|
effectiveGPD,
|
|
effectiveProductName,
|
|
assetTypeA,
|
|
assetTypeB
|
|
];
|
|
|
|
// Build original filename components (without 1-product-assets)
|
|
const filenameComponents = [
|
|
productCategory,
|
|
productType,
|
|
effectiveGPD,
|
|
effectiveProductName,
|
|
assetTypeA,
|
|
assetTypeB
|
|
];
|
|
|
|
// Add Asset Type C if present
|
|
if (assetTypeC) {
|
|
folderComponents.push(assetTypeC);
|
|
filenameComponents.push(assetTypeC);
|
|
}
|
|
|
|
// Add colorway to folder path only if it's not NCW
|
|
if (colorway && colorway !== 'NCW') {
|
|
folderComponents.push(colorway);
|
|
}
|
|
// Always add colorway to original filename components
|
|
filenameComponents.push(colorway);
|
|
|
|
// Generate original filename
|
|
let filename = `${omgJobNumber}_${filenameComponents.join('_')}`;
|
|
|
|
// Add custom descriptor to original filename if provided
|
|
if (customDescriptor && customDescriptor.trim()) {
|
|
filename += '_' + customDescriptor.trim();
|
|
}
|
|
|
|
filename += '.ext';
|
|
|
|
// Generate DAM filename (renamed version)
|
|
let damFilenameComponents = [];
|
|
|
|
// Add custom descriptor first if provided
|
|
if (customDescriptor && customDescriptor.trim()) {
|
|
damFilenameComponents.push(customDescriptor.trim());
|
|
}
|
|
|
|
// Start from GPD number (skip job number, dry, robot)
|
|
damFilenameComponents.push(effectiveGPD);
|
|
damFilenameComponents.push(effectiveProductName);
|
|
damFilenameComponents.push(assetTypeA);
|
|
damFilenameComponents.push(assetTypeB);
|
|
|
|
if (assetTypeC) {
|
|
damFilenameComponents.push(assetTypeC);
|
|
}
|
|
|
|
// Add colorway only if it's not NCW
|
|
if (colorway && colorway !== 'NCW') {
|
|
damFilenameComponents.push(colorway);
|
|
}
|
|
|
|
// OMG job number is removed from DAM filename and goes to metadata
|
|
|
|
let damFilename = damFilenameComponents.join('_') + '.ext';
|
|
|
|
// Generate metadata info
|
|
let metadataItems = [];
|
|
metadataItems.push(`Original Job Number: ${omgJobNumber}`);
|
|
|
|
if (productCategory === 'dry' || productType === 'robot') {
|
|
metadataItems.push(`Removed from filename: ${productCategory === 'dry' ? 'dry' : ''} ${productType === 'robot' ? 'robot' : ''}`.trim());
|
|
}
|
|
|
|
if (colorway === 'NCW') {
|
|
metadataItems.push('Colorway: NCW (No Colorway) - removed from DAM filename');
|
|
}
|
|
|
|
// Custom descriptor is shown in both filenames, not removed
|
|
|
|
const folderPath = `/content/dam/bissell/${folderComponents.join('/')}/`;
|
|
|
|
document.getElementById('generatedFilename').textContent = filename;
|
|
document.getElementById('generatedPath').textContent = folderPath;
|
|
document.getElementById('damFilename').textContent = damFilename;
|
|
document.getElementById('metadataInfo').innerHTML = metadataItems.map(item => `• ${item}`).join('<br>');
|
|
document.getElementById('result').style.display = 'block';
|
|
}
|
|
|
|
function copyToClipboard() {
|
|
const filename = document.getElementById('generatedFilename').textContent;
|
|
navigator.clipboard.writeText(filename).then(() => {
|
|
alert('Original filename copied to clipboard!');
|
|
});
|
|
}
|
|
|
|
function copyDAMFilenameToClipboard() {
|
|
const damFilename = document.getElementById('damFilename').textContent;
|
|
navigator.clipboard.writeText(damFilename).then(() => {
|
|
alert('DAM filename copied to clipboard!');
|
|
});
|
|
}
|
|
|
|
function copyPathToClipboard() {
|
|
const path = document.getElementById('generatedPath').textContent;
|
|
navigator.clipboard.writeText(path).then(() => {
|
|
alert('DAM path copied to clipboard!');
|
|
});
|
|
}
|
|
|
|
|
|
// Decoder function
|
|
function decodeFilename() {
|
|
const filename = document.getElementById('filenameInput').value.trim();
|
|
const resultDiv = document.getElementById('decodedResult');
|
|
|
|
if (!filename) {
|
|
resultDiv.innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
// Remove file extension if present
|
|
const nameWithoutExt = filename.replace(/\.[^/.]+$/, '');
|
|
|
|
// Check for description (pipe delimiter)
|
|
let description = '';
|
|
let mainFilename = nameWithoutExt;
|
|
if (nameWithoutExt.includes('|')) {
|
|
const pipeIndex = nameWithoutExt.indexOf('|');
|
|
mainFilename = nameWithoutExt.substring(0, pipeIndex);
|
|
description = nameWithoutExt.substring(pipeIndex + 1);
|
|
}
|
|
|
|
// Split by underscores
|
|
const parts = mainFilename.split('_');
|
|
|
|
if (parts.length < 6) {
|
|
resultDiv.innerHTML = `
|
|
<div class="card" style="border-left-color: #e74c3c;">
|
|
<h4 style="color: #e74c3c;">❌ Invalid Filename Format</h4>
|
|
<p>The filename should have at least 6 parts separated by underscores:</p>
|
|
<p><strong>Format:</strong> OMG-JOB-NUMBER_category_type_gpd_product_assetA_assetB_[colorProfile]_[colorway]_[customDescriptor]</p>
|
|
<p><strong>Example:</strong> 12345678_dry_robot_p2990_crosswave-robot_imagery_hero_rgb_NCW_250x250banner1.ext</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
// Parse the components
|
|
const components = {
|
|
omgJobNumber: parts[0],
|
|
productCategory: parts[1],
|
|
productType: parts[2],
|
|
gpdNumber: parts[3],
|
|
productName: parts[4],
|
|
assetTypeA: parts[5],
|
|
assetTypeB: parts[6] || '',
|
|
assetTypeC: '',
|
|
colorway: '',
|
|
customDescriptor: ''
|
|
};
|
|
|
|
// Handle the last part - could be colorway or custom descriptor
|
|
if (parts.length > 6) {
|
|
// Check if the last part looks like a custom descriptor (contains letters/numbers but not typical colorway pattern)
|
|
const lastPart = parts[parts.length - 1];
|
|
const isCustomDescriptor = /\d/.test(lastPart) && !/^[a-z-]+$/.test(lastPart);
|
|
|
|
if (isCustomDescriptor) {
|
|
components.customDescriptor = lastPart;
|
|
// Colorway would be second to last if present
|
|
if (parts.length > 7) {
|
|
const secondToLast = parts[parts.length - 2];
|
|
if (secondToLast !== 'rgb' && secondToLast !== 'cmyk') {
|
|
components.colorway = secondToLast;
|
|
}
|
|
}
|
|
} else {
|
|
components.colorway = lastPart;
|
|
}
|
|
|
|
// Handle color profile (rgb/cmyk)
|
|
if (parts.length >= 8) {
|
|
let colorProfileIndex = -1;
|
|
for (let i = parts.length - 1; i >= 6; i--) {
|
|
if (parts[i] === 'rgb' || parts[i] === 'cmyk') {
|
|
components.assetTypeC = parts[i];
|
|
colorProfileIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// AssetTypeB is typically at index 6
|
|
if (parts.length > 6) {
|
|
components.assetTypeB = parts[6];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build folder path (NCW colorway doesn't create a folder)
|
|
const folderComponents = [
|
|
'1-product-assets',
|
|
components.productCategory,
|
|
components.productType,
|
|
components.gpdNumber,
|
|
components.productName,
|
|
components.assetTypeA,
|
|
components.assetTypeB
|
|
];
|
|
|
|
if (components.assetTypeC) {
|
|
folderComponents.push(components.assetTypeC);
|
|
}
|
|
|
|
if (components.colorway && components.colorway !== 'NCW') {
|
|
folderComponents.push(components.colorway);
|
|
}
|
|
|
|
const folderPath = folderComponents.join('/');
|
|
|
|
// Build result HTML
|
|
resultDiv.innerHTML = `
|
|
<div class="card" style="border-left-color: #27ae60;">
|
|
<h4 style="color: #27ae60;">✅ Decoded Successfully</h4>
|
|
|
|
<div style="margin: 15px 0;">
|
|
<h5>📁 Folder Structure:</h5>
|
|
<div class="code" style="background: #f8f9fa; padding: 10px; border-radius: 5px; font-family: monospace;">
|
|
${folderPath}/
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin: 15px 0;">
|
|
<h5>📄 Filename Components:</h5>
|
|
<table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
|
|
<tr style="background: #f8f9fa;">
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Component</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Value</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Description</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">OMG Job Number</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.omgJobNumber}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Project identifier prefix</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Product Category</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.productCategory}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Main product line (wet, dry, consumables, etc.)</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Product Type</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.productType}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Specific product type (canister, upright, etc.)</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">GPD Number</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.gpdNumber}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Product development number</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Product Name</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.productName}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Specific product model name</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Asset Type A</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.assetTypeA}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Primary asset category (digital, imagery, etc.)</td>
|
|
</tr>
|
|
${components.assetTypeB ? `
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Asset Type B</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.assetTypeB}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Secondary asset category (hero, banner, etc.)</td>
|
|
</tr>
|
|
` : ''}
|
|
${components.assetTypeC ? `
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Color Profile</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.assetTypeC}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Color space (RGB or CMYK)</td>
|
|
</tr>
|
|
` : ''}
|
|
${components.colorway ? `
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Colorway</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.colorway}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Product color variant or NCW (No Colorway)</td>
|
|
</tr>
|
|
` : ''}
|
|
${components.customDescriptor ? `
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Custom Descriptor</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${components.customDescriptor}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Custom text (moves to front in DAM filename)</td>
|
|
</tr>
|
|
` : ''}
|
|
${description ? `
|
|
<tr>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Description</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd; font-family: monospace;">${description}</td>
|
|
<td style="padding: 8px; border: 1px solid #ddd;">Legacy description field</td>
|
|
</tr>
|
|
` : ''}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Add global keyboard navigation
|
|
document.addEventListener('keydown', function(event) {
|
|
if (event.key === 'Enter') {
|
|
// Handle decoder input
|
|
if (event.target && event.target.id === 'filenameInput') {
|
|
event.preventDefault();
|
|
decodeFilename();
|
|
return;
|
|
}
|
|
|
|
// Check if we're in the naming wizard tab
|
|
const namingTab = document.getElementById('naming-wizard');
|
|
if (!namingTab || !namingTab.classList.contains('active')) {
|
|
return; // Not in naming wizard, let default behavior happen
|
|
}
|
|
|
|
// Find which step we're currently on
|
|
let currentStepNum = currentStep;
|
|
|
|
// Get the current step's input/select element
|
|
const currentStepElement = document.getElementById(`step-${currentStepNum}`);
|
|
if (!currentStepElement) return;
|
|
|
|
const activeInput = currentStepElement.querySelector('input, select');
|
|
if (!activeInput) return;
|
|
|
|
// Check if the current element is focused or if Enter was pressed on it
|
|
if (document.activeElement === activeInput || event.target === activeInput) {
|
|
event.preventDefault();
|
|
|
|
// Validate current field has content
|
|
if (activeInput.tagName === 'INPUT' && !activeInput.value.trim()) {
|
|
console.log('Input field is empty, not advancing');
|
|
return;
|
|
}
|
|
if (activeInput.tagName === 'SELECT' && !activeInput.value) {
|
|
console.log('No selection made, not advancing');
|
|
return;
|
|
}
|
|
|
|
console.log(`Advancing from step ${currentStepNum} via Enter key`);
|
|
|
|
// Move to next step
|
|
if (currentStepNum < totalSteps) {
|
|
nextStep();
|
|
} else {
|
|
// Final step - generate filename
|
|
generateFilename();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Initialize the app when page loads
|
|
document.addEventListener('DOMContentLoaded', async function() {
|
|
await init();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |