⬅ חזרה לתיקייה
✏️ עריכת index.html
C:\TripServer\TripForm\index.html
<!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <meta name="theme-color" content="#0f172a"> <title>טופס קליטת טיול חדש — סוכן הטיולים של איתן v4</title> <style> :root{ --ink:#0f172a;--muted:#475569;--bg:#f4f7fb;--card:#fff;--line:rgba(15,23,42,.13); --teal:#0f766e;--blue:#2563eb;--purple:#7c3aed;--orange:#f97316;--green:#166534;--red:#dc2626;--gold:#f59e0b; --shadow:0 14px 36px rgba(15,23,42,.08); --radius:24px; } *{box-sizing:border-box} html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;text-size-adjust:100%;overflow-x:hidden} body{ margin:0;direction:rtl;color:var(--ink);font-family:Arial,"Noto Sans Hebrew","Segoe UI",sans-serif; font-size:16px;line-height:1.65;background: radial-gradient(circle at 92% 0%,rgba(20,184,166,.14),transparent 30rem), radial-gradient(circle at 0% 0%,rgba(124,58,237,.12),transparent 28rem), linear-gradient(180deg,#f8fafc 0%,#eef6f8 55%,#f8fafc 100%); } a{color:var(--teal);font-weight:900} .wrap{width:100%;max-width:1180px;margin:0 auto;padding:22px 14px 90px} header{ position:relative;overflow:hidden;color:#fff;border-radius:34px;padding:30px 22px;margin:10px 0 16px; background:linear-gradient(135deg,#020617 0%,#0f172a 42%,#0f766e 100%); box-shadow:0 24px 56px rgba(15,23,42,.25) } header:before{content:"";position:absolute;inset:0;background:radial-gradient(circle at top right,rgba(56,189,248,.25),transparent 24rem),radial-gradient(circle at bottom left,rgba(249,115,22,.18),transparent 22rem);pointer-events:none} header>*{position:relative;z-index:1} .badge{display:inline-flex;gap:7px;align-items:center;justify-content:center;min-height:34px;padding:7px 12px;border-radius:999px;background:#dcfce7;color:#166534;font-weight:950;font-size:13px;margin-bottom:12px} h1{margin:0 0 10px;font-size:clamp(30px,7vw,52px);line-height:1.12;letter-spacing:-.4px} .subtitle{font-size:clamp(16px,3.8vw,22px);font-weight:850;opacity:.95;max-width:920px} .hero-grid,.score-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-top:20px} .hero-card,.score-card{ background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.18);border-radius:20px;padding:14px;backdrop-filter:blur(8px);font-weight:900 } .hero-card .label,.score-card .label{font-size:12px;opacity:.78;margin-bottom:4px} .hero-card .value,.score-card .value{font-size:clamp(18px,4vw,24px);font-weight:950} .card,.section-nav,.summary-panel{ background:linear-gradient(180deg,#ffffff,#f8fafc);border:1px solid var(--line);border-radius:var(--radius); padding:20px 16px;margin:18px 0;box-shadow:var(--shadow);overflow:hidden } .section-nav{position:sticky;top:0;z-index:10;background:linear-gradient(145deg,#020617,#0f172a);padding:12px;border-radius:0 0 22px 22px;margin:0 -14px 18px} .nav-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(136px,1fr));gap:8px} .nav-grid a,.action-btn,.mini-btn{ display:inline-flex;align-items:center;justify-content:center;text-align:center;min-height:42px;padding:9px 12px;border:0;border-radius:16px; color:#fff;text-decoration:none;font-family:inherit;font-weight:950;line-height:1.22;cursor:pointer;background:linear-gradient(135deg,#0f766e,#2563eb); box-shadow:0 7px 16px rgba(15,23,42,.16) } .nav-grid a:nth-child(3n+1){background:linear-gradient(135deg,#7c3aed,#2563eb)} .nav-grid a:nth-child(3n+2){background:linear-gradient(135deg,#14b8a6,#0f766e)} .nav-grid a:nth-child(3n+3){background:linear-gradient(135deg,#fb923c,#ea580c)} h2{font-size:clamp(24px,5.8vw,34px);line-height:1.25;margin:0 0 12px;color:#0f4f57} h2:after{content:"";display:block;width:112px;height:5px;border-radius:999px;margin-top:8px;background:linear-gradient(90deg,#0f766e,#38bdf8,#f59e0b)} h3{font-size:clamp(19px,4.6vw,24px);color:var(--teal);margin:20px 0 8px} .note,.warn,.ok,.gold{ border-radius:16px;padding:12px 14px;margin:12px 0;font-weight:850;line-height:1.55 } .note{background:#eff6ff;border:1px solid #bfdbfe;color:#1e3a8a} .warn{background:#fff7ed;border:1px solid #fed7aa;color:#7c2d12} .ok{background:#ecfdf5;border:1px solid #a7f3d0;color:#14532d} .gold{background:#fef3c7;border:1px solid #facc15;color:#78350f} .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px} .field{display:flex;flex-direction:column;gap:5px;min-width:0} .field.full{grid-column:1/-1} label{font-weight:950;color:#0f172a} small{color:var(--muted);font-weight:750;line-height:1.4} input,select,textarea{ width:100%;min-height:44px;border:1px solid rgba(15,23,42,.18);border-radius:14px;padding:10px 12px; font:inherit;font-size:15px;background:#fff;color:var(--ink);outline:none } textarea{min-height:92px;resize:vertical} input:focus,select:focus,textarea:focus{border-color:#38bdf8;box-shadow:0 0 0 4px rgba(56,189,248,.18)} .check-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:8px;margin-top:8px} .check-item{display:flex;gap:8px;align-items:flex-start;background:#f8fafc;border:1px solid rgba(15,23,42,.10);border-radius:14px;padding:10px;font-weight:850} .check-item input{width:22px;min-height:22px;height:22px;margin-top:2px} .repeater{border:1px solid rgba(15,23,42,.12);border-radius:18px;background:#fff;margin:14px 0;padding:12px} .repeater-head{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:10px} .repeater-title{font-size:18px;font-weight:950;color:#0f172a} .rows{display:grid;gap:10px} .row{ position:relative;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;border:1px solid #dbeafe;border-radius:16px;background:#f8fafc;padding:12px } .row .wide{grid-column:span 2} .row .full{grid-column:1/-1} .remove-btn{position:absolute;top:8px;left:8px;background:linear-gradient(135deg,#ef4444,#b91c1c);min-height:34px;padding:6px 10px;border-radius:999px} .action-bar{display:flex;flex-wrap:wrap;gap:10px;margin-top:14px} .action-btn.secondary{background:linear-gradient(135deg,#64748b,#334155)} .action-btn.danger{background:linear-gradient(135deg,#ef4444,#b91c1c)} .action-btn.gold-btn{background:linear-gradient(135deg,#f59e0b,#ea580c)} .progress-wrap{background:#e2e8f0;border-radius:999px;overflow:hidden;height:14px;margin:10px 0} .progress-bar{height:100%;width:0%;background:linear-gradient(90deg,#ef4444,#f59e0b,#22c55e);transition:width .25s ease} #missingList{padding-right:20px;margin:8px 0} #missingList li{margin:6px 0} .preview{white-space:pre-wrap;background:#020617;color:#e5e7eb;border-radius:18px;padding:14px;direction:rtl;text-align:right;max-height:420px;overflow:auto;font-family:Consolas,Menlo,monospace;font-size:13px} .pill{display:inline-flex;align-items:center;justify-content:center;padding:5px 10px;border-radius:999px;background:#eef2ff;color:#312e81;font-weight:950;font-size:12px;margin:3px} .footer-floating{ position:fixed;right:10px;left:10px;bottom:calc(8px + env(safe-area-inset-bottom,0px));z-index:999; display:grid;grid-template-columns:repeat(4,1fr);gap:8px;max-width:880px;margin:auto;padding:8px;border-radius:22px; background:rgba(2,6,23,.92);backdrop-filter:blur(10px);box-shadow:0 18px 44px rgba(0,0,0,.35) } .footer-floating button{min-height:42px;border-radius:16px;font-size:13px;padding:8px} .reference-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px} .reference-card{background:#fff;border:1px solid rgba(15,23,42,.11);border-radius:18px;padding:13px;box-shadow:0 5px 18px rgba(15,23,42,.05)} .reference-card b{display:block;color:#0f4f57;margin-bottom:5px} @media(max-width:760px){ .wrap{padding:14px 10px 110px} header{border-radius:26px;padding:24px 16px} .section-nav{position:static;margin:0 0 14px;border-radius:22px} .nav-grid{grid-template-columns:1fr 1fr} .form-grid{grid-template-columns:1fr} .row{grid-template-columns:1fr} .row .wide,.row .full{grid-column:1/-1} .card{padding:16px 12px;border-radius:20px} .footer-floating{grid-template-columns:1fr 1fr} } @media(max-width:390px){.nav-grid{grid-template-columns:1fr}.footer-floating{grid-template-columns:1fr}.footer-floating button{min-height:38px}} @media print{.section-nav,.footer-floating,.action-bar,.mini-btn,.remove-btn{display:none!important}body{background:#fff}.card,header{box-shadow:none;break-inside:avoid}} </style> </head> <body> <div class="wrap" id="top"> <header> <div class="badge">🧭 סוכן הטיולים של איתן · v3 Gold Reference</div> <h1>טופס קליטת טיול חדש</h1> <div class="subtitle">ממלאים פעם אחת את נתוני הפתיחה — והסוכן מקבל מקור אמת שמספיק לבניית קובץ HTML ברמת יפן/ארה״ב ומעלה: לוגיקה, תקציב, ימים, מובייל, מצב הסתר, נספחים, קישורים וגרסאות סופיות.</div> <div class="hero-grid"> <div class="hero-card"><div class="label">סטנדרט ייחוס</div><div class="value">יפן 2026 + ארה״ב 2027</div></div> <div class="hero-card"><div class="label">תוצר יעד</div><div class="value">HTML פרימיום + בדיקות איתן</div></div> <div class="hero-card"><div class="label">ייצוא</div><div class="value">JSON / TXT / HTML ממולא</div></div> <div class="hero-card"><div class="label">שמירה</div><div class="value">שמירה מקומית בדפדפן</div></div> </div> </header> <section class="section-nav"> <div class="nav-grid"> <a href="#readiness">מוכנות</a> <a href="#gold">סטנדרט זהב</a> <a href="#basics">בסיס הטיול</a> <a href="#people">משתתפים</a> <a href="#route">מסלול ולינות</a> <a href="#flights">טיסות ותחבורה</a> <a href="#content">חובה / לא לשלב</a> <a href="#parks">פארקים וכרטיסים</a> <a href="#budget">תקציב</a> <a href="#food">אוכל</a> <a href="#design">עיצוב וקובץ</a> <a href="#deliver">בדיקות וייצוא</a> </div> </section> <section class="summary-panel" id="readiness"> <h2>מד מוכנות לסוכן</h2> <div class="score-grid"> <div class="score-card" style="background:linear-gradient(135deg,#312e81,#2563eb);color:#fff"><div class="label">ציון מוכנות</div><div class="value" id="scoreText">0%</div></div> <div class="score-card"><div class="label">ימים / לילות</div><div class="value" id="durationText">—</div></div> <div class="score-card"><div class="label">רמת קובץ</div><div class="value" id="levelText">—</div></div> <div class="score-card"><div class="label">מצב מקור אמת</div><div class="value" id="sourceTruthText">חסר</div></div> </div> <div class="progress-wrap"><div class="progress-bar" id="progressBar"></div></div> <div class="note">המטרה אינה רק “למלא טופס”, אלא לתת לסוכן מספיק מידע כדי לא לנחש: מה קשיח, מה גמיש, מה כבר שולם, מה חובה, מה אסור, איזה עיצוב לשמר, ואילו בדיקות חייבות לעבור.</div> <h3>מה עדיין חסר</h3> <ul id="missingList"></ul> </section> <section class="card" id="gold"> <h2>סטנדרט זהב מקבצי יפן וארה״ב</h2> <div class="reference-list"> <div class="reference-card"><b>מבנה חובה</b>Hero פתיחה, נתוני טיול, תוכן עניינים, חלקי מסלול, כרטיסי יום, תקציב, נספחים תפעוליים.</div> <div class="reference-card"><b>יום טיול</b>כותרת, תאריך, סיכום, לוז, תקציב יום, קישורי ניווט, נקודות מרכזיות, אוכל, טיפים, תוכנית גיבוי.</div> <div class="reference-card"><b>מובייל</b>RTL מלא, בלי גלילה אופקית, כפתורים גדולים, מצב הסתר/מובייל, פתיחה תקינה באייפון.</div> <div class="reference-card"><b>תקציב</b>מנוע דינמי, עריכה, prepaid, מטבעות, שער עדכני, הפרדה בין קניות/לוז/שולם מראש.</div> <div class="reference-card"><b>תפעול</b>מזוודות, דד־ליינים, תוכניות גיבוי, חירום, מזג אוויר, עומסים, החלטות משפחתיות.</div> <div class="reference-card"><b>מסירה סופית</b>שתי גרסאות בלבד: Netlify ושרת פרטי, בלי גרסאות מיותרות ובלי שכבות Legacy.</div> </div> <div class="warn">חשוב: קובץ ייחוס לא אומר להעתיק עיוור. המטרה היא לשמר את הרמה, המבנה, המנגנונים והעיצוב — אבל לבנות נקי יותר, עם מקור אמת מרכזי יותר ופחות כפילויות.</div> <div class="form-grid"> <div class="field"> <label>איזה קובץ ייחוס מתאים יותר לטיול החדש?</label> <select data-path="reference.primaryStyle"> <option value="">בחר</option> <option>יפן — טיול עירוני/תחבורה ציבורית/רב־עירוני</option> <option>ארה״ב — Road Trip/פארקים/קרוז/רכב</option> <option>שילוב יפן + ארה״ב</option> <option>לבנות וריאציה חדשה, אבל לפי אותו סטנדרט</option> </select> </div> <div class="field"> <label>רמת נאמנות לעיצוב הקיים</label> <select data-path="reference.designFaithfulness"> <option value="">בחר</option> <option>גבוהה מאוד — לשמר כמעט הכול</option> <option>גבוהה — לשמר מבנה ושפה, לשפר ניקיון</option> <option>בינונית — לקחת השראה, לבנות חדש</option> </select> </div> <div class="field full"> <label>מה חייבים לשמר מקבצי יפן/ארה״ב?</label> <textarea data-path="reference.keep" placeholder="לדוגמה: מצב הסתר, מסך טעינה, תוכן עניינים צבעוני, טבלאות תקציב, כפתורי ניווט, כרטיסי יום, נספחים..."></textarea> </div> <div class="field full"> <label>מה לשפר ביחס לקבצים הישנים?</label> <textarea data-path="reference.improve" placeholder="לדוגמה: פחות כפילויות, מקור אמת אחד, קובץ קל יותר, תמונות דחוסות, פחות שכבות CSS ישנות, תקציב אחיד יותר..."></textarea> </div> </div> </section> <section class="card" id="basics"> <h2>1. בסיס הטיול</h2> <div class="form-grid"> <div class="field"><label>יעד / מדינה / אזור</label><input data-path="basics.destination" placeholder="לדוגמה: הולנד / איטליה / תאילנד"></div> <div class="field"><label>שם פנימי לטיול</label><input data-path="basics.tripName" placeholder="לדוגמה: הולנד 2027 משפחתי"></div> <div class="field"><label>תאריך התחלה</label><input type="date" data-path="basics.startDate"></div> <div class="field"><label>תאריך סיום</label><input type="date" data-path="basics.endDate"></div> <div class="field"><label>סטטוס</label><select data-path="basics.status"><option value="">בחר</option><option>רעיון ראשוני</option><option>בודק כדאיות</option><option>תאריכים סגורים</option><option>טיסות הוזמנו</option><option>חלק מהדברים הוזמנו</option></select></div> <div class="field"><label>מטרת העבודה</label><select data-path="basics.goal"><option value="">בחר</option><option>תכנון ראשוני בלבד</option><option>שלד מסלול</option><option>השוואת חלופות</option><option>קובץ HTML מלא</option><option>קובץ סופי עם בדיקות איתן</option></select></div> <div class="field full"><label>רקע חופשי</label><textarea data-path="basics.freeNotes" placeholder="כל דבר שחשוב לדעת לפני שמתחילים: למה היעד, מה החלום, מה הרגישויות, מה הסגנון..."></textarea></div> </div> </section> <section class="card" id="people"> <h2>2. משתתפים וקצב משפחתי</h2> <div class="repeater" data-repeater="participants"> <div class="repeater-head"><div class="repeater-title">משתתפים</div><button class="mini-btn" type="button" data-add="participants">+ הוסף משתתף</button></div> <div class="rows"></div> </div> <div class="form-grid"> <div class="field"><label>קצב טיול רצוי</label><select data-path="people.pace"><option value="">בחר</option><option>רגוע מאוד</option><option>מאוזן</option><option>עמוס אבל הגיוני</option><option>מקסימום הספק</option></select></div> <div class="field"><label>שעת יציאה מועדפת בבוקר</label><input data-path="people.startTimePreference" placeholder="לדוגמה: 09:30–10:00"></div> <div class="field"><label>שעת חזרה מועדפת למלון</label><input data-path="people.returnTimePreference" placeholder="לדוגמה: עד 22:30"></div> <div class="field"><label>כמה מסעדות/עצירות אוכל כבדות ביום?</label><select data-path="people.foodLoad"><option value="">בחר</option><option>עד אחת</option><option>עד שתיים</option><option>לא משנה</option></select></div> <div class="field full"><label>מגבלות / רגישויות / עומס / הליכה</label><textarea data-path="people.constraints" placeholder="לדוגמה: לא יותר מדי הליכה רצופה, רגישות לחום, ילדים אוהבים מתקנים, לא אוהבים מוזיאונים כבדים..."></textarea></div> </div> </section> <section class="card" id="route"> <h2>3. מסלול, אזורים ולינות</h2> <div class="form-grid"> <div class="field"><label>סגנון מסלול</label><select data-path="route.routeType"><option value="">בחר</option><option>עיר אחת כבסיס</option><option>שתי ערים</option><option>רב־עירוני</option><option>Road Trip</option><option>קרוז + יבשה</option><option>פתוח להמלצה</option></select></div> <div class="field"><label>כמה מעברי מלון מקסימום?</label><input data-path="route.maxHotelMoves" placeholder="לדוגמה: עד 3"></div> <div class="field"><label>רמת מלונות</label><select data-path="route.hotelLevel"><option value="">בחר</option><option>חסכוני טוב</option><option>ביניים איכותי</option><option>פרימיום</option><option>מלון עם יתרון תפעולי חשוב</option></select></div> <div class="field"><label>חדרים</label><input data-path="route.rooms" placeholder="לדוגמה: חדר משפחתי / 2 חדרים / סוויטה"></div> <div class="field full"><label>העדפות לינה</label><textarea data-path="route.hotelPreferences" placeholder="מיקום, חניה, כביסה, מטבחון, קרבה לרכבת/פארק, נוף, Premier/Express, שליחת מזוודות וכו׳"></textarea></div> </div> <div class="repeater" data-repeater="stays"> <div class="repeater-head"><div class="repeater-title">אזורים / לינות מתוכננות</div><button class="mini-btn" type="button" data-add="stays">+ הוסף אזור/לינה</button></div> <div class="rows"></div> </div> </section> <section class="card" id="flights"> <h2>4. טיסות, רכב ותחבורה</h2> <div class="form-grid"> <div class="field"><label>טיסות</label><select data-path="transport.flightStatus"><option value="">בחר</option><option>אין עדיין</option><option>יש אופציות</option><option>הוזמן</option><option>צריך המלצה</option></select></div> <div class="field"><label>תחבורה מרכזית</label><select data-path="transport.mainMode"><option value="">בחר</option><option>תחבורה ציבורית</option><option>רכב שכור</option><option>שילוב</option><option>קרוז</option><option>פתוח להמלצה</option></select></div> <div class="field"><label>מגבלת נהיגה יומית</label><input data-path="transport.maxDrivePerDay" placeholder="לדוגמה: עד 4 שעות, חריגים עד 6"></div> <div class="field"><label>מזוודות</label><input data-path="transport.luggage" placeholder="לדוגמה: 4 טרולי + 2 מזוודות גדולות"></div> <div class="field full"><label>כללי תחבורה חשובים</label><textarea data-path="transport.rules" placeholder="רכב חובה/אסור, חניה, כבישי אגרה, רכבות, שינוע מזוודות, החזרת רכב, שאטלים, מעבורות..."></textarea></div> </div> <div class="repeater" data-repeater="flights"> <div class="repeater-head"><div class="repeater-title">טיסות / מקטעים</div><button class="mini-btn" type="button" data-add="flights">+ הוסף טיסה/מקטע</button></div> <div class="rows"></div> </div> <div class="repeater" data-repeater="transportSegments"> <div class="repeater-head"><div class="repeater-title">מקטעי תחבורה יבשתיים חשובים</div><button class="mini-btn" type="button" data-add="transportSegments">+ הוסף מקטע</button></div> <div class="rows"></div> </div> </section> <section class="card" id="content"> <h2>5. תוכן חובה, לא לשלב ואילוצי ימים</h2> <div class="repeater" data-repeater="mustPlaces"> <div class="repeater-head"><div class="repeater-title">אטרקציות / מקומות חובה</div><button class="mini-btn" type="button" data-add="mustPlaces">+ הוסף מקום</button></div> <div class="rows"></div> </div> <div class="repeater" data-repeater="hardConstraints"> <div class="repeater-head"><div class="repeater-title">אילוצי ימים קשיחים</div><button class="mini-btn" type="button" data-add="hardConstraints">+ הוסף אילוץ</button></div> <div class="rows"></div> </div> <div class="form-grid"> <div class="field full"><label>לא לשלב / לא מעניין / להימנע</label><textarea data-path="content.noGo" placeholder="לדוגמה: לא מוזיאונים כבדים, לא ימי קניות ארוכים, לא נסיעות לילה, לא פארקים מסוימים..."></textarea></div> <div class="field full"><label>חלומות / העדפות פתוחות</label><textarea data-path="content.wishlist" placeholder="דברים שכיף לשלב אם מסתדר, אבל לא חובה."></textarea></div> </div> </section> <section class="card" id="parks"> <h2>6. פארקים, כרטיסים, Priority / Express</h2> <div class="form-grid"> <div class="field"><label>האם הטיול כולל פארקי שעשועים/מים?</label><select data-path="parks.hasParks"><option value="">בחר</option><option>כן</option><option>לא</option><option>אולי</option></select></div> <div class="field"><label>גישה לכרטיסי דילוג/Express</label><select data-path="parks.expressApproach"><option value="">בחר</option><option>חובה כשאפשר</option><option>רק למתקנים מרכזיים</option><option>לפי מחיר</option><option>לא רלוונטי</option></select></div> <div class="field full"><label>אסטרטגיית פארקים כללית</label><textarea data-path="parks.strategyNotes" placeholder="מתקנים חובה, מתקני אקסטרים, סימולטורים, ילדים, מים, הפסקות, פארק ביום גשם, שעות פתיחה, Priority וכו׳"></textarea></div> </div> <div class="repeater" data-repeater="parksList"> <div class="repeater-head"><div class="repeater-title">פארקים / ימים מיוחדים</div><button class="mini-btn" type="button" data-add="parksList">+ הוסף פארק</button></div> <div class="rows"></div> </div> </section> <section class="card" id="budget"> <h2>7. תקציב, prepaid, מטבעות ושערים</h2> <div class="form-grid"> <div class="field"><label>תקציב כולל</label><input type="number" data-path="budget.total" placeholder="לדוגמה: 45000"></div> <div class="field"><label>מטבע תקציב ראשי</label><select data-path="budget.primaryCurrency"><option value="">בחר</option><option>ILS</option><option>USD</option><option>EUR</option><option>JPY</option><option>GBP</option></select></div> <div class="field"><label>תקציב קניות נפרד</label><input type="number" data-path="budget.shopping" placeholder="לדוגמה: 10000"></div> <div class="field"><label>תקציב אוכל יומי</label><input data-path="budget.dailyFood" placeholder="לדוגמה: 260$ למשפחה / 800₪"></div> <div class="field"><label>רזרבה / בלת״ם</label><input data-path="budget.reserve" placeholder="לדוגמה: 10% / 5000₪"></div> <div class="field"><label>שער תכנון</label><input data-path="budget.planningRate" placeholder="לדוגמה: 1$=3.75₪"></div> <div class="field full"><label>מה התקציב כולל ומה לא?</label><textarea data-path="budget.scope" placeholder="טיסות, מלונות, רכב, ביטוח, קרוז, אוכל, קניות, אטרקציות, שולם מראש, לא נספר פעמיים..."></textarea></div> </div> <div class="repeater" data-repeater="prepaid"> <div class="repeater-head"><div class="repeater-title">שולם מראש / הוזמן / חייב לא להיספר פעמיים</div><button class="mini-btn" type="button" data-add="prepaid">+ הוסף שורה</button></div> <div class="rows"></div> </div> </section> <section class="card" id="food"> <h2>8. אוכל, מסעדות והעדפות</h2> <div class="form-grid"> <div class="field"><label>סגנון אוכל</label><input data-path="food.style" placeholder="לדוגמה: משפחתי, סושי, בשר, מסעדות קלילות, שווקים"></div> <div class="field"><label>הגבלות מזון</label><input data-path="food.restrictions" placeholder="כשרות/אלרגיות/לא אוכלים..."></div> <div class="field full"><label>כללי אוכל יומיים</label><textarea data-path="food.rules" placeholder="לא יותר משתי מסעדות כבדות, איפה חייבים להזמין, אוכל ליד פארקים, ארוחות בוקר במלון וכו׳"></textarea></div> </div> <div class="repeater" data-repeater="restaurants"> <div class="repeater-head"><div class="repeater-title">מסעדות / אוכל חובה / אופציות</div><button class="mini-btn" type="button" data-add="restaurants">+ הוסף מסעדה</button></div> <div class="rows"></div> </div> </section> <section class="card" id="design"> <h2>9. עיצוב, מנגנונים וקובץ HTML</h2> <div class="check-grid"> <label class="check-item"><input type="checkbox" data-path="design.hero">Hero פתיחה פרימיום</label> <label class="check-item"><input type="checkbox" data-path="design.loader">מסך טעינה מלא + דילוג מקומי</label> <label class="check-item"><input type="checkbox" data-path="design.toc">תוכן עניינים יומי צבעוני</label> <label class="check-item"><input type="checkbox" data-path="design.currentDay">כפתור קפיצה ליום נוכחי</label> <label class="check-item"><input type="checkbox" data-path="design.hideMode">מצב הסתר/מובייל</label> <label class="check-item"><input type="checkbox" data-path="design.editMode">מצב עריכת תקציב</label> <label class="check-item"><input type="checkbox" data-path="design.smartSearch">חיפוש חכם בתוך הקובץ</label> <label class="check-item"><input type="checkbox" data-path="design.liveFx">שער מטבע עדכני מהרשת</label> <label class="check-item"><input type="checkbox" data-path="design.maps">מפות/תמונות/קישורי ניווט</label> <label class="check-item"><input type="checkbox" data-path="design.presentationButton">כפתור מצגת בראש הקובץ</label> <label class="check-item"><input type="checkbox" data-path="design.appendices">נספחי תפעול בסוף</label> </div> <div class="form-grid"> <div class="field"><label>קישור אפליקציית תקציב — Netlify</label><input data-path="design.budgetNetlifyUrl" placeholder="https://..."></div> <div class="field"><label>קישור אפליקציית תקציב — שרת פרטי</label><input data-path="design.budgetPrivateServerUrl" placeholder="http://... / https://..."></div> <div class="field"><label>רמת קובץ מבוקשת</label><select data-path="design.outputLevel"><option value="">בחר</option><option>טיוטה נוחה לקריאה</option><option>קובץ מלא</option><option>קובץ סופי ברמת יפן/ארה״ב</option><option>יותר טוב מיפן/ארה״ב — מקור אמת אחד וניקוי כפילויות</option></select></div> <div class="field"><label>משקל קובץ רצוי</label><select data-path="design.fileWeight"><option value="">בחר</option><option>לא חשוב</option><option>חשוב שיהיה קל ככל האפשר</option><option>עדיף איכות על משקל</option><option>לא לעבור גודל סביר לאייפון</option></select></div> <div class="field full"><label>הערות עיצוב / UX</label><textarea data-path="design.notes" placeholder="צבעים, מבנה, אילו חלקים מהקבצים הישנים אהבת, מה הפריע, מה להבליט במובייל..."></textarea></div> </div> </section> <section class="card" id="presentation"> <h2>10. מצגת טיול מסונכרנת</h2> <div class="check-grid"> <label class="check-item"><input type="checkbox" data-path="presentation.enabled"> להפיק מצגת HTML תואמת לקובץ הטיול</label> <label class="check-item"><input type="checkbox" data-path="presentation.linkFromTripHero"> כפתור מצגת בראש ההירו, גלוי בלי גלילה</label> <label class="check-item"><input type="checkbox" data-path="presentation.dailySlides"> שקף לכל יום/תחנת טיול לפי הלו״ז</label> <label class="check-item"><input type="checkbox" data-path="presentation.imagesMatchItinerary"> תמונות/קישורי תמונות חייבים להתאים ליום וללוז</label> <label class="check-item"><input type="checkbox" data-path="presentation.summaryTextPerDay"> מלל קצר ומסכם לכל יום, לא עומס טקסט</label> <label class="check-item"><input type="checkbox" data-path="presentation.musicEnabled"> לכלול שיר/מוזיקה למצגת</label> <label class="check-item"><input type="checkbox" data-path="presentation.audioSync"> סנכרון משך המצגת למשך השיר</label> <label class="check-item"><input type="checkbox" data-path="presentation.localFirstImages"> תמונות Local First עם fallback לרשת</label> <label class="check-item"><input type="checkbox" data-path="presentation.adminPanelSupport"> הפאנל החכם חייב לכלול העלאת מצגת/תמונות/שיר</label> <label class="check-item"><input type="checkbox" data-path="presentation.cleanFinalNoWorkNotes"> מצגת וקובץ טיול ללא הסברי תיקונים/עדכונים</label> </div> <div class="form-grid"> <div class="field"><label>קוד תיקייה/Route בשרת</label><input data-path="presentation.tripCode" placeholder="לדוגמה: USA / JAPAN / EUROPE_2028"></div> <div class="field"><label>שם קובץ מצגת בשרת</label><input data-path="presentation.presentationFilename" placeholder="לדוגמה: trip-presentation.html / usa2027-presentation.html"></div> <div class="field"><label>נתיב תמונות למצגת</label><input data-path="presentation.imagesFolder" placeholder="media/trip-presentation/images"></div> <div class="field"><label>שם קובץ שיר</label><input data-path="presentation.musicFilename" placeholder="לדוגמה: trip-theme.mp3"></div> <div class="field"><label>סגנון מוזיקה</label><input data-path="presentation.musicStyle" placeholder="מרגש / משפחתי / כביש אמריקאי / יפן / טבע / קצבי"></div> <div class="field"><label>מקור השיר</label><select data-path="presentation.musicSource"><option value="">בחר</option><option>המשתמש יעלה MP3</option><option>לבקש המלצת שיר/מקור חופשי</option><option>בלי שיר כרגע, להשאיר מנגנון מוכן</option></select></div> <div class="field"><label>מבנה שקפים רצוי</label><select data-path="presentation.slideStructure"><option value="">בחר</option><option>Intro + תקציב + שקף לכל יום + סיכום</option><option>רק ימי טיול מרכזיים</option><option>שקפים לפי אזורים ולא לפי ימים</option><option>להחליט לפי הלוז והאופי של הטיול</option></select></div> <div class="field"><label>יחס תצוגה</label><select data-path="presentation.aspectRatio"><option value="">בחר</option><option>16:9 פרימיום</option><option>מותאם מובייל קודם</option><option>גם 16:9 וגם מובייל</option></select></div> <div class="field full"><label>הוראות למצגת</label><textarea data-path="presentation.notes" placeholder="אווירה רצויה, מה להבליט, מה לא להראות, טון כתיבה, תמונות חובה, סיום, כפתור חזרה לקובץ הטיול וכו׳"></textarea></div> </div> </section> <section class="card" id="deliver"> <h2>11. בדיקות איתן, מסירה וסיכום</h2> <div class="check-grid"> <label class="check-item"><input type="checkbox" data-path="tests.level1">בדיקות רמה 1 — קוד/מבנה</label> <label class="check-item"><input type="checkbox" data-path="tests.level2">בדיקות רמה 2 — DOM/לוגיקה/תקציב</label> <label class="check-item"><input type="checkbox" data-path="tests.level3">בדיקות רמה 3 — Chromium/Playwright מובייל</label> <label class="check-item"><input type="checkbox" data-path="tests.noHorizontalScroll">בדיקת אין גלילה אופקית</label> <label class="check-item"><input type="checkbox" data-path="tests.links">בדיקת קישורים/עוגנים/onClick</label> <label class="check-item"><input type="checkbox" data-path="tests.budget">בדיקת תקציב/prepaid/מטבעות</label> <label class="check-item"><input type="checkbox" data-path="tests.contentDup">בדיקת כפילויות תוכן</label> <label class="check-item"><input type="checkbox" data-path="tests.iphone">בדיקת מובייל 390×844</label> </div> <div class="form-grid"> <div class="field"><label>סוג מסירה</label><select data-path="deliver.mode"><option value="">בחר</option><option>תכנון בצ׳אט בלבד</option><option>מקור אמת JSON בלבד</option><option>HTML אחד לבדיקה</option><option>שתי גרסאות סופיות בלבד — Netlify ושרת פרטי</option></select></div> <div class="field"><label>האם להפיק דוח בדיקות נפרד?</label><select data-path="deliver.testReport"><option value="">בחר</option><option>לא, פירוט בצ׳אט בלבד</option><option>כן, לצרף דוח נפרד</option></select></div> <div class="field full"><label>הערות אחרונות לסוכן לפני התחלה</label><textarea data-path="deliver.finalInstructions" placeholder="הוראות קשיחות, דברים לא לשנות, מה לבדוק במיוחד, מה נחשב הצלחה..."></textarea></div> </div> <div class="action-bar"> <button class="action-btn" type="button" id="saveBtn">💾 שמירה מקומית</button> <button class="action-btn gold-btn" type="button" id="exportJsonBtn">📦 יצוא JSON להעלאה</button> <button class="action-btn" type="button" id="exportTxtBtn">📝 יצוא TXT מסודר</button> <button class="action-btn" type="button" id="exportHtmlBtn">🌐 יצוא HTML ממולא</button> <button class="action-btn secondary" type="button" id="copySummaryBtn">📋 העתק סיכום לצ׳אט</button> <button class="action-btn danger" type="button" id="clearBtn">🧹 ניקוי הטופס</button> </div> <div class="field full" style="margin-top:14px"> <label>ייבוא JSON קודם</label> <input type="file" id="importJsonInput" accept="application/json,.json"> <small>מאפשר לפתוח מחדש טיול שכבר מילאת ולעדכן אותו.</small> </div> <h3>תצוגת סיכום לסוכן</h3> <div class="preview" id="summaryPreview"></div> </section> </div> <div class="footer-floating" aria-label="פעולות מהירות"> <button class="action-btn" type="button" onclick="document.getElementById('saveBtn').click()">💾 שמור</button> <button class="action-btn gold-btn" type="button" onclick="document.getElementById('exportJsonBtn').click()">📦 JSON</button> <button class="action-btn secondary" type="button" onclick="location.hash='readiness'">📊 מוכנות</button> <button class="action-btn danger" type="button" onclick="location.hash='top'">⬆️ למעלה</button> </div> <script> (function(){ 'use strict'; const STORAGE_KEY = 'eitan_trip_intake_v4_trip_and_presentation'; function lsGet(key){ try{return window.localStorage.getItem(key);}catch(e){return null;} } function lsSet(key,value){ try{window.localStorage.setItem(key,value);return true;}catch(e){return false;} } function lsRemove(key){ try{window.localStorage.removeItem(key);return true;}catch(e){return false;} } const GENERATED_AT = '2026-06-27'; const referenceProfile = { gold_files_used:[ 'טיול יפן 2026 מחשב.html', 'טיול ארצות הברית 2027 מחשב.html' ], extracted_standard:{ rtl:true, mobile_first:true, premium_hero:true, daily_toc:true, day_cards:true, dynamic_budget:true, edit_mode:true, hide_mode:true, loader:true, local_skip_loader:true, live_fx:true, budget_app_links:true, route_maps:true, operational_appendices:true, linked_trip_presentation:true, presentation_images_music:true, smart_admin_panel_media:true, eitan_tests:true, final_versions:['Netlify','שרת פרטי'] } }; const repeaters = { participants:{ empty:{name:'',role:'',age:'',interests:'',constraints:''}, fields:[ ['name','שם','לדוגמה: איתן'], ['role','תפקיד/קרבה','מבוגר / ילד / בן זוג'], ['age','גיל בזמן הטיול',''], ['interests','תחומי עניין','מתקנים, שופינג, אוכל, טבע...'], ['constraints','מגבלות/רגישויות','הליכה, אוכל, פחד גבהים...','full'] ] }, stays:{ empty:{area:'',nights:'',hotelStatus:'',hotelName:'',mustReasons:'',notes:''}, fields:[ ['area','אזור/עיר','אמסטרדם / אורלנדו / טוקיו'], ['nights','לילות','3'], ['hotelStatus','סטטוס מלון','אין / יש אופציות / הוזמן'], ['hotelName','שם מלון אם ידוע',''], ['mustReasons','למה האזור/מלון חשוב','קרוב לפארק / רכבת / חניה','wide'], ['notes','הערות','חדרים, חניה, מזוודות, כביסה...','full'] ] }, flights:{ empty:{direction:'',date:'',from:'',to:'',time:'',airline:'',status:'',notes:''}, fields:[ ['direction','כיוון','הלוך / חזור / פנימי'], ['date','תאריך',''], ['from','מוצא','TLV'], ['to','יעד','AMS'], ['time','שעה',''], ['airline','חברה/מספר טיסה',''], ['status','סטטוס','נבדק / הוזמן / פתוח'], ['notes','הערות','כבודה, מושבים, נחיתה מאוחרת...','full'] ] }, transportSegments:{ empty:{from:'',to:'',dateOrDay:'',mode:'',durationLimit:'',bookingStatus:'',notes:''}, fields:[ ['from','מ־',''], ['to','אל',''], ['dateOrDay','תאריך/יום',''], ['mode','אמצעי','רכב / רכבת / טיסה / שאטל'], ['durationLimit','מגבלה','עד 4 שעות נהיגה'], ['bookingStatus','סטטוס','פתוח / הוזמן'], ['notes','הערות','חניה, אגרה, מזוודות, כבישים...','full'] ] }, mustPlaces:{ empty:{name:'',city:'',type:'',priority:'',fixedDate:'',prepaid:'',links:'',notes:''}, fields:[ ['name','שם מקום/אטרקציה',''], ['city','אזור/עיר',''], ['type','סוג','פארק / מוזיאון / שוק / טבע'], ['priority','עדיפות','חובה / חשוב / אופציה'], ['fixedDate','תאריך קשיח אם יש',''], ['prepaid','שולם/צריך להזמין','שולם / להזמין / לא'], ['links','קישורים','אתר/מפות/כרטיסים','wide'], ['notes','הערות לסוכן','שעות, עומסים, מי רוצה, למה חשוב','full'] ] }, hardConstraints:{ empty:{dateOrDay:'',constraint:'',reason:'',flexibility:''}, fields:[ ['dateOrDay','תאריך/יום',''], ['constraint','האילוץ','USJ ביום 10 / קרוז בשבת'], ['reason','סיבה','הזמנה / מחיר / לוגיסטיקה'], ['flexibility','גמישות','קשיח / אפשר להזיז / לבדיקה'] ] }, parksList:{ empty:{name:'',date:'',tickets:'',express:'',mustRides:'',avoid:'',notes:''}, fields:[ ['name','שם פארק',''], ['date','תאריך/יום',''], ['tickets','כרטיסים','אין / הוזמן / צריך המלצה'], ['express','Express/Priority','חובה / אולי / לא'], ['mustRides','מתקנים חובה','רשימה','wide'], ['avoid','לא מעניין','מתקנים/אזורים לוותר','wide'], ['notes','אסטרטגיה','פתיחה, עומסים, גשם, הפסקות','full'] ] }, prepaid:{ empty:{item:'',category:'',amount:'',currency:'',paid:'',date:'',proof:'',notes:''}, fields:[ ['item','פריט','טיסות / מלון / כרטיסים'], ['category','קטגוריה','טיסות / מלונות / אטרקציות'], ['amount','סכום',''], ['currency','מטבע','ILS/USD/EUR/JPY'], ['paid','סטטוס','שולם / הוזמן / פיקדון'], ['date','תאריך תשלום',''], ['proof','אסמכתא/קישור',''], ['notes','הערות','לא לספור פעמיים / כלול בתקציב','full'] ] }, restaurants:{ empty:{name:'',city:'',type:'',priority:'',reservation:'',link:'',notes:''}, fields:[ ['name','שם',''], ['city','עיר/אזור',''], ['type','סוג','בשר / סושי / שוק / מתוק'], ['priority','עדיפות','חובה / אופציה'], ['reservation','הזמנה','צריך / לא / הוזמן'], ['link','קישור',''], ['notes','הערות','מתי לשלב, רמת עומס, מחיר','full'] ] } }; function emptyData(){ return { meta:{schema:'EitanTripIntakeV4TripAndPresentation', generatedAt:GENERATED_AT, referenceProfile}, reference:{primaryStyle:'',designFaithfulness:'',keep:'',improve:''}, basics:{destination:'',tripName:'',startDate:'',endDate:'',status:'',goal:'',freeNotes:''}, people:{pace:'',startTimePreference:'',returnTimePreference:'',foodLoad:'',constraints:''}, route:{routeType:'',maxHotelMoves:'',hotelLevel:'',rooms:'',hotelPreferences:''}, transport:{flightStatus:'',mainMode:'',maxDrivePerDay:'',luggage:'',rules:''}, content:{noGo:'',wishlist:''}, parks:{hasParks:'',expressApproach:'',strategyNotes:''}, budget:{total:'',primaryCurrency:'',shopping:'',dailyFood:'',reserve:'',planningRate:'',scope:''}, food:{style:'',restrictions:'',rules:''}, design:{hero:true,loader:true,toc:true,currentDay:true,hideMode:true,editMode:true,smartSearch:true,liveFx:true,maps:true,presentationButton:true,appendices:true,budgetNetlifyUrl:'',budgetPrivateServerUrl:'',outputLevel:'',fileWeight:'',notes:''}, presentation:{enabled:true,linkFromTripHero:true,dailySlides:true,imagesMatchItinerary:true,summaryTextPerDay:true,musicEnabled:true,audioSync:true,localFirstImages:true,adminPanelSupport:true,cleanFinalNoWorkNotes:true,tripCode:'',presentationFilename:'trip-presentation.html',imagesFolder:'media/trip-presentation/images',musicFilename:'trip-theme.mp3',musicStyle:'',musicSource:'',slideStructure:'Intro + תקציב + שקף לכל יום + סיכום',aspectRatio:'גם 16:9 וגם מובייל',notes:''}, tests:{level1:true,level2:true,level3:true,noHorizontalScroll:true,links:true,budget:true,contentDup:true,iphone:true}, deliver:{mode:'',testReport:'לא, פירוט בצ׳אט בלבד',finalInstructions:''}, participants:[ {name:'איתן',role:'מבוגר',age:'',interests:'תכנון, תקציב, לוגיקה, איכות קובץ',constraints:''}, {name:'קרין',role:'מבוגרת',age:'',interests:'',constraints:''}, {name:'עידו',role:'ילד/נער',age:'',interests:'',constraints:''}, {name:'מיכאל',role:'ילד',age:'',interests:'',constraints:''} ], stays:[], flights:[], transportSegments:[], mustPlaces:[], hardConstraints:[], parksList:[], prepaid:[], restaurants:[] }; } let data = emptyData(); function getPath(obj,path){ return path.split('.').reduce((o,k)=>o && o[k], obj); } function setPath(obj,path,value){ const parts=path.split('.'); let cur=obj; for(let i=0;i<parts.length-1;i++){ if(!cur[parts[i]] || typeof cur[parts[i]]!=='object') cur[parts[i]]={}; cur=cur[parts[i]]; } cur[parts[parts.length-1]]=value; } function rowField(field,rowIndex,listName,value){ const [key,label,placeholder,cls]=field; const wrap=document.createElement('div'); wrap.className='field '+(cls||''); const id=`${listName}_${rowIndex}_${key}`; wrap.innerHTML=`<label for="${id}">${label}</label><input id="${id}" data-list="${listName}" data-row="${rowIndex}" data-key="${key}" placeholder="${placeholder||''}">`; wrap.querySelector('input').value=value||''; return wrap; } function renderRepeater(listName){ const cfg=repeaters[listName]; const root=document.querySelector(`[data-repeater="${listName}"] .rows`); if(!root) return; root.innerHTML=''; const arr=Array.isArray(data[listName])?data[listName]:[]; arr.forEach((row,idx)=>{ const div=document.createElement('div'); div.className='row'; div.dataset.list=listName; div.dataset.row=idx; const remove=document.createElement('button'); remove.type='button'; remove.className='mini-btn remove-btn'; remove.textContent='מחק'; remove.addEventListener('click',()=>{ data[listName].splice(idx,1); renderRepeater(listName); syncInputsFromData(); updateAll(); }); div.appendChild(remove); cfg.fields.forEach(f=>div.appendChild(rowField(f,idx,listName,row[f[0]]))); root.appendChild(div); }); } function renderAllRepeaters(){ Object.keys(repeaters).forEach(renderRepeater); } function syncInputsFromData(){ document.querySelectorAll('[data-path]').forEach(el=>{ const val=getPath(data,el.dataset.path); if(el.type==='checkbox') el.checked=!!val; else el.value=val ?? ''; }); } function collectFromDom(){ document.querySelectorAll('[data-path]').forEach(el=>{ setPath(data,el.dataset.path, el.type==='checkbox' ? el.checked : el.value); }); Object.keys(repeaters).forEach(list=>{ const rows=[]; const rowEls=[...document.querySelectorAll(`.row[data-list="${list}"]`)]; rowEls.forEach(rowEl=>{ const obj={}; rowEl.querySelectorAll('[data-key]').forEach(inp=>obj[inp.dataset.key]=inp.value); rows.push(obj); }); data[list]=rows; }); data.meta.updatedAt=new Date().toISOString(); data.meta.referenceProfile=referenceProfile; return data; } function save(){ collectFromDom(); lsSet(STORAGE_KEY, JSON.stringify(data)); updateAll(); } function load(){ const raw=lsGet(STORAGE_KEY); if(raw){ try{ data = Object.assign(emptyData(), JSON.parse(raw)); } catch(e){ data=emptyData(); } } else data=emptyData(); // deep defaults for missing objects const def=emptyData(); data={...def,...data}; ['meta','reference','basics','people','route','transport','content','parks','budget','food','design','presentation','tests','deliver'].forEach(k=>data[k]={...def[k],...(data[k]||{})}); Object.keys(repeaters).forEach(k=>{ if(!Array.isArray(data[k])) data[k]=def[k]||[]; }); renderAllRepeaters(); syncInputsFromData(); updateAll(); } function duration(){ const s=data.basics.startDate, e=data.basics.endDate; if(!s || !e) return null; const sd=new Date(s+'T00:00:00'), ed=new Date(e+'T00:00:00'); if(isNaN(sd)||isNaN(ed)||ed<sd) return null; const days=Math.round((ed-sd)/(1000*60*60*24))+1; return {days,nights:Math.max(0,days-1)}; } function missingChecks(){ collectFromDom(); const m=[]; function need(condition,text){ if(!condition) m.push(text); } need(data.basics.destination,'יעד הטיול'); need(data.basics.startDate && data.basics.endDate,'תאריכי התחלה וסיום'); need(data.basics.goal,'מטרת העבודה / רמת התוצר'); need(data.reference.primaryStyle,'בחירת קובץ/סגנון ייחוס'); need(data.reference.designFaithfulness,'רמת נאמנות לעיצוב הישן'); need((data.participants||[]).some(p=>p.name),'לפחות משתתף אחד'); need(data.people.pace,'קצב טיול רצוי'); need(data.route.routeType,'סוג מסלול/אופי חלוקת אזורים'); need(data.transport.mainMode,'תחבורה מרכזית'); need(data.budget.total && data.budget.primaryCurrency,'תקציב כולל ומטבע'); need(data.budget.scope,'מה התקציב כולל ומה לא'); need((data.mustPlaces||[]).some(x=>x.name) || data.content.wishlist,'מקומות חובה או לפחות Wishlist'); need(data.content.noGo,'מה לא לשלב / ממה להימנע'); need(data.design.outputLevel,'רמת קובץ HTML מבוקשת'); need(data.presentation.enabled,'החלטה/אישור להפקת מצגת טיול מסונכרנת'); if(data.presentation.enabled){ need(data.presentation.presentationFilename,'שם קובץ מצגת בשרת'); need(data.presentation.imagesFolder,'נתיב תמונות למצגת'); need(data.presentation.musicFilename || data.presentation.musicSource,'שיר/מוזיקה למצגת או החלטה להשאיר מנגנון מוכן'); need(data.presentation.adminPanelSupport,'אישור שהפאנל החכם יכלול מצגת/תמונות/שיר'); } need(data.deliver.mode,'סוג מסירה סופית'); need(data.design.budgetNetlifyUrl || data.design.budgetPrivateServerUrl,'קישור לאפליקציית תקציב או החלטה שאין עדיין'); need(data.tests.level1 && data.tests.level2,'אישור בדיקות איתן רמה 1+2'); return m; } function score(){ const total=22; const miss=missingChecks().length; return Math.max(0,Math.round((total-miss)/total*100)); } function summaryText(){ collectFromDom(); const dur=duration(); const lines=[]; lines.push('פתח טיול חדש לפי סוכן הטיולים של איתן — v4 Trip + Presentation Reference'); lines.push(''); lines.push('מקור ייחוס: '+(data.reference.primaryStyle||'לא צוין')); lines.push('רמת נאמנות לעיצוב: '+(data.reference.designFaithfulness||'לא צוין')); lines.push('יעד: '+(data.basics.destination||'')); lines.push('שם טיול: '+(data.basics.tripName||'')); lines.push('תאריכים: '+(data.basics.startDate||'')+' עד '+(data.basics.endDate||'')+(dur?` (${dur.days} ימים / ${dur.nights} לילות)`:'')); lines.push('מטרה: '+(data.basics.goal||'')); lines.push('סטטוס: '+(data.basics.status||'')); lines.push(''); lines.push('משתתפים:'); (data.participants||[]).filter(p=>p.name).forEach(p=>lines.push(`- ${p.name} | ${p.role||''} | גיל: ${p.age||''} | עניין: ${p.interests||''} | מגבלות: ${p.constraints||''}`)); lines.push(''); lines.push('קצב וכללים:'); lines.push('- קצב: '+(data.people.pace||'')); lines.push('- יציאה בבוקר: '+(data.people.startTimePreference||'')); lines.push('- חזרה למלון: '+(data.people.returnTimePreference||'')); lines.push('- מגבלות: '+(data.people.constraints||'')); lines.push(''); lines.push('מסלול ולינות:'); lines.push('- סוג מסלול: '+(data.route.routeType||'')); lines.push('- מעברי מלון מקסימום: '+(data.route.maxHotelMoves||'')); lines.push('- רמת מלונות: '+(data.route.hotelLevel||'')); lines.push('- חדרים: '+(data.route.rooms||'')); (data.stays||[]).filter(x=>x.area||x.hotelName).forEach(x=>lines.push(`- ${x.area||''}: ${x.nights||''} לילות | ${x.hotelStatus||''} | ${x.hotelName||''} | ${x.notes||''}`)); lines.push(''); lines.push('תחבורה:'); lines.push('- טיסות: '+(data.transport.flightStatus||'')); lines.push('- אמצעי מרכזי: '+(data.transport.mainMode||'')); lines.push('- מגבלת נהיגה: '+(data.transport.maxDrivePerDay||'')); lines.push('- מזוודות: '+(data.transport.luggage||'')); lines.push(''); lines.push('חובה לשלב:'); (data.mustPlaces||[]).filter(x=>x.name).forEach(x=>lines.push(`- ${x.name} | ${x.city||''} | ${x.priority||''} | תאריך: ${x.fixedDate||''} | ${x.notes||''}`)); lines.push(''); lines.push('לא לשלב: '+(data.content.noGo||'')); lines.push('Wishlist: '+(data.content.wishlist||'')); lines.push(''); lines.push('תקציב:'); lines.push(`- כולל: ${data.budget.total||''} ${data.budget.primaryCurrency||''}`); lines.push('- קניות: '+(data.budget.shopping||'')); lines.push('- אוכל יומי: '+(data.budget.dailyFood||'')); lines.push('- רזרבה: '+(data.budget.reserve||'')); lines.push('- שער תכנון: '+(data.budget.planningRate||'')); lines.push('- כולל/לא כולל: '+(data.budget.scope||'')); lines.push(''); lines.push('Prepaid / הוזמן:'); (data.prepaid||[]).filter(x=>x.item).forEach(x=>lines.push(`- ${x.item} | ${x.category||''} | ${x.amount||''} ${x.currency||''} | ${x.paid||''} | ${x.notes||''}`)); lines.push(''); lines.push('אוכל:'); lines.push('- סגנון: '+(data.food.style||'')); lines.push('- הגבלות: '+(data.food.restrictions||'')); lines.push('- כללים: '+(data.food.rules||'')); lines.push(''); lines.push('מצגת טיול:'); lines.push('- להפיק מצגת: '+(data.presentation.enabled?'כן':'לא')); lines.push('- כפתור מצגת בראש ההירו: '+(data.presentation.linkFromTripHero?'כן':'לא')); lines.push('- מבנה שקפים: '+(data.presentation.slideStructure||'')); lines.push('- קוד תיקייה/Route: '+(data.presentation.tripCode||'')); lines.push('- קובץ מצגת: '+(data.presentation.presentationFilename||'')); lines.push('- נתיב תמונות: '+(data.presentation.imagesFolder||'')); lines.push('- שיר/קובץ מוזיקה: '+(data.presentation.musicFilename||'')); lines.push('- סגנון מוזיקה: '+(data.presentation.musicStyle||'')); lines.push('- מקור השיר: '+(data.presentation.musicSource||'')); lines.push('- תמונות תואמות לוז: '+(data.presentation.imagesMatchItinerary?'כן':'לא')); lines.push('- סנכרון לשיר: '+(data.presentation.audioSync?'כן':'לא')); lines.push('- תמיכה בפאנל חכם: '+(data.presentation.adminPanelSupport?'כן':'לא')); lines.push('- הערות מצגת: '+(data.presentation.notes||'')); lines.push(''); lines.push('דרישות קובץ:'); lines.push('- רמת פלט: '+(data.design.outputLevel||'')); lines.push('- Netlify budget URL: '+(data.design.budgetNetlifyUrl||'')); lines.push('- Private budget URL: '+(data.design.budgetPrivateServerUrl||'')); lines.push('- מצב הסתר: '+(data.design.hideMode?'כן':'לא')); lines.push('- עריכת תקציב: '+(data.design.editMode?'כן':'לא')); lines.push('- שער חי: '+(data.design.liveFx?'כן':'לא')); lines.push('- כפתור מצגת: '+(data.design.presentationButton?'כן':'לא')); lines.push('- בדיקות איתן: '+Object.keys(data.tests).filter(k=>data.tests[k]).join(', ')); lines.push('- מסירה: '+(data.deliver.mode||'')); lines.push(''); lines.push('מה לשמר מהקבצים הישנים: '+(data.reference.keep||'')); lines.push('מה לשפר: '+(data.reference.improve||'')); lines.push('הוראות אחרונות: '+(data.deliver.finalInstructions||'')); return lines.join('\n'); } function updateAll(){ collectFromDom(); const sc=score(); document.getElementById('scoreText').textContent=sc+'%'; document.getElementById('progressBar').style.width=sc+'%'; const dur=duration(); document.getElementById('durationText').textContent=dur?`${dur.days} ימים / ${dur.nights} לילות`:'—'; document.getElementById('levelText').textContent=data.design.outputLevel||data.basics.goal||'—'; document.getElementById('sourceTruthText').textContent=sc>=85?'מספיק להתחלה חזקה':(sc>=55?'בסיסי, חסרים פרטים':'חסר'); const ul=document.getElementById('missingList'); const miss=missingChecks(); ul.innerHTML=miss.length?miss.map(x=>`<li>${x}</li>`).join(''):'<li>אין חוסרים קריטיים. אפשר להעלות JSON ולהתחיל עבודה.</li>'; document.getElementById('summaryPreview').textContent=summaryText(); } function download(filename,content,mime){ const blob=new Blob([content],{type:mime||'text/plain;charset=utf-8'}); const a=document.createElement('a'); a.href=URL.createObjectURL(blob); a.download=filename; document.body.appendChild(a); a.click(); setTimeout(()=>{URL.revokeObjectURL(a.href);a.remove();},200); } function safeName(){ const dest=(data.basics.destination||'trip').replace(/[^\u0590-\u05FFA-Za-z0-9_-]+/g,'_').replace(/^_+|_+$/g,''); return dest || 'trip'; } document.addEventListener('input', e=>{ if(e.target.matches('[data-path],[data-key]')){ collectFromDom(); updateAll(); lsSet(STORAGE_KEY, JSON.stringify(data)); } }); document.addEventListener('change', e=>{ if(e.target.matches('[data-path],[data-key]')){ collectFromDom(); updateAll(); lsSet(STORAGE_KEY, JSON.stringify(data)); } }); document.querySelectorAll('[data-add]').forEach(btn=>{ btn.addEventListener('click',()=>{ const list=btn.dataset.add; collectFromDom(); data[list]=Array.isArray(data[list])?data[list]:[]; data[list].push({...repeaters[list].empty}); renderRepeater(list); syncInputsFromData(); updateAll(); }); }); document.getElementById('saveBtn').addEventListener('click',()=>{save(); alert('נשמר מקומית בדפדפן.');}); document.getElementById('exportJsonBtn').addEventListener('click',()=>{ collectFromDom(); download(`Eitan_Trip_Intake_${safeName()}_v4.json`, JSON.stringify(data,null,2), 'application/json;charset=utf-8'); }); document.getElementById('exportTxtBtn').addEventListener('click',()=>{ download(`Eitan_Trip_Intake_${safeName()}_summary.txt`, summaryText(), 'text/plain;charset=utf-8'); }); document.getElementById('exportHtmlBtn').addEventListener('click',()=>{ save(); const html='<!DOCTYPE html>\n'+document.documentElement.outerHTML; download(`Eitan_Trip_Intake_${safeName()}_filled.html`, html, 'text/html;charset=utf-8'); }); document.getElementById('copySummaryBtn').addEventListener('click',async()=>{ try{ await navigator.clipboard.writeText(summaryText()); alert('הסיכום הועתק.'); } catch(e){ alert('לא הצלחתי להעתיק אוטומטית. אפשר לסמן ולהעתיק מהתצוגה למטה.'); } }); document.getElementById('clearBtn').addEventListener('click',()=>{ if(confirm('למחוק את כל הנתונים שנשמרו בטופס הזה?')){ lsRemove(STORAGE_KEY); data=emptyData(); renderAllRepeaters(); syncInputsFromData(); updateAll(); } }); document.getElementById('importJsonInput').addEventListener('change', async e=>{ const file=e.target.files && e.target.files[0]; if(!file) return; try{ const txt=await file.text(); const imported=JSON.parse(txt); data={...emptyData(),...imported}; renderAllRepeaters(); syncInputsFromData(); save(); updateAll(); alert('ה־JSON נטען לטופס.'); }catch(err){ alert('הקובץ לא נראה כמו JSON תקין.'); } }); load(); })(); </script> </body> </html>
שמור קובץ