From b80f7b314f08493bd0d223c91c6fd18292983e09 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Tue, 16 Sep 2025 23:15:02 -0400 Subject: [PATCH] generated ui client --- wow-item-scaler-web/README.md | 174 ++++++ wow-item-scaler-web/css/wow-theme.css | 772 ++++++++++++++++++++++++++ wow-item-scaler-web/images/README.md | 34 ++ wow-item-scaler-web/index.html | 143 +++++ wow-item-scaler-web/js/item-scaler.js | 342 ++++++++++++ wow-item-scaler-web/js/wow-theme.js | 250 +++++++++ 6 files changed, 1715 insertions(+) create mode 100644 wow-item-scaler-web/README.md create mode 100644 wow-item-scaler-web/css/wow-theme.css create mode 100644 wow-item-scaler-web/images/README.md create mode 100644 wow-item-scaler-web/index.html create mode 100644 wow-item-scaler-web/js/item-scaler.js create mode 100644 wow-item-scaler-web/js/wow-theme.js diff --git a/wow-item-scaler-web/README.md b/wow-item-scaler-web/README.md new file mode 100644 index 0000000..8ff2e0d --- /dev/null +++ b/wow-item-scaler-web/README.md @@ -0,0 +1,174 @@ +# WoW Item Scaler Web Frontend + +A beautiful World of Warcraft-themed web interface for scaling items using your existing Go backend. + +## Features + +- **Immersive WoW-styled UI** with layered backgrounds, magical particle effects, and authentic textures +- **Modern visual effects** including animated glowing elements, parallax backgrounds, and fantasy decorations +- **Multiple input methods**: + - Single item by entry ID + - Comma-separated list of entry IDs + - Search by item name with dropdown selection +- **Visual item comparison** showing before/after scaling +- **Wowhead integration** for item images and tooltips +- **Responsive design** that works on desktop and mobile +- **Export functionality** for SQL and copy-to-clipboard + +## Dependencies & Libraries Used + +### External CDN Libraries +1. **Tailwind CSS** (`https://cdn.tailwindcss.com`) + - Utility-first CSS framework + - Used for responsive layouts, spacing, and utility classes + - No build process required + +2. **Wowhead Power** (`https://wow.zamimg.com/widgets/power.js`) + - Official Wowhead tooltips and item integration + - Provides item images and tooltip functionality + - Authentic WoW item data + +3. **Google Fonts** + - **Cinzel**: Elegant serif font for body text and UI elements + - **Uncial Antiqua**: Fantasy/medieval font for titles and headings + - Provides authentic WoW-like typography + +### Custom Theme System +- **Custom CSS Variables** for consistent WoW color scheme +- **Modular component classes** (`.wow-panel`, `.wow-button`, etc.) +- **Quality-based item coloring** (epic purple, legendary orange) +- **Animated effects** and hover states + +## Setup + +1. Open `index.html` in a web browser +2. Update the backend URL in `js/item-scaler.js` (line 4) to match your Go backend server +3. Ensure your backend provides these API endpoints: + - `POST /api/scale-items` - Scale items with parameters + - `GET /api/search-items?q=query` - Search items by name + - `POST /api/export-sql` - Export scaled items as SQL + +## Backend API Expected Format + +### Scale Items Request +```json +{ + "itemLevel": 325, + "phase": 1, + "quality": 4, + "catchup": 1.5, + "entries": [19019, 19103, 17076] +} +``` + +### Scale Items Response +```json +{ + "items": [ + { + "success": true, + "entryId": 19019, + "original": { + "entry": 19019, + "name": "Thunderfury, Blessed Blade of the Windseeker", + "quality": 5, + "itemLevel": 80, + "stats": { "Stamina": 15, "Agility": 5 } + }, + "scaled": { + "entry": 19019, + "name": "Thunderfury, Blessed Blade of the Windseeker", + "quality": 5, + "itemLevel": 325, + "stats": { "Stamina": 45, "Agility": 15 } + }, + "referenceItem": { + "name": "Some Reference Item", + "entry": 12345 + } + } + ], + "summary": { + "total": 1, + "successful": 1, + "failed": 0, + "successRate": "100.0" + } +} +``` + +### Search Items Response +```json +[ + { + "entry": 19019, + "name": "Thunderfury, Blessed Blade of the Windseeker", + "quality": 5 + } +] +``` + +## File Structure + +``` +wow-item-scaler-web/ +├── index.html # Main HTML file with CDN dependencies +├── css/ +│ └── wow-theme.css # Complete WoW theme styles (8.4KB) +├── js/ +│ ├── wow-theme.js # Theme utilities and UI components (9.8KB) +│ └── item-scaler.js # Main application logic and API calls (12KB) +└── README.md # This documentation +``` + +## Technical Details + +### CSS Architecture +- **CSS Custom Properties** for theme consistency +- **Component-based styling** with `.wow-` prefixed classes +- **Layered background system** with multiple gradients, textures, and particle effects +- **Animated decorative elements** including glowing gems, magical sparkles, and pulsing effects +- **Responsive breakpoints** for mobile compatibility +- **Advanced visual effects** using pure CSS (no external images required) + +### JavaScript Architecture +- **Class-based ES6 modules** for organization +- **Event-driven architecture** for user interactions +- **LocalStorage integration** for user preferences +- **Error handling** with user-friendly notifications + +### Browser Compatibility +- Modern browsers with ES6+ support +- Chrome, Firefox, Safari, Edge +- Mobile browsers (responsive design) + +## Customization + +### Backend URL +Update the `backendUrl` in `js/item-scaler.js`: +```javascript +this.backendUrl = 'http://your-backend-server:8080'; +``` + +### Styling +The WoW theme can be customized in `css/wow-theme.css`. Key variables: +```css +:root { + --wow-gold: #ffd100; /* Primary accent color */ + --wow-blue: #0084ff; /* Rare item color */ + --wow-purple: #a335ee; /* Epic item color */ + --wow-orange: #ff8000; /* Legendary item color */ + --wow-bg-dark: #0a0a0a; /* Primary background */ + --wow-bg-panel: #1a1a1a; /* Panel background */ +} +``` + +### Item Icons +Item icons are loaded from Wowhead. Enhance the `getItemIcon()` function in `wow-theme.js` with your own icon mapping logic. + +## No Build Process Required +This is a pure frontend application that runs directly in the browser: +- No bundlers (Webpack, Vite, etc.) +- No transpilation needed +- No package.json or npm install +- Just open `index.html` and go! \ No newline at end of file diff --git a/wow-item-scaler-web/css/wow-theme.css b/wow-item-scaler-web/css/wow-theme.css new file mode 100644 index 0000000..ae92312 --- /dev/null +++ b/wow-item-scaler-web/css/wow-theme.css @@ -0,0 +1,772 @@ +/* Araxia Online - Wrath of the Lich King Theme */ +:root { + --araxia-primary: #11111f; /* Deep navy from Araxia */ + --araxia-text: #f8f8ff; /* Ghost white from Araxia */ + --wotlk-ice-blue: #4fc3f7; /* Frost blue accent */ + --wotlk-ice-light: #81d4fa; /* Light ice blue */ + --wotlk-ice-dark: #0288d1; /* Dark ice blue */ + --wotlk-frost: #b3e5fc; /* Frost white-blue */ + --wotlk-shadow: #1a237e; /* Deep shadow blue */ + --wow-purple: #a335ee; /* Epic purple (unchanged) */ + --wow-orange: #ff8000; /* Legendary orange (unchanged) */ + --wow-green: #1eff00; /* Uncommon green (unchanged) */ + --wow-red: #ff0000; /* Error red (unchanged) */ + --wow-bg-dark: var(--araxia-primary); + --wow-bg-panel: #1e1e3f; /* Slightly lighter navy */ + --wow-bg-card: #252547; /* Card background - navy tinted */ + --wow-border: #4fc3f7; /* Ice blue borders */ + --wow-border-primary: var(--wotlk-ice-blue); + --wow-text-primary: var(--araxia-text); + --wow-text-secondary: var(--wotlk-frost); + --wow-text-muted: #7986cb; /* Muted ice blue */ +} + +/* Background with Araxia/WotLK ice-themed layers and mage imagery */ +/* Body and main container for sticky footer */ +body.wow-bg { + min-height: 100vh; + display: flex; + flex-direction: column; + background: + /* Frost crystal pattern overlay */ + radial-gradient(circle at 25% 25%, rgba(79, 195, 247, 0.06) 0%, transparent 50%), + radial-gradient(circle at 75% 75%, rgba(129, 212, 250, 0.04) 0%, transparent 50%), + radial-gradient(circle at 50% 10%, rgba(26, 35, 126, 0.05) 0%, transparent 60%), + /* CSS-based frost/ice landscape simulation */ + radial-gradient(ellipse at 30% 20%, rgba(179, 229, 252, 0.1) 0%, transparent 40%), + radial-gradient(ellipse at 70% 80%, rgba(26, 35, 126, 0.08) 0%, transparent 50%), + /* Mountain/landscape silhouettes */ + linear-gradient(180deg, + transparent 0%, + transparent 60%, + rgba(79, 195, 247, 0.05) 65%, + rgba(129, 212, 250, 0.08) 75%, + rgba(17, 17, 31, 0.3) 85%, + rgba(37, 37, 71, 0.6) 100% + ), + /* Enhanced CSS frost landscape simulation */ + conic-gradient(from 180deg at 20% 30%, + rgba(79, 195, 247, 0.1) 0deg, + rgba(26, 35, 126, 0.05) 60deg, + rgba(179, 229, 252, 0.08) 120deg, + rgba(79, 195, 247, 0.1) 180deg, + rgba(26, 35, 126, 0.05) 240deg, + rgba(179, 229, 252, 0.08) 300deg, + rgba(79, 195, 247, 0.1) 360deg + ), + /* Iceberg/mountain silhouette effect */ + radial-gradient(ellipse 800px 400px at 30% 100%, + rgba(37, 37, 71, 0.4) 0%, + rgba(79, 195, 247, 0.1) 40%, + transparent 70% + ), + radial-gradient(ellipse 600px 300px at 70% 100%, + rgba(26, 35, 126, 0.3) 0%, + rgba(129, 212, 250, 0.08) 50%, + transparent 80% + ), + /* Local images with proper sizing and color filtering */ + url('images/wotlk-bg1.jpg'), + url('images/wotlk-bg2.jpg'), + url('images/icecrown-bg.jpg'), + /* Theme matching overlay for images */ + linear-gradient(45deg, + rgba(17, 17, 31, 0.85) 0%, + rgba(30, 30, 63, 0.88) 25%, + rgba(17, 17, 31, 0.85) 50%, + rgba(37, 37, 71, 0.88) 75%, + rgba(17, 17, 31, 0.85) 100% + ), + /* Base Araxia navy gradient */ + linear-gradient(135deg, var(--araxia-primary) 0%, #1e1e3f 30%, var(--araxia-primary) 60%, #252547 100%), + /* Ice crystal texture pattern */ + repeating-linear-gradient( + 30deg, + transparent, + transparent 3px, + rgba(79, 195, 247, 0.015) 3px, + rgba(79, 195, 247, 0.015) 6px + ), + repeating-linear-gradient( + -30deg, + transparent, + transparent 3px, + rgba(129, 212, 250, 0.01) 3px, + rgba(129, 212, 250, 0.01) 6px + ); + background-attachment: fixed; + background-size: + 1000px 1000px, + 800px 800px, + 600px 600px, + 1200px 800px, + 800px 600px, + cover, + cover, + cover, + cover, + cover, + /* Image sizing with proper aspect ratio maintenance */ + cover, + cover, + cover, + cover, + cover, + 6px 6px, + 6px 6px; + background-position: + center, + center, + center, + center top, + center bottom, + center, + center, + center, + center, + center, + /* Image positioning for best visual impact */ + center center, + left center, + right center, + center, + center, + center, + center; + background-repeat: no-repeat; + color: var(--wow-text-primary); + font-family: 'Cinzel', serif; + position: relative; + /* Color adjustment filter for better theme matching */ + filter: hue-rotate(-10deg) saturate(1.1) brightness(0.95); +} + +/* Main content area that pushes footer down */ +main { + flex: 1; +} + +/* Add subtle frost animated effects */ +.wow-bg::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: + radial-gradient(circle at 20% 20%, rgba(79, 195, 247, 0.03) 0%, transparent 30%), + radial-gradient(circle at 80% 80%, rgba(129, 212, 250, 0.02) 0%, transparent 40%), + radial-gradient(circle at 60% 60%, rgba(26, 35, 126, 0.02) 0%, transparent 50%); + animation: wow-frost-glow 12s ease-in-out infinite alternate; + pointer-events: none; + z-index: -1; +} + +@keyframes wow-frost-glow { + 0% { opacity: 0.4; transform: scale(1) rotate(0deg); } + 100% { opacity: 0.8; transform: scale(1.03) rotate(0.5deg); } +} + +/* Header with Araxia/WotLK ice theme */ +.wow-header { + background: + /* Frost border pattern */ + linear-gradient(90deg, transparent 0%, rgba(79, 195, 247, 0.15) 20%, rgba(79, 195, 247, 0.25) 50%, rgba(79, 195, 247, 0.15) 80%, transparent 100%), + /* Ice texture */ + linear-gradient(180deg, #252547 0%, #1e1e3f 20%, #2a2a52 40%, #1e1e3f 60%, #252547 100%), + /* Crystal-like pattern */ + repeating-linear-gradient(45deg, transparent, transparent 1px, rgba(79, 195, 247, 0.03) 1px, rgba(79, 195, 247, 0.03) 2px); + border-image: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent) 1; + position: relative; + overflow: hidden; +} + +.wow-header::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent); + box-shadow: 0 0 5px rgba(79, 195, 247, 0.4); +} + +.wow-header::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 200px; + height: 3px; + background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), var(--wotlk-ice-light), var(--wotlk-ice-blue), transparent); + box-shadow: 0 0 15px rgba(79, 195, 247, 0.6); +} + +.wow-logo-container { + text-align: center; +} + +.araxia-logo { + max-width: 120px; + height: auto; + filter: drop-shadow(0 0 10px rgba(79, 195, 247, 0.4)); + transition: all 0.3s ease; +} + +.araxia-logo:hover { + filter: drop-shadow(0 0 20px rgba(79, 195, 247, 0.8)); + transform: scale(1.05); +} + +.wow-server-name { + color: var(--wotlk-ice-light); + font-size: 0.9rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 0.25rem; + text-shadow: 0 0 10px rgba(129, 212, 250, 0.6); +} + +.wow-title { + font-family: 'Uncial Antiqua', serif; + color: var(--wotlk-ice-blue); + text-shadow: + 2px 2px 4px rgba(0, 0, 0, 0.9), + 0 0 20px rgba(79, 195, 247, 0.8), + 0 0 40px rgba(129, 212, 250, 0.4); + letter-spacing: 2px; + position: relative; + display: inline-block; +} + +.wow-title::before { + content: '❄'; + position: absolute; + left: -2rem; + top: 50%; + transform: translateY(-50%); + color: var(--wotlk-ice-light); + font-size: 0.8em; + text-shadow: 0 0 15px rgba(129, 212, 250, 0.9); + animation: wow-ice-pulse 4s ease-in-out infinite; +} + +.wow-title::after { + content: '❄'; + position: absolute; + right: -2rem; + top: 50%; + transform: translateY(-50%) rotate(45deg); + color: var(--wotlk-ice-light); + font-size: 0.8em; + text-shadow: 0 0 15px rgba(129, 212, 250, 0.9); + animation: wow-ice-pulse 4s ease-in-out infinite reverse; +} + +@keyframes wow-ice-pulse { + 0%, 100% { opacity: 0.7; transform: translateY(-50%) scale(1) rotate(0deg); } + 50% { opacity: 1; transform: translateY(-50%) scale(1.2) rotate(15deg); } +} + +.wow-subtitle { + color: var(--wow-text-secondary); + font-style: italic; + margin-top: -5px; +} + +/* Panels */ +.wow-panel { + background: + /* Ornate corner decorations */ + radial-gradient(circle at 0% 0%, rgba(255, 209, 0, 0.1) 0%, transparent 15%), + radial-gradient(circle at 100% 0%, rgba(255, 209, 0, 0.1) 0%, transparent 15%), + radial-gradient(circle at 0% 100%, rgba(255, 209, 0, 0.1) 0%, transparent 15%), + radial-gradient(circle at 100% 100%, rgba(255, 209, 0, 0.1) 0%, transparent 15%), + /* Stone-like texture */ + linear-gradient(135deg, rgba(26, 26, 26, 0.95) 0%, rgba(30, 30, 30, 0.98) 50%, rgba(22, 22, 22, 0.95) 100%), + /* Subtle pattern */ + repeating-linear-gradient(0deg, transparent, transparent 1px, rgba(255, 255, 255, 0.01) 1px, rgba(255, 255, 255, 0.01) 2px); + border: 2px solid var(--wow-border); + border-radius: 8px; + box-shadow: + 0 8px 32px rgba(0, 0, 0, 0.6), + inset 0 1px 0 rgba(255, 209, 0, 0.1), + inset 0 -1px 0 rgba(0, 0, 0, 0.5); + overflow: hidden; + position: relative; +} + +.wow-panel::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + linear-gradient(45deg, transparent 48%, rgba(255, 209, 0, 0.05) 49%, rgba(255, 209, 0, 0.05) 51%, transparent 52%), + linear-gradient(-45deg, transparent 48%, rgba(255, 209, 0, 0.03) 49%, rgba(255, 209, 0, 0.03) 51%, transparent 52%); + pointer-events: none; +} + +.wow-panel-header { + background: + /* Ornate header pattern */ + linear-gradient(90deg, + rgba(255, 209, 0, 0.05) 0%, + rgba(255, 209, 0, 0.1) 10%, + rgba(255, 209, 0, 0.08) 50%, + rgba(255, 209, 0, 0.1) 90%, + rgba(255, 209, 0, 0.05) 100% + ), + linear-gradient(90deg, var(--wow-bg-card), var(--wow-bg-panel)); + border-bottom: 2px solid var(--wow-border-gold); + padding: 1rem 1.5rem; + display: flex; + justify-content: space-between; + align-items: center; + position: relative; +} + +.wow-panel-header::before { + content: ''; + position: absolute; + top: 50%; + left: 1rem; + transform: translateY(-50%); + width: 8px; + height: 8px; + background: radial-gradient(circle, var(--wow-gold), var(--wow-gold-dark)); + border-radius: 50%; + box-shadow: 0 0 8px rgba(255, 209, 0, 0.6); +} + +.wow-panel-header::after { + content: ''; + position: absolute; + top: 50%; + right: 1rem; + transform: translateY(-50%); + width: 8px; + height: 8px; + background: radial-gradient(circle, var(--wow-gold), var(--wow-gold-dark)); + border-radius: 50%; + box-shadow: 0 0 8px rgba(255, 209, 0, 0.6); +} + +.wow-panel-content { + padding: 1.5rem; +} + +.wow-section-title { + color: var(--wow-gold); + font-family: 'Uncial Antiqua', serif; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8); +} + +/* Form Elements */ +.wow-input-group { + display: flex; + flex-direction: column; +} + +.wow-label { + color: var(--wow-gold); + font-weight: 600; + margin-bottom: 0.5rem; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +.wow-input, +.wow-select { + background: var(--wow-bg-card); + border: 2px solid var(--wow-border); + border-radius: 4px; + padding: 0.75rem; + color: var(--wow-text-primary); + font-family: 'Cinzel', serif; + transition: all 0.3s ease; +} + +.wow-input:focus, +.wow-select:focus { + outline: none; + border-color: var(--wow-gold); + box-shadow: 0 0 10px rgba(255, 209, 0, 0.3); +} + +.wow-help-text { + color: var(--wow-text-muted); + font-size: 0.8rem; + margin-top: 0.25rem; + font-style: italic; +} + +/* Tabs */ +.wow-tab-container { + display: flex; + border-bottom: 2px solid var(--wow-border); + margin-bottom: 1rem; +} + +.wow-tab { + background: var(--wow-bg-card); + border: 2px solid var(--wow-border); + border-bottom: none; + padding: 0.75rem 1.5rem; + color: var(--wow-text-secondary); + font-family: 'Cinzel', serif; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + margin-right: 2px; +} + +.wow-tab:hover { + background: var(--wow-bg-panel); + color: var(--wow-gold); +} + +.wow-tab-active { + background: var(--wow-bg-panel); + color: var(--wow-gold); + border-color: var(--wow-border-gold); + position: relative; +} + +.wow-tab-active::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + right: 0; + height: 2px; + background: var(--wow-gold); +} + +/* Buttons */ +.wow-button { + background: linear-gradient(135deg, var(--wow-bg-card), var(--wow-bg-panel)); + border: 2px solid var(--wow-border); + color: var(--wow-text-primary); + padding: 0.75rem 2rem; + border-radius: 4px; + font-family: 'Cinzel', serif; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.wow-button::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 209, 0, 0.2), transparent); + transition: left 0.5s ease; +} + +.wow-button:hover::before { + left: 100%; +} + +.wow-button:hover { + border-color: var(--wow-gold); + box-shadow: 0 0 20px rgba(255, 209, 0, 0.3); + transform: translateY(-2px); +} + +.wow-button-primary { + background: linear-gradient(135deg, #2a4d6e, #1e3a52); + border-color: var(--wow-blue); + color: var(--wow-text-primary); +} + +.wow-button-primary:hover { + border-color: var(--wow-gold); + background: linear-gradient(135deg, #3a5d7e, #2e4a62); +} + +.wow-button-secondary { + background: linear-gradient(135deg, #4a2a6e, #3a1e52); + border-color: var(--wow-purple); +} + +.wow-button-secondary:hover { + border-color: var(--wow-gold); + background: linear-gradient(135deg, #5a3a7e, #4a2e62); +} + +.wow-button-small { + padding: 0.5rem 1rem; + font-size: 0.85rem; +} + +.wow-button-text { + position: relative; + z-index: 1; +} + +/* Search Results */ +.wow-search-results { + background: var(--wow-bg-card); + border: 2px solid var(--wow-border); + border-top: none; + max-height: 200px; + overflow-y: auto; + position: absolute; + width: 100%; + z-index: 10; + border-radius: 0 0 4px 4px; +} + +.wow-search-item { + padding: 0.75rem; + cursor: pointer; + border-bottom: 1px solid var(--wow-border); + display: flex; + align-items: center; + gap: 0.75rem; + transition: background 0.2s ease; +} + +.wow-search-item:hover { + background: var(--wow-bg-panel); +} + +.wow-search-item:last-child { + border-bottom: none; +} + +/* Item Cards */ +.wow-item-card { + background: var(--wow-bg-card); + border: 2px solid var(--wow-border); + border-radius: 8px; + padding: 1.5rem; + margin-bottom: 1rem; + position: relative; + overflow: hidden; +} + +.wow-item-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, transparent, var(--wow-gold), transparent); +} + +.wow-item-epic { + border-color: var(--wow-purple); +} + +.wow-item-legendary { + border-color: var(--wow-orange); +} + +.wow-item-comparison { + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 2rem; + align-items: center; +} + +.wow-item-before, +.wow-item-after { + text-align: center; +} + +.wow-item-arrow { + font-size: 2rem; + color: var(--wow-gold); + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8); +} + +.wow-item-image { + width: 64px; + height: 64px; + border: 2px solid var(--wow-border); + border-radius: 4px; + margin: 0 auto 1rem; + background: var(--wow-bg-panel); +} + +.wow-item-name { + font-weight: 700; + font-size: 1.1rem; + margin-bottom: 0.5rem; +} + +.wow-item-stats { + font-size: 0.9rem; + line-height: 1.5; +} + +.wow-stat-line { + margin-bottom: 0.25rem; +} + +.wow-stat-improved { + color: var(--wow-green); +} + +.wow-stat-decreased { + color: var(--wow-red); +} + +/* Quality Colors */ +.quality-poor { color: #9d9d9d; } +.quality-common { color: #ffffff; } +.quality-uncommon { color: #1eff00; } +.quality-rare { color: #0070dd; } +.quality-epic { color: var(--wow-purple); } +.quality-legendary { color: var(--wow-orange); } + +/* Loading Animation */ +.wow-loading { + text-align: center; + padding: 3rem; +} + +.wow-spinner { + width: 60px; + height: 60px; + border: 4px solid var(--wow-border); + border-top: 4px solid var(--wow-gold); + border-radius: 50%; + animation: wow-spin 1s linear infinite; + margin: 0 auto 1rem; +} + +.wow-loading-text { + color: var(--wow-gold); + font-size: 1.1rem; + font-weight: 600; + animation: wow-pulse 1.5s ease-in-out infinite; +} + +@keyframes wow-spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes wow-pulse { + 0%, 100% { opacity: 0.7; } + 50% { opacity: 1; } +} + +/* Footer - always at bottom */ +.wow-footer { + background: var(--wow-bg-panel); + border-top: 2px solid var(--wow-border); + margin-top: auto; /* This pushes footer to bottom */ + flex-shrink: 0; /* Prevent footer from shrinking */ + position: relative; + z-index: 10; +} + +.wow-footer::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent); + box-shadow: 0 0 10px rgba(79, 195, 247, 0.4); +} + +.wow-footer-text { + color: var(--wow-text-muted); + font-size: 0.9rem; +} + +/* Frost/ice particle effects */ +.wow-bg::after { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + radial-gradient(2px 2px at 20px 30px, rgba(79, 195, 247, 0.4), transparent), + radial-gradient(1px 1px at 40px 70px, rgba(129, 212, 250, 0.3), transparent), + radial-gradient(3px 3px at 90px 40px, rgba(179, 229, 252, 0.3), transparent), + radial-gradient(1px 1px at 130px 80px, rgba(79, 195, 247, 0.4), transparent), + radial-gradient(2px 2px at 160px 30px, rgba(129, 212, 250, 0.25), transparent), + radial-gradient(1px 1px at 180px 60px, rgba(26, 35, 126, 0.3), transparent); + background-repeat: repeat; + background-size: 250px 120px; + animation: wow-frost-particles 25s linear infinite; + pointer-events: none; + z-index: -1; + opacity: 0.7; +} + +@keyframes wow-frost-particles { + 0% { transform: translateY(0) translateX(0) rotate(0deg); } + 100% { transform: translateY(-120px) translateX(30px) rotate(180deg); } +} + +/* Enhanced section titles with ice decorative elements */ +.wow-section-title::before { + content: '❖'; + margin-right: 0.5rem; + color: var(--wotlk-ice-light); + text-shadow: 0 0 15px rgba(129, 212, 250, 0.9); + animation: wow-ice-gem-glow 3s ease-in-out infinite alternate; +} + +.wow-section-title::after { + content: '❖'; + margin-left: 0.5rem; + color: var(--wotlk-ice-light); + text-shadow: 0 0 15px rgba(129, 212, 250, 0.9); + animation: wow-ice-gem-glow 3s ease-in-out infinite alternate-reverse; +} + +@keyframes wow-ice-gem-glow { + 0% { opacity: 0.6; transform: scale(1) rotate(0deg); } + 100% { opacity: 1; transform: scale(1.3) rotate(45deg); } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .wow-item-comparison { + grid-template-columns: 1fr; + gap: 1rem; + } + + .wow-item-arrow { + transform: rotate(90deg); + font-size: 1.5rem; + } + + .wow-tab-container { + flex-direction: column; + } + + .wow-tab { + margin-right: 0; + margin-bottom: 2px; + } + + .wow-title::before, + .wow-title::after { + display: none; /* Hide sword decorations on mobile */ + } +} \ No newline at end of file diff --git a/wow-item-scaler-web/images/README.md b/wow-item-scaler-web/images/README.md new file mode 100644 index 0000000..8acd265 --- /dev/null +++ b/wow-item-scaler-web/images/README.md @@ -0,0 +1,34 @@ +# Background Images for WoW Item Scaler + +This directory should contain WoW frost/ice themed background images to enhance the visual appearance of the application. + +## Required Images: + +1. **wotlk-bg1.jpg** - Wrath of the Lich King landscape (recommended: 1920x1080) +2. **wotlk-bg2.jpg** - Northrend/frost themed scenery (recommended: 1920x1080) +3. **icecrown-bg.jpg** - Icecrown Citadel or similar WotLK location (recommended: 1920x1080) + +## Recommended Sources: + +- Official Blizzard press kit images +- WoW community screenshots (with proper licensing) +- Fan art (with permission) +- Generated AI art in WoW style + +## Image Specifications: + +- **Format**: JPG or PNG +- **Size**: 1920x1080 recommended for best quality +- **Theme**: Frost, ice, snow, Wrath of the Lich King landscapes +- **Colors**: Cool blues, whites, grays to match Araxia theme + +## CSS Integration: + +The images are automatically integrated via `css/wow-theme.css` with: +- Araxia color overlay blending +- Ice particle effects on top +- Multiple layer composition for depth + +## Fallback: + +If images are not present, the CSS will fall back to pure CSS-generated frost patterns and gradients that simulate the WoW aesthetic. \ No newline at end of file diff --git a/wow-item-scaler-web/index.html b/wow-item-scaler-web/index.html new file mode 100644 index 0000000..1d64e55 --- /dev/null +++ b/wow-item-scaler-web/index.html @@ -0,0 +1,143 @@ + + + + + + WoW Item Scaler + + + + + + + +
+
+
+
+
+ +
+

Item Scaler

+

Wrath of the Lich King

+

Araxia Online

+
+
+
+
+ +
+ +
+
+

Scaling Parameters

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+

Item Selection

+
+
+
+ +
+ + +
+ + +
+
+ + +

Enter item entry IDs separated by commas

+
+
+ + + + + +
+ + +
+
+
+
+ + + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/wow-item-scaler-web/js/item-scaler.js b/wow-item-scaler-web/js/item-scaler.js new file mode 100644 index 0000000..6ebb7aa --- /dev/null +++ b/wow-item-scaler-web/js/item-scaler.js @@ -0,0 +1,342 @@ +// Main Item Scaler Application Logic +class ItemScaler { + constructor() { + this.backendUrl = 'http://localhost:8080'; // Update this to match your backend + this.searchTimeout = null; + this.init(); + } + + init() { + this.setupEventListeners(); + this.loadDefaultValues(); + } + + setupEventListeners() { + // Scale button + document.getElementById('scaleButton').addEventListener('click', () => { + this.scaleItems(); + }); + + // Clear button + document.getElementById('clearButton').addEventListener('click', () => { + WowTheme.clearResults(); + }); + + // Export SQL button + document.getElementById('exportSqlButton').addEventListener('click', () => { + this.exportSQL(); + }); + + // Copy results button + document.getElementById('copyResultsButton').addEventListener('click', () => { + this.copyResults(); + }); + + // Search input + document.getElementById('itemSearch').addEventListener('input', (e) => { + this.handleSearchInput(e.target.value); + }); + + // Hide search results when clicking outside + document.addEventListener('click', (e) => { + const searchContainer = document.getElementById('searchPanel'); + if (!searchContainer.contains(e.target)) { + document.getElementById('searchResults').classList.add('hidden'); + } + }); + } + + loadDefaultValues() { + // Load any saved preferences from localStorage + const savedValues = this.getSavedValues(); + if (savedValues) { + document.getElementById('itemLevel').value = savedValues.itemLevel || 325; + document.getElementById('phase').value = savedValues.phase || 0; + document.getElementById('quality').value = savedValues.quality || 4; + document.getElementById('catchup').value = savedValues.catchup || 1.0; + } + } + + getSavedValues() { + try { + return JSON.parse(localStorage.getItem('wowItemScalerSettings')); + } catch { + return null; + } + } + + saveValues() { + const values = { + itemLevel: parseInt(document.getElementById('itemLevel').value), + phase: parseInt(document.getElementById('phase').value), + quality: parseInt(document.getElementById('quality').value), + catchup: parseFloat(document.getElementById('catchup').value) + }; + localStorage.setItem('wowItemScalerSettings', JSON.stringify(values)); + } + + async scaleItems() { + try { + WowTheme.showLoading(); + this.saveValues(); + + const scalingParams = this.getScalingParameters(); + const itemEntries = this.getItemEntries(); + + if (itemEntries.length === 0) { + WowTheme.hideLoading(); + WowTheme.showNotification('Please provide item entries or search for items', 'error'); + return; + } + + const results = await this.callBackendAPI(scalingParams, itemEntries); + this.displayResults(results); + + } catch (error) { + console.error('Error scaling items:', error); + WowTheme.hideLoading(); + WowTheme.showNotification(`Error: ${error.message}`, 'error'); + } + } + + getScalingParameters() { + return { + itemLevel: parseInt(document.getElementById('itemLevel').value), + phase: parseInt(document.getElementById('phase').value), + quality: parseInt(document.getElementById('quality').value), + catchup: parseFloat(document.getElementById('catchup').value) + }; + } + + getItemEntries() { + const entriesText = document.getElementById('entryIds').value.trim(); + if (!entriesText) { + return []; + } + + return entriesText + .split(',') + .map(entry => entry.trim()) + .filter(entry => entry && !isNaN(entry)) + .map(entry => parseInt(entry)); + } + + async callBackendAPI(scalingParams, itemEntries) { + const requestData = { + ...scalingParams, + entries: itemEntries + }; + + console.log('Sending request:', requestData); + + const response = await fetch(`${this.backendUrl}/api/scale-items`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestData) + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`); + } + + return await response.json(); + } + + displayResults(results) { + const container = document.getElementById('resultsContainer'); + container.innerHTML = ''; + + if (!results || !results.items || results.items.length === 0) { + container.innerHTML = ` +
+

No items were successfully scaled.

+

Check the console for detailed error information.

+
+ `; + WowTheme.showResults(); + return; + } + + // Display summary + const summary = ` +
+
+
+

Scaling Summary

+
+
+
Total Items
+
${results.summary?.total || results.items.length}
+
+
+
Successful
+
${results.summary?.successful || results.items.length}
+
+
+
Failed
+
${results.summary?.failed || 0}
+
+
+
Success Rate
+
${results.summary?.successRate || '100.0'}%
+
+
+
+
+
+ `; + + container.innerHTML = summary; + + // Display individual item results + results.items.forEach(result => { + if (result.success) { + const itemCard = WowTheme.createItemCard( + result.original, + result.scaled, + result.referenceItem + ); + container.innerHTML += itemCard; + } else { + // Display error for failed item + container.innerHTML += ` +
+
+

Failed to scale item

+

Entry ID: ${result.entryId}

+
+ ${result.errors ? result.errors.map(error => `

${error}

`).join('') : 'Unknown error occurred'} +
+
+
+ `; + } + }); + + WowTheme.showResults(); + this.reinitializeTooltips(); + } + + reinitializeTooltips() { + // Reinitialize Wowhead tooltips for newly added content + if (window.$WowheadPower) { + window.$WowheadPower.refreshLinks(); + } + } + + async handleSearchInput(query) { + clearTimeout(this.searchTimeout); + + if (!query || query.length < 2) { + document.getElementById('searchResults').classList.add('hidden'); + return; + } + + this.searchTimeout = setTimeout(async () => { + try { + const results = await this.searchItems(query); + this.displaySearchResults(results); + } catch (error) { + console.error('Search error:', error); + WowTheme.showNotification('Search failed', 'error'); + } + }, 300); + } + + async searchItems(query) { + const response = await fetch(`${this.backendUrl}/api/search-items?q=${encodeURIComponent(query)}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + } + }); + + if (!response.ok) { + throw new Error(`Search failed: ${response.status}`); + } + + return await response.json(); + } + + displaySearchResults(results) { + const container = document.getElementById('searchResults'); + + if (!results || results.length === 0) { + container.innerHTML = ` +
+
No items found
+
+ `; + } else { + container.innerHTML = results + .slice(0, 10) // Limit to 10 results + .map(item => WowTheme.createSearchResultItem(item)) + .join(''); + } + + container.classList.remove('hidden'); + } + + async exportSQL() { + try { + const scalingParams = this.getScalingParameters(); + const itemEntries = this.getItemEntries(); + + if (itemEntries.length === 0) { + WowTheme.showNotification('No items to export', 'error'); + return; + } + + const response = await fetch(`${this.backendUrl}/api/export-sql`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...scalingParams, + entries: itemEntries + }) + }); + + if (!response.ok) { + throw new Error(`Export failed: ${response.status}`); + } + + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `scaled_items_${Date.now()}.sql`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + + WowTheme.showNotification('SQL exported successfully!', 'success'); + + } catch (error) { + console.error('Export error:', error); + WowTheme.showNotification('Export failed', 'error'); + } + } + + async copyResults() { + try { + const container = document.getElementById('resultsContainer'); + const textContent = container.innerText; + await navigator.clipboard.writeText(textContent); + WowTheme.showNotification('Results copied to clipboard!', 'success'); + } catch (error) { + console.error('Copy error:', error); + WowTheme.showNotification('Failed to copy results', 'error'); + } + } +} + +// Initialize the application +document.addEventListener('DOMContentLoaded', () => { + new ItemScaler(); +}); \ No newline at end of file diff --git a/wow-item-scaler-web/js/wow-theme.js b/wow-item-scaler-web/js/wow-theme.js new file mode 100644 index 0000000..798091e --- /dev/null +++ b/wow-item-scaler-web/js/wow-theme.js @@ -0,0 +1,250 @@ +// WoW Theme JavaScript utilities +class WowTheme { + static init() { + this.setupTabNavigation(); + this.setupTooltips(); + this.initializeWowheadPower(); + } + + static setupTabNavigation() { + const tabButtons = document.querySelectorAll('.wow-tab'); + const tabPanels = document.querySelectorAll('.wow-tab-panel'); + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + const isEntries = button.id === 'tabEntries'; + + // Update tab states + tabButtons.forEach(btn => btn.classList.remove('wow-tab-active')); + button.classList.add('wow-tab-active'); + + // Show/hide panels + document.getElementById('entriesPanel').classList.toggle('hidden', !isEntries); + document.getElementById('searchPanel').classList.toggle('hidden', isEntries); + }); + }); + } + + static setupTooltips() { + // Initialize Wowhead tooltips when they become available + if (window.$WowheadPower) { + window.$WowheadPower.init(); + } + } + + static initializeWowheadPower() { + // Configure Wowhead Power settings + if (window.$WH) { + window.$WH.Tooltip.config = { + colorLinks: true, + iconizeLinks: true, + renameLinks: true + }; + } + } + + static showLoading() { + document.getElementById('loadingSpinner').classList.remove('hidden'); + document.getElementById('resultsPanel').classList.add('hidden'); + } + + static hideLoading() { + document.getElementById('loadingSpinner').classList.add('hidden'); + } + + static showResults() { + document.getElementById('resultsPanel').classList.remove('hidden'); + this.hideLoading(); + } + + static clearResults() { + const container = document.getElementById('resultsContainer'); + container.innerHTML = ''; + document.getElementById('resultsPanel').classList.add('hidden'); + } + + static getQualityClass(quality) { + const qualityMap = { + 0: 'quality-poor', + 1: 'quality-common', + 2: 'quality-uncommon', + 3: 'quality-rare', + 4: 'quality-epic', + 5: 'quality-legendary' + }; + return qualityMap[quality] || 'quality-common'; + } + + static getQualityName(quality) { + const qualityNames = { + 0: 'Poor', + 1: 'Common', + 2: 'Uncommon', + 3: 'Rare', + 4: 'Epic', + 5: 'Legendary' + }; + return qualityNames[quality] || 'Common'; + } + + static createItemCard(original, scaled, referenceItem = null) { + const qualityClass = this.getQualityClass(scaled.quality); + const cardClass = scaled.quality === 4 ? 'wow-item-epic' : 'wow-item-legendary'; + + return ` +
+
+
+

+ ${original.name} +

+
+ ${original.name} +
+
+
Item Level: ${original.itemLevel || 'Unknown'}
+
Quality: ${this.getQualityName(original.quality)}
+ ${this.formatStats(original.stats)} +
+
+ +
+ +
+

+ ${scaled.name} +

+
+ ${scaled.name} +
+
+
Item Level: ${scaled.itemLevel}
+
Quality: ${this.getQualityName(scaled.quality)}
+ ${this.formatStats(scaled.stats, original.stats)} +
+
+
+ + ${referenceItem ? ` +
+

Reference Item Used:

+

${referenceItem.name}

+
+ ` : ''} + +
+ Entry ID: ${scaled.entry} + +
+
+ `; + } + + static formatStats(stats, originalStats = null) { + if (!stats || Object.keys(stats).length === 0) { + return '
No stats available
'; + } + + return Object.entries(stats).map(([statName, value]) => { + let className = 'wow-stat-line'; + let prefix = ''; + + if (originalStats && originalStats[statName] !== undefined) { + const originalValue = originalStats[statName]; + if (value > originalValue) { + className += ' wow-stat-improved'; + prefix = '+'; + } else if (value < originalValue) { + className += ' wow-stat-decreased'; + } + } + + return `
${prefix}${value} ${statName}
`; + }).join(''); + } + + static getItemIcon(entryId) { + // This would ideally come from your backend or a lookup table + // For now, return a default or use the entry ID as a fallback + return `inv_misc_questionmark`; + } + + static async copyToClipboard(text) { + try { + await navigator.clipboard.writeText(text); + this.showNotification('Copied to clipboard!', 'success'); + } catch (err) { + console.error('Failed to copy: ', err); + this.showNotification('Failed to copy', 'error'); + } + } + + static showNotification(message, type = 'info') { + const notification = document.createElement('div'); + notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 transition-all duration-300 ${ + type === 'success' ? 'bg-green-600' : + type === 'error' ? 'bg-red-600' : + 'bg-blue-600' + } text-white`; + notification.textContent = message; + + document.body.appendChild(notification); + + // Animate in + setTimeout(() => notification.classList.add('opacity-100'), 10); + + // Remove after 3 seconds + setTimeout(() => { + notification.classList.add('opacity-0'); + setTimeout(() => notification.remove(), 300); + }, 3000); + } + + static createSearchResultItem(item) { + return ` +
+
+ ${item.name} +
+
+
${item.name}
+
Entry ID: ${item.entry}
+
+
+ `; + } + + static selectSearchItem(entryId, itemName) { + const entriesInput = document.getElementById('entryIds'); + const currentEntries = entriesInput.value.trim(); + + if (currentEntries) { + entriesInput.value = `${currentEntries}, ${entryId}`; + } else { + entriesInput.value = entryId.toString(); + } + + // Clear search + document.getElementById('itemSearch').value = ''; + document.getElementById('searchResults').classList.add('hidden'); + + // Switch to entries tab + document.getElementById('tabEntries').click(); + + this.showNotification(`Added ${itemName} to selection`, 'success'); + } +} + +// Initialize theme when DOM is loaded +document.addEventListener('DOMContentLoaded', () => { + WowTheme.init(); +}); \ No newline at end of file