# -*- coding: utf-8 -*-

from flask import Flask, request, redirect, send_from_directory, make_response
from werkzeug.utils import secure_filename
import os
import time
import sys
import shutil
import py_compile
import threading
import html as html_lib
import glob
import zipfile
import subprocess

app = Flask(__name__)

BASE_DIR = r"C:\TripServer"

FOLDERS = {
    "JAPAN": os.path.join(BASE_DIR, "JAPAN"),
    "USA": os.path.join(BASE_DIR, "USA"),
    "Moneyapp": os.path.join(BASE_DIR, "Moneyapp"),
    "MoneyHome": os.path.join(BASE_DIR, "MoneyHome"),
}

APP_FOLDERS = {"JAPAN", "USA", "Moneyapp", "MoneyHome"}

JAPAN_PRESENTATION_FILES = {
    "flight_intro": "japan-flight-intro.html",
    "japan_experience": "japan-experience.html",
}

USA_PRESENTATION_FILES = {
    "usa_presentation": "usa2027-presentation.html",
}

USA_MUSIC_FILES = {
    "usa_music": os.path.join("media", "usa-presentation", "music", "usa-roadtrip-theme.mp3"),
    "usa_music_part2": os.path.join("media", "usa-presentation", "music", "usa-part2-theme.mp3"),
}

USA_IMAGE_KEYS = ({"intro", "budget", "roadtrip-map", "summary", "extra-big-sur", "extra-diner", "extra-death-valley", "extra-mono-lake"} | {f"day-{i:02d}" for i in range(1, 37)})
USA_IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp"}

ADMIN_SERVER_PATH = os.path.abspath(__file__)
ADMIN_BACKUP_DIR = os.path.join(BASE_DIR, "backups", "admin_server")
ADMIN_UPLOAD_TMP_DIR = os.path.join(BASE_DIR, "tmp", "admin_server_uploads")
ADMIN_MAX_UPLOAD_BYTES = 2 * 1024 * 1024
ADMIN_RESTART_CMD_PATH = os.path.join(BASE_DIR, "restart_admin_server.cmd")
ADMIN_RESTART_LOG_PATH = os.path.join(BASE_DIR, "admin_restart.log")


def save_usa_presentation_image(image_key, uploaded_file):
    if image_key not in USA_IMAGE_KEYS:
        return None

    image_dir = os.path.join(FOLDERS["USA"], "media", "usa-presentation", "images")
    os.makedirs(image_dir, exist_ok=True)

    # Keep the presentation predictable: every single-card upload is normalized to .jpg.
    # This is especially important for roadtrip-map, because the presentation expects:
    # media/usa-presentation/images/roadtrip-map.jpg
    target_ext = ".jpg"

    for old_ext in USA_IMAGE_EXTENSIONS:
        old_path = os.path.join(image_dir, image_key + old_ext)
        if os.path.exists(old_path):
            try:
                os.remove(old_path)
            except Exception:
                pass

    filename = image_key + target_ext
    target_path = os.path.join(image_dir, filename)

    # Prefer real JPEG conversion when Pillow exists; if not installed, save raw bytes with .jpg.
    try:
        from PIL import Image
        uploaded_file.stream.seek(0)
        img = Image.open(uploaded_file.stream)
        if img.mode not in ("RGB", "L"):
            img = img.convert("RGB")
        elif img.mode == "L":
            img = img.convert("RGB")
        img.save(target_path, "JPEG", quality=90, optimize=True)
    except Exception:
        try:
            uploaded_file.stream.seek(0)
        except Exception:
            pass
        uploaded_file.save(target_path)

    return os.path.join("media", "usa-presentation", "images", filename)


def save_usa_presentation_images_zip(uploaded_file):
    """Upload a ZIP of presentation images from the phone/admin panel.
    Only known USA presentation image filenames are accepted.
    Files may be at ZIP root or under an images/ folder.
    """
    image_dir = os.path.join(FOLDERS["USA"], "media", "usa-presentation", "images")
    os.makedirs(image_dir, exist_ok=True)

    stamp = time.strftime("%Y%m%d_%H%M%S")
    tmp_zip = os.path.join(ADMIN_UPLOAD_TMP_DIR, f"usa_images_{stamp}.zip")
    os.makedirs(ADMIN_UPLOAD_TMP_DIR, exist_ok=True)
    uploaded_file.save(tmp_zip)

    imported = []
    skipped = []
    try:
        with zipfile.ZipFile(tmp_zip, "r") as zf:
            for info in zf.infolist():
                if info.is_dir():
                    continue
                raw_name = info.filename.replace("\\", "/")
                base = os.path.basename(raw_name)
                if not base or base.startswith(".") or raw_name.startswith("__MACOSX/"):
                    continue
                stem, ext = os.path.splitext(base)
                stem = stem.strip()
                ext = ext.lower()
                if ext not in USA_IMAGE_EXTENSIONS:
                    skipped.append(base)
                    continue
                if stem not in USA_IMAGE_KEYS:
                    skipped.append(base)
                    continue

                for old_ext in USA_IMAGE_EXTENSIONS:
                    old_path = os.path.join(image_dir, stem + old_ext)
                    if os.path.exists(old_path):
                        try:
                            os.remove(old_path)
                        except Exception:
                            pass

                target = os.path.join(image_dir, stem + ".jpg")
                try:
                    from PIL import Image
                    with zf.open(info, "r") as source:
                        img = Image.open(source)
                        if img.mode not in ("RGB", "L"):
                            img = img.convert("RGB")
                        elif img.mode == "L":
                            img = img.convert("RGB")
                        img.save(target, "JPEG", quality=90, optimize=True)
                except Exception:
                    with zf.open(info, "r") as source, open(target, "wb") as dest:
                        shutil.copyfileobj(source, dest)
                imported.append(stem + ".jpg")
    finally:
        try:
            os.remove(tmp_zip)
        except Exception:
            pass

    return imported, skipped

for folder_path in FOLDERS.values():
    os.makedirs(folder_path, exist_ok=True)

os.makedirs(os.path.join(FOLDERS["USA"], "media", "usa-presentation", "music"), exist_ok=True)
os.makedirs(os.path.join(FOLDERS["USA"], "media", "usa-presentation", "images"), exist_ok=True)
os.makedirs(ADMIN_BACKUP_DIR, exist_ok=True)
os.makedirs(ADMIN_UPLOAD_TMP_DIR, exist_ok=True)


def no_cache_response(response):
    response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "0"
    return response


def find_html_file(folder_path):
    index_path = os.path.join(folder_path, "index.html")
    if os.path.exists(index_path):
        return "index.html"

    html_files = [f for f in os.listdir(folder_path) if f.lower().endswith((".html", ".htm"))]
    if html_files:
        return html_files[0]
    return None


def clean_wrong_main_files(folder_path):
    wrong_names = {"html", "htm", "index", "index.htm", "download", "unknown"}
    for name in os.listdir(folder_path):
        full_path = os.path.join(folder_path, name)
        if not os.path.isfile(full_path):
            continue
        if name.lower().strip() in wrong_names:
            try:
                os.remove(full_path)
            except Exception:
                pass


def save_uploaded_file(folder, uploaded_file):
    folder_path = FOLDERS[folder]
    os.makedirs(folder_path, exist_ok=True)

    original_filename = uploaded_file.filename or ""
    safe_name = secure_filename(original_filename)

    if folder in APP_FOLDERS:
        clean_wrong_main_files(folder_path)
        filename = "index.html"
    else:
        filename = safe_name or f"upload_{int(time.time())}"

    uploaded_file.save(os.path.join(folder_path, filename))
    return filename


def save_japan_presentation(presentation_key, uploaded_file):
    if presentation_key not in JAPAN_PRESENTATION_FILES:
        return None
    japan_folder = FOLDERS["JAPAN"]
    os.makedirs(japan_folder, exist_ok=True)
    filename = JAPAN_PRESENTATION_FILES[presentation_key]
    uploaded_file.save(os.path.join(japan_folder, filename))
    return filename


def save_usa_presentation(presentation_key, uploaded_file):
    if presentation_key not in USA_PRESENTATION_FILES:
        return None
    usa_folder = FOLDERS["USA"]
    os.makedirs(usa_folder, exist_ok=True)
    filename = USA_PRESENTATION_FILES[presentation_key]
    uploaded_file.save(os.path.join(usa_folder, filename))
    return filename


def save_usa_music(music_key, uploaded_file):
    if music_key not in USA_MUSIC_FILES:
        return None
    usa_folder = FOLDERS["USA"]
    rel_path = USA_MUSIC_FILES[music_key]
    full_path = os.path.join(usa_folder, rel_path)
    os.makedirs(os.path.dirname(full_path), exist_ok=True)
    uploaded_file.save(full_path)
    return rel_path


def validate_admin_server_upload(tmp_path):
    size = os.path.getsize(tmp_path)
    if size <= 0:
        return False, "הקובץ ריק."
    if size > ADMIN_MAX_UPLOAD_BYTES:
        return False, f"הקובץ גדול מדי: {size:,} bytes. המגבלה היא {ADMIN_MAX_UPLOAD_BYTES:,} bytes."

    try:
        py_compile.compile(tmp_path, doraise=True)
    except py_compile.PyCompileError as exc:
        return False, "בדיקת קומפילציה נכשלה:\n" + str(exc)
    except Exception as exc:
        return False, "שגיאה בבדיקת הקובץ:\n" + repr(exc)

    try:
        with open(tmp_path, "r", encoding="utf-8", errors="ignore") as fh:
            content = fh.read(400000)
    except Exception as exc:
        return False, "לא ניתן לקרוא את הקובץ:\n" + repr(exc)

    required_tokens = ["Flask", "@app.route", "app.run"]
    missing = [token for token in required_tokens if token not in content]
    if missing:
        return False, "הקובץ עבר קומפילציה, אבל לא נראה כמו admin_server.py של TripServer. חסרים סימנים: " + ", ".join(missing)

    return True, "OK"


def admin_status_page(title, message, ok=True, details=""):
    color = "#16a34a" if ok else "#dc2626"
    bg = "#052e1b" if ok else "#3f1d1d"
    safe_title = html_lib.escape(title)
    safe_message = html_lib.escape(message).replace("\n", "<br>")
    safe_details = html_lib.escape(details).replace("\n", "<br>")
    details_html = f"<pre>{safe_details}</pre>" if details else ""
    return f"""
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{safe_title}</title>
<style>
body{{margin:0;font-family:Arial;background:#0f172a;color:white;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}}
.card{{width:min(780px,94vw);background:#1e293b;border-radius:24px;padding:26px;box-shadow:0 18px 70px rgba(0,0,0,.35)}}
.badge{{display:inline-block;background:{bg};border:1px solid {color};color:white;border-radius:999px;padding:8px 14px;font-weight:bold;margin-bottom:12px}}
h1{{margin:0 0 12px;font-size:30px}}
p{{line-height:1.7;color:#e2e8f0}}
pre{{white-space:pre-wrap;direction:ltr;text-align:left;background:#020617;color:#fde68a;border:1px solid #334155;border-radius:14px;padding:14px;overflow:auto;max-height:360px}}
a,button{{display:inline-block;margin:8px 0 0 8px;background:#2563eb;color:white;border:0;border-radius:12px;padding:12px 16px;text-decoration:none;font-weight:bold;cursor:pointer}}
button.restart{{background:#ea580c}}
form{{display:inline}}
</style>
</head>
<body>
<div class="card">
<div class="badge">TripServer Admin</div>
<h1>{safe_title}</h1>
<p>{safe_message}</p>
{details_html}
<a href="/admin">חזרה לפאנל</a>
<form action="/restart_admin_server" method="post" onsubmit="return confirm('לאתחל את הפאנל עכשיו?')"><button class="restart">אתחל פאנל עכשיו</button></form>
</div>
</body>
</html>
"""


def backup_current_admin_server(prefix="admin_server_backup"):
    os.makedirs(ADMIN_BACKUP_DIR, exist_ok=True)
    stamp = time.strftime("%Y%m%d_%H%M%S")
    backup_path = os.path.join(ADMIN_BACKUP_DIR, f"{prefix}_{stamp}.py")
    shutil.copy2(ADMIN_SERVER_PATH, backup_path)
    return backup_path


def latest_admin_backup():
    os.makedirs(ADMIN_BACKUP_DIR, exist_ok=True)
    backups = glob.glob(os.path.join(ADMIN_BACKUP_DIR, "admin_server_backup_*.py"))
    if not backups:
        return None
    return max(backups, key=os.path.getmtime)


def log_admin_restart(message):
    try:
        os.makedirs(BASE_DIR, exist_ok=True)
        with open(ADMIN_RESTART_LOG_PATH, "a", encoding="utf-8") as fh:
            fh.write(time.strftime("%Y-%m-%d %H:%M:%S") + " | " + str(message) + "\n")
    except Exception:
        pass


def restart_process_later():
    """Reliable self-restart for Windows/iPhone admin workflow."""
    time.sleep(0.8)
    script_path = os.path.abspath(ADMIN_SERVER_PATH)
    python_exe = sys.executable or "python"
    work_dir = os.path.dirname(script_path) or BASE_DIR
    try:
        log_admin_restart(f"restart requested | script={script_path} | python={python_exe} | argv={sys.argv}")
        if os.name == "nt":
            # Use a detached helper so the server can exit first and free port 8000.
            cmd = "\r\n".join([
                "@echo off",
                'cd /d "{}"'.format(work_dir),
                "timeout /t 2 /nobreak >nul",
                'start "TripServer Admin" /min "{}" "{}"'.format(python_exe, script_path),
                ""
            ])
            with open(ADMIN_RESTART_CMD_PATH, "w", encoding="utf-8") as fh:
                fh.write(cmd)
            subprocess.Popen(["cmd", "/c", ADMIN_RESTART_CMD_PATH], cwd=work_dir, close_fds=True)
            os._exit(0)
        else:
            subprocess.Popen([python_exe, script_path], cwd=work_dir, close_fds=True, start_new_session=True)
            os._exit(0)
    except Exception as exc:
        # Fallback: do not silently die before recording what happened.
        log_admin_restart("restart failed: " + repr(exc))
        try:
            os.execv(python_exe, [python_exe, script_path])
        except Exception as exc2:
            log_admin_restart("execv fallback failed: " + repr(exc2))
            os._exit(1)


def serve_app(folder):
    file_name = find_html_file(FOLDERS[folder])
    if not file_name:
        return f"No HTML file found in {folder} folder", 404
    response = make_response(send_from_directory(FOLDERS[folder], file_name))
    return no_cache_response(response)


@app.route("/")
def home():
    return """
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>מרכז ניהול</title>
<style>
body{margin:0;font-family:Arial;background:linear-gradient(135deg,#081229,#102447);color:white;min-height:100vh}
.hero{text-align:center;padding:50px 20px 20px}
.hero h1{font-size:42px;margin:0}
.hero p{color:#d8e6ff;font-size:18px}
.grid{width:min(1100px,92%);margin:40px auto;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:22px}
.card{text-decoration:none;color:white;border-radius:28px;padding:28px;min-height:180px;box-shadow:0 10px 30px rgba(0,0,0,.25);transition:.2s transform,.2s box-shadow}
.card:hover{transform:translateY(-4px);box-shadow:0 16px 40px rgba(0,0,0,.35)}
.card h2{font-size:30px;margin:0 0 12px}
.card p{line-height:1.6;margin:0}
.icon{font-size:52px;margin-bottom:15px}
.admin{background:linear-gradient(135deg,#0f9b0f,#38ef7d)}
.money{background:linear-gradient(135deg,#5f2c82,#49a09d)}
.homebudget{background:linear-gradient(135deg,#11998e,#38ef7d)}
.japan{background:linear-gradient(135deg,#ff416c,#ff4b2b)}
.usa{background:linear-gradient(135deg,#0052d4,#4364f7,#6fb1fc)}
.footer{text-align:center;color:#b8c8e6;padding:25px}

.image-guide{margin:12px 0 16px;background:#0f172a;border:1px solid #334155;border-radius:14px;padding:12px;color:#e2e8f0}
.image-guide summary{cursor:pointer;font-weight:bold;color:#bfdbfe;margin-bottom:10px}
.image-guide-table{width:100%;border-collapse:collapse;margin-top:10px;font-size:14px}
.image-guide-table th,.image-guide-table td{border-bottom:1px solid #334155;padding:9px;text-align:right;vertical-align:top}
.image-guide-table th{color:#93c5fd;background:#111827}
.image-guide-table td:first-child{white-space:nowrap;color:#fde68a}


.image-upload-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-top:16px}
.image-upload-card{margin:0}
.image-file-input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.image-upload-label{display:block;height:100%;cursor:pointer;background:#0f172a;border:1px solid #334155;border-radius:18px;padding:15px;transition:.18s transform,.18s border-color,.18s background;box-shadow:0 8px 22px rgba(0,0,0,.14)}
.image-upload-label:hover{transform:translateY(-2px);border-color:#60a5fa;background:#13213a}
.image-key{display:inline-flex;background:#1d4ed8;color:#dbeafe;border-radius:999px;padding:4px 9px;font-size:12px;font-weight:900;margin-bottom:8px}
.image-title{display:block;color:#fff;font-size:17px;font-weight:950;margin-bottom:7px;line-height:1.25}
.image-desc{display:block;color:#cbd5e1;font-size:14px;line-height:1.45;min-height:42px}
.upload-status{display:block;margin-top:12px;color:#fde68a;font-size:13px;font-weight:900;border-top:1px solid #334155;padding-top:10px}
@media(max-width:640px){.image-upload-grid{grid-template-columns:1fr}.image-title{font-size:16px}.image-desc{min-height:auto}}

</style>
</head>
<body>
<div class="hero">
<h1>מרכז ניהול</h1>
<p>מערכת מרכזית לניהול קבצים, תקציבים ולוחות זמנים</p>
</div>
<div class="grid">
<a class="card admin" href="/admin"><div class="icon">📁</div><h2>ניהול קבצים</h2><p>העלאת קבצים ישירות למחשב.</p></a>
<a class="card money" href="/Moneyapp/"><div class="icon">💰</div><h2>תקציב טיולים</h2><p>ניהול תקציב חופשות וסנכרון נתונים.</p></a>
<a class="card homebudget" href="/MoneyHome/"><div class="icon">🏠</div><h2>תקציב משפחתי</h2><p>ניהול תקציב חודשי, Limits, הוצאות בפועל וארכיון חודשים.</p></a>
<a class="card japan" href="/JAPAN/"><div class="icon">🇯🇵</div><h2>טיול יפן</h2><p>לוז, ניווטים ומסמכי הטיול.</p></a>
<a class="card usa" href="/USA/"><div class="icon">🇺🇸</div><h2>טיול ארה״ב</h2><p>טיול בר מצווה 2027.</p></a>
</div>
<div class="footer">TripServer</div>
</body>
</html>
"""


@app.route("/admin")
@app.route("/admin/")
def admin():
    return """
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ניהול קבצים</title>
<style>
body{margin:0;font-family:Arial;background:#0f172a;color:white}
.wrap{width:min(950px,92%);margin:auto;padding:40px 0}
h1{text-align:center}
.box{background:#1e293b;border-radius:22px;padding:24px;margin-bottom:24px}
.box h2{margin-top:0}
input[type=file]{width:100%;padding:14px;background:#0f172a;color:white;border-radius:12px;border:1px solid #334155}
button{margin-top:16px;border:0;background:#0b84f3;color:white;padding:14px 22px;border-radius:12px;font-size:16px;font-weight:bold;cursor:pointer}
.back{display:inline-block;margin-bottom:24px;color:#93c5fd;text-decoration:none}
.note{color:#cbd5e1;font-size:14px;line-height:1.6}
.preview-links{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
.preview-links a{display:inline-block;color:white;background:#334155;border:1px solid #475569;border-radius:12px;padding:10px 14px;text-decoration:none;font-weight:bold}
.warning{background:#3f1d1d;border:1px solid #7f1d1d;color:#fecaca;border-radius:14px;padding:12px;line-height:1.6;margin-bottom:14px}
.good{background:#143326;border:1px solid #1f7a54;color:#bbf7d0;border-radius:14px;padding:12px;line-height:1.6;margin-top:14px}
.danger{background:#dc2626!important}
.orange{background:#ea580c!important}
.admin-update-actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:12px}
.admin-update-actions form{margin:0}
.admin-mini{font-size:13px;color:#bfdbfe;line-height:1.6;background:#0f172a;border:1px solid #334155;border-radius:12px;padding:10px;margin-top:12px}

.image-guide{margin:12px 0 16px;background:#0f172a;border:1px solid #334155;border-radius:14px;padding:12px;color:#e2e8f0}
.image-guide summary{cursor:pointer;font-weight:bold;color:#bfdbfe;margin-bottom:10px}
.image-guide-table{width:100%;border-collapse:collapse;margin-top:10px;font-size:14px}
.image-guide-table th,.image-guide-table td{border-bottom:1px solid #334155;padding:9px;text-align:right;vertical-align:top}
.image-guide-table th{color:#93c5fd;background:#111827}
.image-guide-table td:first-child{white-space:nowrap;color:#fde68a}


.image-upload-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-top:16px}
.image-upload-card{margin:0}
.image-file-input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.image-upload-label{display:block;height:100%;cursor:pointer;background:#0f172a;border:1px solid #334155;border-radius:18px;padding:15px;transition:.18s transform,.18s border-color,.18s background;box-shadow:0 8px 22px rgba(0,0,0,.14)}
.image-upload-label:hover{transform:translateY(-2px);border-color:#60a5fa;background:#13213a}
.image-key{display:inline-flex;background:#1d4ed8;color:#dbeafe;border-radius:999px;padding:4px 9px;font-size:12px;font-weight:900;margin-bottom:8px}
.image-title{display:block;color:#fff;font-size:17px;font-weight:950;margin-bottom:7px;line-height:1.25}
.image-desc{display:block;color:#cbd5e1;font-size:14px;line-height:1.45;min-height:42px}
.upload-status{display:block;margin-top:12px;color:#fde68a;font-size:13px;font-weight:900;border-top:1px solid #334155;padding-top:10px}
@media(max-width:640px){.image-upload-grid{grid-template-columns:1fr}.image-title{font-size:16px}.image-desc{min-height:auto}}

</style>
</head>
<body>
<div class="wrap">
<a class="back" href="/">⬅ חזרה למסך הראשי</a>
<h1>ניהול והעלאת קבצים</h1>

<div class="box">
<h2>🇯🇵 טיול יפן</h2>
<form action="/upload/JAPAN" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה קובץ טיול יפן ראשי</button>
</form>
<p class="note">כל קובץ שתעלה כאן יישמר כ־index.html.</p>
</div>

<div class="box">
<h2>🎬 מצגות יפן</h2>
<div class="warning">כאן מעלים רק את קבצי המצגות, לא את קובץ הטיול הראשי. הקבצים יישמרו אוטומטית בשמות הנכונים.</div>
<h3>✈️ פתיח טיסה</h3>
<form action="/upload_presentation/flight_intro" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה פתיח טיסה</button>
</form>
<p class="note">יישמר אוטומטית בשם:<br><strong>japan-flight-intro.html</strong></p>
<h3>🎌 מצגת יפן</h3>
<form action="/upload_presentation/japan_experience" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה מצגת יפן</button>
</form>
<p class="note">יישמר אוטומטית בשם:<br><strong>japan-experience.html</strong></p>
<div class="preview-links">
<a href="/JAPAN/japan-flight-intro.html" target="_blank">בדיקת פתיח טיסה</a>
<a href="/JAPAN/japan-experience.html" target="_blank">בדיקת מצגת יפן</a>
<a href="/JAPAN/" target="_blank">פתיחת קובץ יפן</a>
</div>
</div>

<div class="box">
<h2>🇺🇸 טיול ארה״ב</h2>
<form action="/upload/USA" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה קובץ טיול ארה״ב</button>
</form>
<p class="note">כל קובץ שתעלה כאן יישמר כ־index.html.</p>
<div class="preview-links">
<a href="/USA/" target="_blank">פתיחת קובץ ארה״ב</a>
<a href="/USA/usa2027-presentation.html?from=admin" target="_blank">בדיקת מצגת ארה״ב</a>
</div>
</div>

<div class="box">
<h2>🎬 מצגת ארה״ב</h2>
<div class="warning">כאן מעלים רק את קובץ המצגת של ארה״ב. גם אם שם הקובץ אצלך שגוי – הוא יישמר אוטומטית בשם התקין, כדי שכפתור המצגת בקובץ הטיול יעבוד מיד.</div>
<form action="/upload_usa_presentation/usa_presentation" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה מצגת ארה״ב</button>
</form>
<p class="note">יישמר אוטומטית בשם:<br><strong>usa2027-presentation.html</strong></p>
<div class="preview-links">
<a href="/USA/usa2027-presentation.html?from=admin" target="_blank">בדיקת מצגת ארה״ב</a>
<a href="/USA/usa2027-presentation.html?from=admin" target="_blank">פתיחת מצגת במסך מלא</a>
<a href="/USA/" target="_blank">פתיחת קובץ ארה״ב</a>
</div>
</div>



<div class="box">
<h2>🖼️ תמונות מצגת ארה״ב</h2>
<div class="good">עודכן למצגת החדשה: הפאנל תומך עכשיו גם בשקף מפת הרוד־טריפ החדש — roadtrip-map — בנוסף לכל 36 הימים, פתיחה, תקציב, סיכום ושקפי הרוד־טריפ.</div>
<div class="warning">אפשר לעבוד מהטלפון בשתי דרכים: העלאת תמונה בודדת דרך הכרטיס המתאים, או העלאת ZIP אחד עם שמות הקבצים המדויקים. הפאנל ישמור אוטומטית בתיקיית התמונות הנכונה.</div>
<form action="/upload_usa_images_zip" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".zip,application/zip,application/x-zip-compressed">
<button>העלה ZIP תמונות מצגת ארה״ב</button>
</form>
<p class="note">ב־ZIP יש לשים קבצים בשם מדויק, למשל: <strong>roadtrip-map.jpg</strong>, <strong>extra-big-sur.jpg</strong>, <strong>extra-diner.jpg</strong>, <strong>extra-death-valley.jpg</strong>, <strong>extra-mono-lake.jpg</strong>, או <strong>day-15.jpg</strong> להחלפת תמונת יום 15.</p>
<div class="image-upload-grid">
<form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="intro">
<input class="image-file-input" id="image-file-intro" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-intro" class="image-upload-label">
<span class="image-key">intro</span>
<span class="image-title">פתיחה</span>
<span class="image-desc">אמריקה על פול ווליום / תמונת Road Trip כללית</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="budget">
<input class="image-file-input" id="image-file-budget" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-budget" class="image-upload-label">
<span class="image-key">budget</span>
<span class="image-title">תקציב</span>
<span class="image-desc">מפת טיול / כסף / תכנון כללי</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="roadtrip-map">
<input class="image-file-input" id="image-file-roadtrip-map" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה מפת רוד־טריפ...';this.closest('form').submit();}">
<label for="image-file-roadtrip-map" class="image-upload-label">
<span class="image-key">roadtrip-map</span>
<span class="image-title">🗺️ מפת רוד־טריפ</span>
<span class="image-desc">שקף 3 במצגת: מפת המסלול הכללית של מערב ארה״ב. נשמר אוטומטית בשם roadtrip-map.jpg</span>
<span class="upload-status">לחץ כאן ובחר את המפה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-01">
<input class="image-file-input" id="image-file-day-01" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-01" class="image-upload-label">
<span class="image-key">day-01</span>
<span class="image-title">יום 1</span>
<span class="image-desc">נחיתה בסן פרנסיסקו / Golden Gate / Skyline</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-02">
<input class="image-file-input" id="image-file-day-02" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-02" class="image-upload-label">
<span class="image-key">day-02</span>
<span class="image-title">יום 2</span>
<span class="image-desc">סן פרנסיסקו + Alcatraz / מפרץ, אלקטרז, גשר הזהב</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-03">
<input class="image-file-input" id="image-file-day-03" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-03" class="image-upload-label">
<span class="image-key">day-03</span>
<span class="image-title">יום 3</span>
<span class="image-desc">סן פרנסיסקו → מונטריי / כביש 1, 17-Mile Drive, חוף קליפורניה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-04">
<input class="image-file-input" id="image-file-day-04" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-04" class="image-upload-label">
<span class="image-key">day-04</span>
<span class="image-title">יום 4</span>
<span class="image-desc">Monterey Bay Whale Watch + Big Sur / לוויתן עוצר נשימה + נוף כביש 1</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="extra-big-sur">
<input class="image-file-input" id="image-file-extra-big-sur" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-extra-big-sur" class="image-upload-label">
<span class="image-key">extra-big-sur</span>
<span class="image-title">שקף חדש: Big Sur / Bixby Bridge</span>
<span class="image-desc">אחרי יום 4 — גשר Bixby / Big Sur / McWay Falls / כביש 1</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-05">
<input class="image-file-input" id="image-file-day-05" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-05" class="image-upload-label">
<span class="image-key">day-05</span>
<span class="image-title">יום 5</span>
<span class="image-desc">Pismo → לוס אנג׳לס / חוף, דקלים, כניסה ל־LA</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-06">
<input class="image-file-input" id="image-file-day-06" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-06" class="image-upload-label">
<span class="image-key">day-06</span>
<span class="image-title">יום 6</span>
<span class="image-desc">Warner Bros + Beverly Hills + Griffith / הוליווד, Griffith, LA בלילה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-07">
<input class="image-file-input" id="image-file-day-07" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-07" class="image-upload-label">
<span class="image-key">day-07</span>
<span class="image-title">יום 7</span>
<span class="image-desc">לוס אנג׳לס → Zion / מעבר למדבר וצוקים אדומים</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="extra-diner">
<input class="image-file-input" id="image-file-extra-diner" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-extra-diner" class="image-upload-label">
<span class="image-key">extra-diner</span>
<span class="image-title">שקף חדש: Peggy Sue’s Diner</span>
<span class="image-desc">אחרי יום 7 — דיינר אמריקאי קלאסי / עצירת Road Trip</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-08">
<input class="image-file-input" id="image-file-day-08" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-08" class="image-upload-label">
<span class="image-key">day-08</span>
<span class="image-title">יום 8</span>
<span class="image-desc">Zion → Bryce → Page / Bryce Canyon, Hoodoos, נוף יוטה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-09">
<input class="image-file-input" id="image-file-day-09" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-09" class="image-upload-label">
<span class="image-key">day-09</span>
<span class="image-title">יום 9</span>
<span class="image-desc">Antelope Canyon + Horseshoe Bend / קניון אנטילופ, פרסת הסוס</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-10">
<input class="image-file-input" id="image-file-day-10" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-10" class="image-upload-label">
<span class="image-key">day-10</span>
<span class="image-title">יום 10</span>
<span class="image-desc">Page → Grand Canyon South Rim / גרנד קניון דרום</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-11">
<input class="image-file-input" id="image-file-day-11" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-11" class="image-upload-label">
<span class="image-key">day-11</span>
<span class="image-title">יום 11</span>
<span class="image-desc">Grand Canyon → Las Vegas / כניסה לווגאס, Strip, ניאון</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-12">
<input class="image-file-input" id="image-file-day-12" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-12" class="image-upload-label">
<span class="image-key">day-12</span>
<span class="image-title">יום 12</span>
<span class="image-desc">Vegas Outlets + Sphere / Sphere, אאוטלטים, וגאס בלילה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-13">
<input class="image-file-input" id="image-file-day-13" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-13" class="image-upload-label">
<span class="image-key">day-13</span>
<span class="image-title">יום 13</span>
<span class="image-desc">Vegas Extra Day + Strip / Bellagio, Strip, קניות, מלונות</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-14">
<input class="image-file-input" id="image-file-day-14" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-14" class="image-upload-label">
<span class="image-key">day-14</span>
<span class="image-title">יום 14</span>
<span class="image-desc">Las Vegas → Mammoth Lakes / אגמים והרים ב־Mammoth</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="extra-death-valley">
<input class="image-file-input" id="image-file-extra-death-valley" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-extra-death-valley" class="image-upload-label">
<span class="image-key">extra-death-valley</span>
<span class="image-title">שקף חדש: Death Valley</span>
<span class="image-desc">אחרי יום 14 — הדרך מווגאס למדבר / עמק המוות / כביש פתוח</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="extra-mono-lake">
<input class="image-file-input" id="image-file-extra-mono-lake" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-extra-mono-lake" class="image-upload-label">
<span class="image-key">extra-mono-lake</span>
<span class="image-title">שקף חדש: Mono Lake / Eastern Sierra</span>
<span class="image-desc">אחרי Death Valley ולפני Yosemite — Mono Lake / הרי הסיירה / מעבר ליוסמיטי</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-15">
<input class="image-file-input" id="image-file-day-15" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-15" class="image-upload-label">
<span class="image-key">day-15</span>
<span class="image-title">יום 15</span>
<span class="image-desc">Mammoth → Yosemite Valley / Tunnel View, El Capitan, Half Dome</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-16">
<input class="image-file-input" id="image-file-day-16" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-16" class="image-upload-label">
<span class="image-key">day-16</span>
<span class="image-title">יום 16</span>
<span class="image-desc">Yosemite Highlights + Mariposa Grove / סקויה ענקית, מפלים, יוסמיטי</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-17">
<input class="image-file-input" id="image-file-day-17" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-17" class="image-upload-label">
<span class="image-key">day-17</span>
<span class="image-title">יום 17</span>
<span class="image-desc">Yosemite → SFO → Orlando / יום מעבר וטיסה לאורלנדו</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-18">
<input class="image-file-input" id="image-file-day-18" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-18" class="image-upload-label">
<span class="image-key">day-18</span>
<span class="image-title">יום 18</span>
<span class="image-desc">התאוששות + Disney Springs / Disney Springs בלילה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-19">
<input class="image-file-input" id="image-file-day-19" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-19" class="image-upload-label">
<span class="image-key">day-19</span>
<span class="image-title">יום 19</span>
<span class="image-desc">Premier Check-In + Islands of Adventure / Hulk, Hogwarts, Universal</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-20">
<input class="image-file-input" id="image-file-day-20" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-20" class="image-upload-label">
<span class="image-key">day-20</span>
<span class="image-title">יום 20</span>
<span class="image-desc">Universal Studios Florida + CityWalk / גלוב יוניברסל ו־CityWalk</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-21">
<input class="image-file-input" id="image-file-day-21" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-21" class="image-upload-label">
<span class="image-key">day-21</span>
<span class="image-title">יום 21</span>
<span class="image-desc">מנוחה + Hollywood Drive‑In Mini Golf / מיני גולף צבעוני באורלנדו</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-22">
<input class="image-file-input" id="image-file-day-22" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-22" class="image-upload-label">
<span class="image-key">day-22</span>
<span class="image-title">יום 22</span>
<span class="image-desc">Epic Universe + Express / Epic Universe, פורטלים, פארק חדש</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-23">
<input class="image-file-input" id="image-file-day-23" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-23" class="image-upload-label">
<span class="image-key">day-23</span>
<span class="image-title">יום 23</span>
<span class="image-desc">פארק חבלים + בריכה / International Drive / אקשן קליל ובריכה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-24">
<input class="image-file-input" id="image-file-day-24" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-24" class="image-upload-label">
<span class="image-key">day-24</span>
<span class="image-title">יום 24</span>
<span class="image-desc">Volcano Bay + Express / Krakatau, מגלשות מים, פארק מים</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-25">
<input class="image-file-input" id="image-file-day-25" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-25" class="image-upload-label">
<span class="image-key">day-25</span>
<span class="image-title">יום 25</span>
<span class="image-desc">רגוע + Halloween Horror Nights / HHN, אימה, Universal בלילה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-26">
<input class="image-file-input" id="image-file-day-26" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-26" class="image-upload-label">
<span class="image-key">day-26</span>
<span class="image-title">יום 26</span>
<span class="image-desc">Orlando → Sawgrass Mills → Miami / Sawgrass, שופינג, מעבר למיאמי</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-27">
<input class="image-file-input" id="image-file-day-27" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-27" class="image-upload-label">
<span class="image-key">day-27</span>
<span class="image-title">יום 27</span>
<span class="image-desc">Miami Chill Before Cruise / Miami Beach, Ocean Drive, דקלים</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-28">
<input class="image-file-input" id="image-file-day-28" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-28" class="image-upload-label">
<span class="image-key">day-28</span>
<span class="image-title">יום 28</span>
<span class="image-desc">החזרת רכב ועלייה לקרוז / נמל מיאמי ואוניית קרוז ענקית</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-29">
<input class="image-file-input" id="image-file-day-29" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-29" class="image-upload-label">
<span class="image-key">day-29</span>
<span class="image-title">יום 29</span>
<span class="image-desc">קרוז: יום ים 1 / סיפון אונייה, בריכה, אוקיינוס</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-30">
<input class="image-file-input" id="image-file-day-30" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-30" class="image-upload-label">
<span class="image-key">day-30</span>
<span class="image-title">יום 30</span>
<span class="image-desc">קרוז: יום ים 2 / מגלשות, בריכות ואווירת אונייה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-31">
<input class="image-file-input" id="image-file-day-31" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-31" class="image-upload-label">
<span class="image-key">day-31</span>
<span class="image-title">יום 31</span>
<span class="image-desc">St. Maarten / חוף קריבי, מים טורקיז, אי</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-32">
<input class="image-file-input" id="image-file-day-32" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-32" class="image-upload-label">
<span class="image-key">day-32</span>
<span class="image-title">יום 32</span>
<span class="image-desc">St. Thomas + Dolphin Splash / דולפינים וחוף קריבי</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-33">
<input class="image-file-input" id="image-file-day-33" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-33" class="image-upload-label">
<span class="image-key">day-33</span>
<span class="image-title">יום 33</span>
<span class="image-desc">קרוז: יום ים 3 / שקיעה מהסיפון ואוקיינוס</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-34">
<input class="image-file-input" id="image-file-day-34" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-34" class="image-upload-label">
<span class="image-key">day-34</span>
<span class="image-title">יום 34</span>
<span class="image-desc">Perfect Day at CocoCay / CocoCay, חוף טורקיז, פארק מים</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-35">
<input class="image-file-input" id="image-file-day-35" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-35" class="image-upload-label">
<span class="image-key">day-35</span>
<span class="image-title">יום 35</span>
<span class="image-desc">ירידה מהקרוז + לילה ליד השדה / מיאמי, שדה, סגירת טיול</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="day-36">
<input class="image-file-input" id="image-file-day-36" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-day-36" class="image-upload-label">
<span class="image-key">day-36</span>
<span class="image-title">יום 36</span>
<span class="image-desc">טיסה חזרה לישראל / מטוס, חלון, שקיעה</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form><form class="image-upload-card" action="/upload_usa_image" method="post" enctype="multipart/form-data">
<input type="hidden" name="image_key" value="summary">
<input class="image-file-input" id="image-file-summary" type="file" name="file" accept=".jpg,.jpeg,.png,.webp,image/*" onchange="if(this.files && this.files.length){this.closest('form').querySelector('.upload-status').textContent='מעלה תמונה...';this.closest('form').submit();}">
<label for="image-file-summary" class="image-upload-label">
<span class="image-key">summary</span>
<span class="image-title">סיכום</span>
<span class="image-desc">תמונה משפחתית / Road Trip / סוף מסע</span>
<span class="upload-status">לחץ כאן ובחר תמונה מהטלפון</span>
</label>
</form>
</div>
<p class="note">התמונות נשמרות אוטומטית בתיקייה:<br><strong>C:\\TripServer\\USA\\media\\usa-presentation\\images</strong><br>ובשם קבוע לפי השקף, למשל <strong>day-04.jpg</strong>, <strong>day-12.jpg</strong>, <strong>day-34.jpg</strong>, <strong>extra-big-sur.jpg</strong>, <strong>extra-diner.jpg</strong>, <strong>extra-death-valley.jpg</strong>, <strong>extra-mono-lake.jpg</strong>.</p>
<div class="preview-links">
<a href="/USA/usa2027-presentation.html?from=admin" target="_blank">בדיקת המצגת אחרי העלאת תמונה</a>
</div>
</div>

<div class="box">
<h2>🎵 מוזיקת מצגת ארה״ב</h2>
<div class="good">עודכן למצגת שני השירים: אפשר להעלות מהפאנל את שיר חלק 1 ואת שיר חלק 2. לא משנה מה שם הקובץ בטלפון — הפאנל שומר בשם הקבוע שהמצגת מחפשת.</div>
<h3>🎸 שיר חלק 1 — Road Trip</h3>
<form action="/upload_usa_music/usa_music" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".mp3,.mpeg,.wav,.m4a,.aac,.ogg">
<button>העלה שיר חלק 1</button>
</form>
<p class="note">יישמר כ־<strong>C:\\TripServer\\USA\\media\\usa-presentation\\music\\usa-roadtrip-theme.mp3</strong></p>
<h3>🎧 שיר חלק 2 — Orlando / Miami / Cruise</h3>
<form action="/upload_usa_music/usa_music_part2" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".mp3,.mpeg,.wav,.m4a,.aac,.ogg">
<button>העלה שיר חלק 2</button>
</form>
<p class="note">יישמר כ־<strong>C:\\TripServer\\USA\\media\\usa-presentation\\music\\usa-part2-theme.mp3</strong></p>
<div class="preview-links">
<a href="/USA/media/usa-presentation/music/usa-roadtrip-theme.mp3" target="_blank">בדיקת שיר חלק 1</a>
<a href="/USA/media/usa-presentation/music/usa-part2-theme.mp3" target="_blank">בדיקת שיר חלק 2</a>
<a href="/USA/usa2027-presentation.html?from=admin" target="_blank">בדיקת המצגת עם שני השירים</a>
</div>
</div>

<div class="box">
<h2>⚙️ עדכון קובץ הפאנל / admin_server.py</h2>
<div class="warning"><strong>זהירות:</strong> זה הקובץ שמפעיל את הפאנל עצמו. ההעלאה תתבצע רק אחרי בדיקת קומפילציה, גיבוי אוטומטי, ורק אז החלפה.</div>
<div class="good">הזרימה: העלאת admin_server.py חדש → בדיקת Python → גיבוי לקובץ הקיים → החלפה → אתחול פאנל.</div>
<form action="/upload_admin_server" method="post" enctype="multipart/form-data" onsubmit="return confirm('להעלות admin_server.py חדש ולהחליף את קובץ הפאנל הפעיל אחרי בדיקה וגיבוי?')">
<input type="file" name="file" accept=".py,text/x-python,text/plain">
<button class="orange">העלה admin_server.py חדש</button>
</form>
<div class="admin-update-actions">
<form action="/restart_admin_server" method="post" onsubmit="return confirm('לאתחל את הפאנל עכשיו?')">
<button class="orange">אתחל פאנל</button>
</form>
<form action="/restore_admin_server_backup" method="post" onsubmit="return confirm('לשחזר את הגיבוי האחרון של admin_server.py?')">
<button class="danger">שחזר גיבוי אחרון</button>
</form>
</div>
<p class="note">הקובץ הפעיל: <strong>C:\\TripServer\\admin_server.py</strong><br>גיבויים נשמרים אוטומטית בתיקייה: <strong>C:\\TripServer\\backups\\admin_server</strong></p>
<div class="admin-mini">הערה: אחרי החלפת הקובץ, חובה ללחוץ “אתחל פאנל” כדי שהשרת יעבור לגרסה החדשה. עד האתחול, הפאנל שרץ בזיכרון ממשיך לעבוד על הגרסה הישנה.</div>
</div>

<div class="box">
<h2>💰 תקציב טיולים</h2>
<form action="/upload/Moneyapp" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה קובץ</button>
</form>
<p class="note">כל קובץ שתעלה כאן יישמר כ־index.html.</p>
</div>

<div class="box">
<h2>🏠 תקציב משפחתי</h2>
<form action="/upload/MoneyHome" method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".html,.htm">
<button>העלה קובץ</button>
</form>
<p class="note">כל קובץ שתעלה כאן יישמר כ־index.html.</p>
</div>
</div>
</body>
</html>
"""



@app.route("/health")
def health():
    return {"ok": True, "service": "TripServer Admin", "version": "USA2027 panel v3 reliable restart", "slides": 43}

@app.route("/upload_admin_server", methods=["POST"])
def upload_admin_server():
    file = request.files.get("file")
    if not file or file.filename == "":
        return admin_status_page("לא נבחר קובץ", "בחר קובץ admin_server.py להעלאה.", ok=False), 400

    original_name = file.filename or ""
    if not original_name.lower().endswith(".py"):
        return admin_status_page("קובץ לא תקין", "ניתן להעלות כאן רק קובץ Python עם סיומת .py", ok=False), 400

    if request.content_length and request.content_length > ADMIN_MAX_UPLOAD_BYTES + 250000:
        return admin_status_page("קובץ גדול מדי", f"הקובץ גדול מהמותר. המגבלה היא בערך {ADMIN_MAX_UPLOAD_BYTES:,} bytes.", ok=False), 413

    os.makedirs(ADMIN_UPLOAD_TMP_DIR, exist_ok=True)
    stamp = time.strftime("%Y%m%d_%H%M%S")
    tmp_path = os.path.join(ADMIN_UPLOAD_TMP_DIR, f"admin_server_upload_{stamp}.py")

    try:
        file.save(tmp_path)
        valid, details = validate_admin_server_upload(tmp_path)
        if not valid:
            try:
                os.remove(tmp_path)
            except Exception:
                pass
            return admin_status_page("העלאת admin_server.py נדחתה", "הקובץ הקיים לא הוחלף. נמצאה בעיה בקובץ שהעלית.", ok=False, details=details), 400

        backup_path = backup_current_admin_server("admin_server_backup")
        os.replace(tmp_path, ADMIN_SERVER_PATH)
        message = (
            "הקובץ החדש עבר בדיקת קומפילציה והחליף את admin_server.py הפעיל.\n"
            "נוצר גיבוי לקובץ הקודם.\n\n"
            "כדי שהשינוי ייכנס בפועל, לחץ עכשיו על אתחל פאנל."
        )
        details = f"קובץ פעיל:\n{ADMIN_SERVER_PATH}\n\nגיבוי:\n{backup_path}"
        return admin_status_page("admin_server.py עודכן בהצלחה", message, ok=True, details=details)
    except Exception as exc:
        try:
            if os.path.exists(tmp_path):
                os.remove(tmp_path)
        except Exception:
            pass
        return admin_status_page("שגיאה בעדכון admin_server.py", "הקובץ הקיים לא הוחלף.", ok=False, details=repr(exc)), 500


@app.route("/restart_admin_server", methods=["POST"])
def restart_admin_server():
    threading.Thread(target=restart_process_later, daemon=True).start()
    return admin_status_page(
        "הפאנל מופעל מחדש",
        "האיפוס התחיל דרך מנגנון Restart חיצוני. המתן 5–10 שניות ואז רענן את הפאנל. אם לא עולה, בדוק את C:\\TripServer\\admin_restart.log.",
        ok=True,
    )


@app.route("/restore_admin_server_backup", methods=["POST"])
def restore_admin_server_backup():
    backup_path = latest_admin_backup()
    if not backup_path:
        return admin_status_page("אין גיבוי לשחזור", "לא נמצא קובץ גיבוי בתיקיית הגיבויים.", ok=False), 404

    try:
        valid, details = validate_admin_server_upload(backup_path)
        if not valid:
            return admin_status_page("הגיבוי האחרון לא תקין", "הגיבוי לא שוחזר כי הוא לא עבר בדיקת תקינות.", ok=False, details=details), 400
        safety_backup = backup_current_admin_server("admin_server_before_restore")
        shutil.copy2(backup_path, ADMIN_SERVER_PATH)
        msg = "הגיבוי האחרון שוחזר אל admin_server.py. לחץ אתחל פאנל כדי להפעיל אותו."
        det = f"שוחזר מתוך:\n{backup_path}\n\nגיבוי בטיחות לגרסה שהייתה פעילה לפני השחזור:\n{safety_backup}"
        return admin_status_page("שחזור גיבוי בוצע", msg, ok=True, details=det)
    except Exception as exc:
        return admin_status_page("שגיאה בשחזור גיבוי", "לא בוצע שחזור.", ok=False, details=repr(exc)), 500


@app.route("/upload/<folder>", methods=["POST"])
def upload(folder):
    if folder not in FOLDERS:
        return "Folder not found", 404
    file = request.files.get("file")
    if not file or file.filename == "":
        return "No file selected", 400
    save_uploaded_file(folder, file)
    return redirect("/admin")


@app.route("/upload_presentation/<presentation_key>", methods=["POST"])
def upload_presentation(presentation_key):
    file = request.files.get("file")
    if not file or file.filename == "":
        return "No file selected", 400
    filename = save_japan_presentation(presentation_key, file)
    if not filename:
        return "Presentation type not found", 404
    return redirect("/admin")


@app.route("/upload_usa_presentation/<presentation_key>", methods=["POST"])
def upload_usa_presentation(presentation_key):
    file = request.files.get("file")
    if not file or file.filename == "":
        return "No file selected", 400
    filename = save_usa_presentation(presentation_key, file)
    if not filename:
        return "USA presentation type not found", 404
    return redirect("/admin")



@app.route("/upload_usa_image", methods=["POST"])
def upload_usa_image():
    image_key = request.form.get("image_key", "").strip()
    file = request.files.get("file")
    if not file or file.filename == "":
        return "No file selected", 400
    filename = save_usa_presentation_image(image_key, file)
    if not filename:
        return "USA image key not found", 404
    return redirect("/admin")


@app.route("/upload_usa_images_zip", methods=["POST"])
def upload_usa_images_zip():
    file = request.files.get("file")
    if not file or file.filename == "":
        return "No file selected", 400
    original_name = file.filename or ""
    if not original_name.lower().endswith(".zip"):
        return admin_status_page("קובץ לא תקין", "ניתן להעלות כאן רק קובץ ZIP של תמונות מצגת.", ok=False), 400
    try:
        imported, skipped = save_usa_presentation_images_zip(file)
        details = "הועלו:\n" + ("\n".join(imported) if imported else "לא הועלו תמונות.")
        if skipped:
            details += "\n\nדולגו כי השם לא מתאים למצגת או שהסיומת לא נתמכת:\n" + "\n".join(skipped[:80])
        return admin_status_page(
            "ZIP תמונות ארה״ב נטען",
            f"הועלו {len(imported)} תמונות למצגת. עכשיו אפשר לבדוק את המצגת מהפאנל.",
            ok=True,
            details=details,
        )
    except zipfile.BadZipFile:
        return admin_status_page("ZIP לא תקין", "הקובץ שהועלה אינו ZIP תקין או שלא ניתן לפתוח אותו.", ok=False), 400
    except Exception as exc:
        return admin_status_page("שגיאה בהעלאת ZIP", "לא ניתן היה לעבד את קובץ התמונות.", ok=False, details=repr(exc)), 500


@app.route("/upload_usa_music/<music_key>", methods=["POST"])
def upload_usa_music(music_key):
    file = request.files.get("file")
    if not file or file.filename == "":
        return "No file selected", 400
    filename = save_usa_music(music_key, file)
    if not filename:
        return "USA music type not found", 404
    return redirect("/admin")


@app.route("/JAPAN")
@app.route("/JAPAN/")
def japan():
    return serve_app("JAPAN")


@app.route("/USA")
@app.route("/USA/")
def usa():
    return serve_app("USA")


@app.route("/Moneyapp")
@app.route("/Moneyapp/")
def moneyapp():
    return serve_app("Moneyapp")


@app.route("/MoneyHome")
@app.route("/MoneyHome/")
def moneyhome():
    return serve_app("MoneyHome")


@app.route("/JAPAN/<path:path>")
def japan_static(path):
    response = make_response(send_from_directory(FOLDERS["JAPAN"], path))
    return no_cache_response(response)


@app.route("/USA/<path:path>")
def usa_static(path):
    response = make_response(send_from_directory(FOLDERS["USA"], path))
    return no_cache_response(response)


@app.route("/Moneyapp/<path:path>")
def money_static(path):
    response = make_response(send_from_directory(FOLDERS["Moneyapp"], path))
    return no_cache_response(response)


@app.route("/MoneyHome/<path:path>")
def moneyhome_static(path):
    response = make_response(send_from_directory(FOLDERS["MoneyHome"], path))
    return no_cache_response(response)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000, threaded=True, use_reloader=False)
