/* EntryForm — 採用エントリー 送信先: 同ディレクトリの api/entry.php → 採用担当へメール通知 + data/entries.csv へ自動記録 設定は api/config.php の上部2行(宛先メール・送信元メール)のみ。 ※ PHPが動かない環境(Vercelプレビュー等)では自動的にデモ動作になります。 */ const ENTRY_ENDPOINT = "api/entry.php"; const RECAPTCHA_SITE_KEY = "6LcTJL0tAAAAAMYKuslQ8K-bOBRmOWQYwaAQvhbS"; // 14日分の候補日を生成 const buildDateOptions = () => { const today = new Date(); const arr = []; const dow = ['日','月','火','水','木','金','土']; for (let i = 1; i <= 14; i++) { const d = new Date(today); d.setDate(today.getDate() + i); arr.push({ key: `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`, m: d.getMonth() + 1, d: d.getDate(), dow: dow[d.getDay()], isWeekend: d.getDay() === 0 || d.getDay() === 6, }); } return arr; }; const TIME_SLOTS = ['09:00','10:00','11:00','12:00','13:00','14:00','15:00','16:00','17:00','18:00']; const EntryForm = () => { const [submitted, setSubmitted] = React.useState(false); const [submitting, setSubmitting] = React.useState(false); const [error, setError] = React.useState(''); const [recaptchaToken, setRecaptchaToken] = React.useState(''); const [honeypot, setHoneypot] = React.useState(''); // ボット対策(人には見えない項目) const [activeDate, setActiveDate] = React.useState(null); const dateOptions = React.useMemo(buildDateOptions, []); const recaptchaBoxRef = React.useRef(null); const recaptchaWidgetIdRef = React.useRef(null); const [form, setForm] = React.useState({ lastName: '', firstName: '', lastKana: '', firstKana: '', birth: '', gender: '', email: '', tel: '', zip: '', address: '', license: '', interviewSlots: [], career: '', motivation: '', selfPr: '', referral: '', agree: false, }); const update = (k, v) => setForm(s => ({ ...s, [k]: v })); const toggleSlot = (key) => setForm(s => ({ ...s, interviewSlots: s.interviewSlots.includes(key) ? s.interviewSlots.filter(x => x !== key) : [...s.interviewSlots, key], })); const removeSlot = (key) => setForm(s => ({ ...s, interviewSlots: s.interviewSlots.filter(x => x !== key) })); // reCAPTCHAスクリプトは非同期読込のため、Reactが
を描画する前に // 暗黙的レンダリング(class="g-recaptcha"の自動スキャン)が走ってしまい // チェックボックスが表示されないことがある。マウント後に明示的に // grecaptcha.render() を呼び出すことで確実に描画する。 React.useEffect(() => { if (RECAPTCHA_SITE_KEY.includes('REPLACE_')) return; let cancelled = false; const tryRender = () => { if (cancelled || recaptchaWidgetIdRef.current !== null) return; if (window.grecaptcha && window.grecaptcha.render && recaptchaBoxRef.current) { recaptchaWidgetIdRef.current = window.grecaptcha.render(recaptchaBoxRef.current, { sitekey: RECAPTCHA_SITE_KEY, callback: (token) => setRecaptchaToken(token), 'expired-callback': () => setRecaptchaToken(''), }); } else { setTimeout(tryRender, 300); } }; tryRender(); return () => { cancelled = true; }; }, []); const onSubmit = async (e) => { e.preventDefault(); setError(''); if (!form.agree) { setError('個人情報の取扱に同意してください。'); return; } if (!RECAPTCHA_SITE_KEY.includes('REPLACE_') && !recaptchaToken) { setError('reCAPTCHA認証を完了してください。'); return; } setSubmitting(true); try { const payload = { ...form, website: honeypot, recaptchaToken }; const res = await fetch(ENTRY_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json;charset=utf-8' }, body: JSON.stringify(payload), }); // PHPが配置されていない環境(Vercelプレビュー等)はデモ動作にする if (res.status === 404 || res.status === 405) { console.warn('[EntryForm] api/entry.php が見つかりません(デモ表示):', payload); setSubmitted(true); return; } let data = null; try { data = await res.json(); } catch (_) { /* JSON以外が返った場合 */ } if (res.ok && data && data.ok) { setSubmitted(true); } else { setError( (data && data.message) || '送信に失敗しました。お手数ですが、お電話(0120-444-317)にてご連絡ください。' ); } } catch (err) { setError('通信エラーが発生しました。電波状況をご確認のうえ、再度お試しください。'); } finally { setSubmitting(false); } }; if (submitted) { return (
✓

エントリーありがとうございました

ご記入いただいた内容を確認のうえ、採用担当よりご連絡いたします。
お急ぎの場合は、下記までお気軽にお電話ください。

採用・お問い合わせ フリーダイヤル 0120-444-317 受付:24時間365日
); } return (
07 ENTRY

応募フォーム

松村タクシーの採用にご興味をお持ちいただきありがとうございます。
下記フォームよりご記入ください。採用担当より、面談日程のご連絡を差し上げます。
お急ぎの方は 0120-444-317(24時間365日)へお電話ください。

{/* ボット対策:人には見えない項目。ここに入力があると送信を破棄します */} {/* === 01 基本情報 === */}
01 基本情報
update('lastName', e.target.value)} placeholder="松村" autoComplete="family-name"/>
update('firstName', e.target.value)} placeholder="太郎" autoComplete="given-name"/>
update('lastKana', e.target.value)} placeholder="マツムラ"/>
update('firstKana', e.target.value)} placeholder="タロウ"/>
update('birth', e.target.value)}/>
{['女性','男性','回答しない'].map(g => ( ))}
{/* === 02 ご連絡先 === */}
02 ご連絡先
update('email', e.target.value)} placeholder="example@email.com" autoComplete="email"/>
update('tel', e.target.value)} placeholder="090-1234-5678" autoComplete="tel"/>
update('zip', e.target.value)} placeholder="861-5522" autoComplete="postal-code" inputMode="numeric"/>
update('address', e.target.value)} placeholder="熊本県熊本市〇〇区〇〇 1-2-3" autoComplete="street-address"/>
※ 未取得でも、取得についてご相談いただけます
{/* === 03 経歴・志望動機・自己PR === */}
03 あなたについて教えてください