const { useState, useEffect } = React; /* ========================================================= v2: ヘッダー / フッター ========================================================= */ function SiteHeader() { return (
アクアクララ千代田
お電話でのお申込み 0120-074-032
); } function SiteFooter() { return ( ); } /* ========================================================= v2: ヒーロー & プログレス ========================================================= */ function Hero() { return (

Application

お申込みフォーム

ご希望の構成をお選びください。所要時間は約3分です。
送信後、担当より確認のご連絡を差し上げます。

03 min 設置費・入会金 0
); } function Progress({ step }) { const steps = [ { n: 1, label: "プランを組む", sub: "Server / Bottle / Plan" }, { n: 2, label: "お客様情報", sub: "Contact" }, { n: 3, label: "完了", sub: "Done" }, ]; return ( ); } /* ========================================================= v2: Step 1 ========================================================= */ function StepOne({ state, set, onNext, errors }) { const { server, bottle, plan } = state; const sObj = SERVERS.find((s) => s.id === server); const bObj = BOTTLES.find((b) => b.id === bottle); const supportFee = getSupportMonthly(server, plan); const bottleFee = getWaterPrice(bottle, plan); const kosodate = plan === "子育てアクアプラン" ? 550 : 0; const monthly = supportFee !== null && bottleFee !== null ? supportFee + bottleFee * 4 - kosodate : null; const canProceed = server && bottle && plan; // ヘラルボニー選択時はプランを限定 const allowedPlanIds = isHeralbony(server) ? ["2年割プラン", "子育てアクアプラン"] : null; const visiblePlans = allowedPlanIds ? PLANS.filter((p) => allowedPlanIds.includes(p.id)) : PLANS; // 不可プランが選択中ならクリア useEffect(() => { if (allowedPlanIds && plan && !allowedPlanIds.includes(plan)) { set({ plan: null }); } }, [server]); return (
{SERVERS.map((item) => ( set({ server: item.id })} /> ))}
{errors.server &&

{errors.server}

}
{BOTTLES.map((item) => ( set({ bottle: item.id })} /> ))}
{errors.bottle &&

{errors.bottle}

}
{allowedPlanIds && (

ヘラルボニーデザインサーバーは「2年割プラン」「子育てアクアプラン」よりお選びいただけます。

)}
{visiblePlans.map((item) => ( set({ plan: item.id })} /> ))}
{errors.plan &&

{errors.plan}

}

あんしんサポート料

{supportFee !== null ? <>{yen(supportFee)}円 / 月 : }

{(plan === "2年割プラン" || plan === "子育てアクアプラン") && supportFee !== null && (

2年割プラン適用

)}

お水料金(1本)

{bottleFee !== null ? <>{yen(bottleFee)}円 / 本 : }

{(plan === "2年割プラン" || plan === "子育てアクアプラン") && bottleFee !== null && (

割引価格適用

)}

月額目安(4本/月想定)

{monthly !== null ? <>{yen(monthly)}円 / 月 : }

{plan === "子育てアクアプラン" && monthly !== null && (

子育て割 −{yen(550)}円

)}
{plan === "子育てアクアプラン" && (
子育てアクアプラン適用中は、合計金額より毎月 550円(税込) 引きとなります。
)} {!canProceed && (

サーバー・ボトル・プランをお選びいただくと単価が表示されます。

)}
{!canProceed &&

3項目すべてをご選択ください

}
); } /* ========================================================= v2: Step 2 ========================================================= */ function StepTwo({ state, set, onBack, onSubmit, initialErrors = {} }) { const [errors, setErrors] = useState(initialErrors); const [submitting, setSubmitting] = useState(false); const [zipLoading, setZipLoading] = useState(false); const [termsOpen, setTermsOpen] = useState(false); const sObj = SERVERS.find((s) => s.id === state.server); const bObj = BOTTLES.find((b) => b.id === state.bottle); const pObj = PLANS.find((p) => p.id === state.plan); const supportFee = getSupportMonthly(state.server, state.plan); const bottleFee = getWaterPrice(state.bottle, state.plan); const kosodate = state.plan === "子育てアクアプラン" ? 550 : 0; const fetchAddress = async (zip) => { if (zip.length !== 7) return; setZipLoading(true); try { const res = await fetch(`https://zipcloud.ibsnet.co.jp/api/search?zipcode=${zip}`); const data = await res.json(); if (data.results) { const r = data.results[0]; set({ address: `${r.address1}${r.address2}${r.address3}` }); } } catch {} finally { setZipLoading(false); } }; const validate = () => { const e = {}; if (!state.name) e.name = "お名前をご入力ください"; if (!state.kana) e.kana = "フリガナをご入力ください"; if (!state.tel) e.tel = "電話番号をご入力ください"; else if (!/^[0-9\-]{10,}$/.test(state.tel)) e.tel = "正しい電話番号をご入力ください"; if (!state.email) e.email = "メールアドレスをご入力ください"; else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(state.email)) e.email = "メール形式が正しくありません"; if (!state.contact_method) e.contact_method = "ご希望の連絡方法をお選びください"; if (!state.zipcode || state.zipcode.length !== 7) e.zipcode = "7桁でご入力ください"; if (!state.address) e.address = "住所をご入力ください"; else if (!/[0-90-9一二三四五六七八九十百千]/.test(state.address)) e.address = "番地(数字)までご入力ください"; if (!state.agree) e.agree = "規約にご同意ください"; return e; }; const handleSubmit = async (ev) => { ev.preventDefault(); const e = validate(); setErrors(e); if (Object.keys(e).length) { const firstKey = Object.keys(e)[0]; const el = document.querySelector(`[data-field="${firstKey}"]`); if (el) el.scrollIntoView({ behavior: "smooth", block: "center" }); return; } setSubmitting(true); const form = document.createElement('form'); form.method = 'POST'; form.action = 'submit.php'; const fields = { token: window.__CSRF_TOKEN || '', server: state.server || '', bottle_size: state.bottle || '', plan: state.plan || '', name: state.name || '', kana: state.kana || '', company: state.company || '', tel: state.tel || '', email: state.email || '', contact_method: state.contact_method|| '', zipcode: state.zipcode || '', address: state.address || '', building: state.building || '', agree: state.agree ? '1' : '', }; Object.entries(fields).forEach(([k, v]) => { const inp = document.createElement('input'); inp.type = 'hidden'; inp.name = k; inp.value = v; form.appendChild(inp); }); document.body.appendChild(form); sessionStorage.removeItem("apply_v2_state"); sessionStorage.removeItem("apply_v2_step"); form.submit(); }; return (
set({ name: e.target.value })} maxLength={50} />
set({ kana: e.target.value })} maxLength={80} />
set({ company: e.target.value })} maxLength={200} />
set({ tel: e.target.value })} maxLength={20} />
set({ email: e.target.value })} maxLength={254} />
{CONTACT_METHODS.map((v) => ( ))}
{ const v = e.target.value.replace(/[^0-9]/g, ""); set({ zipcode: v }); if (v.length === 7) fetchAddress(v); }} /> {zipLoading && }
set({ address: e.target.value })} maxLength={200} />
set({ building: e.target.value })} maxLength={200} />
{termsOpen && (
{TERMS_TEXT}
)}
{errors.agree &&

{errors.agree}

}

送信後の流れ:内容確認のうえ、担当よりご連絡します。
お電話:0120-074-032(平日 9:00–17:30)

); } /* ========================================================= v2: Done ========================================================= */ function Done({ state, onReset }) { const sObj = SERVERS.find((s) => s.id === state.server); const bObj = BOTTLES.find((b) => b.id === state.bottle); const pObj = PLANS.find((p) => p.id === state.plan); return (

Application Received

お申込みありがとうございます

{state.name && <>{state.name} 様、} 内容を確認のうえ、担当者よりご連絡いたします。
通常 1〜2 営業日以内にご連絡いたします。

お申込み内容

{sObj && (<>
サーバー
{sObj.name}
)} {bObj && (<>
ボトル
{bObj.name}
)} {pObj && (<>
プラン
{pObj.name}
)} {state.email && (<>
メール
{state.email}
)}

お電話でのお問い合わせ

0120-074-032

平日 9:00 – 17:30

); } /* ========================================================= v2: App ========================================================= */ const INITIAL = { server: null, bottle: null, plan: null, name: "", kana: "", company: "", tel: "", email: "", contact_method: "", zipcode: "", address: "", building: "", agree: false, }; function App() { const [step, setStep] = useState(1); const [state, setState] = useState(INITIAL); useEffect(() => { // PHPバリデーションエラー後の復元(submit.php → index.php リダイレクト時) const phpOld = window.__FORM_OLD || {}; if (Object.keys(phpOld).length > 0) { setState((s) => ({ ...s, server: phpOld.server || s.server, bottle: phpOld.bottle_size || s.bottle, plan: phpOld.plan || s.plan, name: phpOld.name || s.name, kana: phpOld.kana || s.kana, company: phpOld.company || s.company, tel: phpOld.tel || s.tel, email: phpOld.email || s.email, contact_method: phpOld.contact_method || s.contact_method, zipcode: phpOld.zipcode || s.zipcode, address: phpOld.address || s.address, building: phpOld.building || s.building, agree: phpOld.agree === '1', })); setStep(window.__INIT_STEP || 1); return; // localStorage 復元はスキップ } try { const saved = JSON.parse(sessionStorage.getItem("apply_v2_state") || "null"); const savedStep = parseInt(sessionStorage.getItem("apply_v2_step") || "1", 10); if (saved) setState((s) => ({ ...s, ...saved })); if (savedStep) setStep(savedStep); } catch {} }, []); useEffect(() => { try { sessionStorage.setItem("apply_v2_state", JSON.stringify(state)); sessionStorage.setItem("apply_v2_step", String(step)); } catch {} }, [state, step]); const set = (patch) => setState((s) => ({ ...s, ...patch })); const gotoStep = (n) => { setStep(n); window.scrollTo({ top: 0, behavior: "smooth" }); }; const handleReset = () => { setState(INITIAL); gotoStep(1); }; return (
{step < 3 && } {step < 3 && } {step === 1 && gotoStep(2)} errors={{}} />} {step === 2 && gotoStep(1)} onSubmit={() => gotoStep(3)} initialErrors={window.__FORM_ERRORS || {}} />} {step === 3 && }
); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render();