diff --git a/CLAUDE.md b/CLAUDE.md
index a34fee8..ca1ca07 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -660,7 +660,7 @@ Six specialized checks for Amazon marketing asset compliance. Originally based o
| `amazon_typography` | Ember Modern Standard Display font, size ratios, leading/tracking, ligatures, **headline-to-date spacing** (cramped = fail) |
| `amazon_headline_layout` | Left-aligned, largest element, natural line splits. Prepositions at line end ("di", "of") are acceptable. One word per line OK in tall formats |
| `amazon_margins` | Visual margin assessment (not pixel-level). Headline breathing room from edges. **Left alignment consistency** between headline, date, and logo/branding |
-| `amazon_box_placement` | Box position (right for landscape, centre OK for portrait). **Tape/flaps** = branded coloured strips on box edges — must not be cropped by asset edge |
+| `amazon_element_placement` | Element placement (box, bag, logo). Box position (right for landscape, centre OK for portrait). **Tape/flaps** = branded coloured strips on box edges — must not be cropped by asset edge |
#### Amazon Prompt Tuning History (2026-03-30)
Prompts were refined based on testing with 9 assets (5 correct, 4 incorrect with known single defects):
@@ -677,7 +677,7 @@ Prompts were refined based on testing with 9 assets (5 correct, 4 incorrect with
| IE_BrightSide_576x1152 copy | Headline too large/close to left edge, not aligned with logo | Headline scaled down for proper margins and left-alignment |
**Key prompt refinements made**:
-1. **Box Placement**: Tape described as branded coloured strips (not plain packing tape). Format-aware positioning (centre OK for portrait). Only fail tape if cropped by asset edge.
+1. **Element Placement**: Tape described as branded coloured strips (not plain packing tape). Format-aware positioning (centre OK for portrait). Only fail tape if cropped by asset edge.
2. **Required Elements**: Subhead demoted to optional. OOH/DOOH formats don't need separate subhead.
3. **Logo Country**: Country/language match is the primary scoring factor. Logo colour is secondary.
4. **Margins**: Visual assessment approach (not pixel calculations). Added left-alignment consistency check between headline and logo.
diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md
index 92d61ae..2d99873 100644
--- a/backend/CLAUDE.md
+++ b/backend/CLAUDE.md
@@ -610,7 +610,7 @@ Six specialized checks for Amazon Sale Day design compliance, with guidelines fr
| `amazon_typography` | Ember Modern Standard Display font, leading/tracking, size ratios (subhead 30-60%, date 20-45%), ligatures |
| `amazon_headline_layout` | Headline left-aligned, largest element, natural line splits |
| `amazon_margins` | 7% shortest side (10% wide, 20%/10% very wide+small formats) |
-| `amazon_box_placement` | Box on right side, cropping rules (tape NEVER cropped) |
+| `amazon_element_placement` | Element placement (box, bag, logo), positioning rules, cropping rules (tape NEVER cropped) |
### Client-Scoped Reporting Dashboard
Reporting has been moved from the Settings modal into a dedicated "Reporting" tab within each client's main view:
diff --git a/backend/profiles/amazon_static.json b/backend/profiles/amazon_static.json
index 45a62ca..46fd2d8 100644
--- a/backend/profiles/amazon_static.json
+++ b/backend/profiles/amazon_static.json
@@ -1,6 +1,6 @@
{
"name": "Amazon Static",
- "description": "Amazon ASD 2025 design guidelines QC profile for static marketing assets. Evaluates layout elements, typography, margins, box placement, and logo correctness per country.",
+ "description": "Amazon ASD 2025 design guidelines QC profile for static marketing assets. Evaluates layout elements, typography, margins, element placement, and logo correctness per country.",
"checks": {
"amazon_required_elements": {
"enabled": true,
@@ -32,11 +32,11 @@
"llm": "Gemini",
"description": "Validates Amazon margin rules: 7% of shortest side, 10% for wide banners, special rules for very wide and small formats"
},
- "amazon_box_placement": {
+ "amazon_element_placement": {
"enabled": true,
"weight": 1.67,
"llm": "Gemini",
- "description": "Checks box sits to right side, cropping rules (right crop OK but never tape, never bottom), and box overlap constraints"
+ "description": "Checks element placement (box, bag, logo), positioning rules, cropping rules (right crop OK but never tape, never bottom), and overlap constraints"
}
}
}
diff --git a/backend/visual_qc_apps/amazon_box_placement/app.py b/backend/visual_qc_apps/amazon_element_placement/app.py
similarity index 97%
rename from backend/visual_qc_apps/amazon_box_placement/app.py
rename to backend/visual_qc_apps/amazon_element_placement/app.py
index c4d2e73..0dfd30b 100644
--- a/backend/visual_qc_apps/amazon_box_placement/app.py
+++ b/backend/visual_qc_apps/amazon_element_placement/app.py
@@ -6,10 +6,10 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(
from visual_qc_apps.flask_app_template import FlaskAppTemplate
-class AmazonBoxPlacementApp(FlaskAppTemplate):
+class AmazonElementPlacementApp(FlaskAppTemplate):
"""
- Amazon ASD 2025 - Box Placement & Cropping Check
- Verifies box positioning (right side) and cropping rules.
+ Amazon ASD 2025 - Element Placement & Cropping Check
+ Verifies element positioning (box, bag, logo) and cropping rules.
"""
def __init__(self):
@@ -132,5 +132,5 @@ Format your response as JSON:
# Run the app if executed directly
if __name__ == "__main__":
- app_instance = AmazonBoxPlacementApp()
+ app_instance = AmazonElementPlacementApp()
app_instance.run()
diff --git a/web_ui.html b/web_ui.html
index b913540..0257319 100644
--- a/web_ui.html
+++ b/web_ui.html
@@ -4038,32 +4038,96 @@
}
}
+ let authCheckInterval = null; // Periodic auth check timer
+
async function checkAuthStatus() {
try {
showAuthLoading();
-
+
const response = await fetch(`${BASE_PATH}auth/status`);
const status = await response.json();
-
+
if (status.authenticated && status.user) {
currentUser = status.user;
isAuthenticated = true;
updateAuthUI();
+ // Start periodic auth check if not already running
+ startPeriodicAuthCheck();
} else {
isAuthenticated = false;
+ stopPeriodicAuthCheck();
showAuthRequired();
}
-
+
return isAuthenticated;
-
+
} catch (error) {
console.error('Auth status check failed:', error);
isAuthenticated = false;
+ stopPeriodicAuthCheck();
showAuthRequired();
return false;
}
}
+ // Silent auth check - runs periodically without showing loading indicators
+ async function silentAuthCheck() {
+ try {
+ const response = await fetch(`${BASE_PATH}auth/status`);
+ const status = await response.json();
+
+ if (!status.authenticated || !status.user) {
+ console.warn('Session expired - prompting re-authentication');
+ isAuthenticated = false;
+ currentUser = null;
+ stopPeriodicAuthCheck();
+ showSessionExpiredPrompt();
+ }
+ } catch (error) {
+ console.error('Silent auth check failed:', error);
+ // Don't immediately log out on network errors - wait for next check
+ }
+ }
+
+ function startPeriodicAuthCheck() {
+ if (authCheckInterval) return; // Already running
+ // Check every 5 minutes (300000ms)
+ authCheckInterval = setInterval(silentAuthCheck, 5 * 60 * 1000);
+ console.log('Periodic auth check started (every 5 minutes)');
+ }
+
+ function stopPeriodicAuthCheck() {
+ if (authCheckInterval) {
+ clearInterval(authCheckInterval);
+ authCheckInterval = null;
+ console.log('Periodic auth check stopped');
+ }
+ }
+
+ function showSessionExpiredPrompt() {
+ // Hide main app content
+ document.getElementById('mainApp').style.display = 'none';
+ document.getElementById('userInfo').style.display = 'none';
+
+ // Show auth required screen with expired message
+ const authRequired = document.getElementById('authRequired');
+ authRequired.style.display = 'block';
+
+ // Show a session expired message if not already present
+ let expiredMsg = document.getElementById('sessionExpiredMsg');
+ if (!expiredMsg) {
+ expiredMsg = document.createElement('div');
+ expiredMsg.id = 'sessionExpiredMsg';
+ expiredMsg.style.cssText = 'background: #fff3cd; color: #856404; border: 1px solid #ffc107; border-radius: 8px; padding: 16px; margin: 16px auto; max-width: 500px; text-align: center; font-size: 14px;';
+ expiredMsg.innerHTML = 'Session Expired
Your authentication session has timed out. Please sign in again to continue.';
+ authRequired.insertBefore(expiredMsg, authRequired.firstChild);
+ }
+ expiredMsg.style.display = 'block';
+
+ // Show login button
+ showLoginButton();
+ }
+
// UI update functions
function showAuthLoading() {
document.getElementById('authLoading').style.display = 'block';