// Main content sections: Business, Brands, CEO, Careers, Contact, Company, Footer
// Exposes to window.
const { useEffect: _useEffect, useState: _useState, useRef: _useRef } = React;
// ---------- Section head ----------
function SectionHead({ eyebrow, title, meta, dark }) {
return (
{eyebrow}
{window.renderTokens(title)}
{meta &&
{meta}
}
);
}
// ---------- News / お知らせ ----------
function NewsSection({ content }) {
const raw = content.news;
if (!raw) return null;
const items = Array.isArray(raw) ? raw : [raw]; // 旧形式(単一オブジェクト)も許容
if (items.length === 0) return null;
return (
{items.map((c, i) => {
const Tag = c.url ? "a" : "div";
const linkProps = c.url
? { href: c.url, target: "_blank", rel: "noopener noreferrer" }
: {};
return (
{c.badge}
{c.date}
{c.title}
{c.body &&
{c.body}
}
{(c.url || c.note) && (
{c.url && (
{c.urlLabel}↗
)}
{c.note && {c.note}}
)}
);
})}
);
}
// ---------- Business ----------
function BusinessSection({ content }) {
const c = content.business;
return (
{c.cards.map((card) => (
{card.num}
{card.title}
{card.body}
{card.tags.map((t) => {t})}
{card.url && (
{card.urlLabel || "詳しく見る"}→
)}
))}
);
}
function BizIcon({ n }) {
// simple iconography: shapes (no realistic SVGs)
if (n === "04") return (
);
if (n === "01") return (
);
if (n === "02") return (
);
return (
);
}
// ---------- Brands ----------
function BrandsSection({ content }) {
const c = content.brands;
return (
{c.items.map((b, i) => (
))}
);
}
// ---------- CEO Message ----------
function CeoSection({ content }) {
const c = content.ceo;
return (
{c.quote}
{c.name}
{c.role}
{c.body.map((p, i) =>
{p}
)}
);
}
// ---------- COO Profile ----------
function CooSection({ content }) {
const c = content.coo;
if (!c) return null;
return (
{c.quote}
{c.name}
{c.role}
{c.body.map((p, i) =>
{p}
)}
{(c.blocks || []).map((b, i) => (
{b.h}
{b.p}
))}
{c.facts && (
{c.facts.map((f, i) => (
))}
)}
);
}
// ---------- Careers ----------
function CareersSection({ content }) {
const c = content.careers;
const APPLY_URL = "https://doteppan.com/contact";
return (
{c.tagWords.map((w, i) => (
{w.text}{i < c.tagWords.length - 1 ? " " : ""}
))}
{c.lead}
{/* Stats: 12店舗 / 90% / 10倍 */}
{c.stats.map((s) => (
))}
{/* Values */}
{c.values.map((v) => (
{v.vn}
{v.h}
{v.p}
))}
{/* Job openings */}
04
{c.openingsTitle}
{c.openingsNote}
{/* Career path / grades */}
05
{c.gradesTitle}
{c.grades.map((g) => (
{g.gn}
{g.name}
{g.role}
{g.desc}
{g.pay}{g.payUnit}
))}
{/* Requirements */}
06
{c.reqsTitle}
{/* Hiring flow */}
{/* Apply CTA */}
);
}
// ---------- Contact ----------
function ContactSection({ content, lang }) {
const c = content.contact;
const L = c.labels;
const [form, setForm] = _useState({
type: [],
name: "",
company: "",
email: "",
message: "",
website: "", // ハニーポット(画面には出さない。botだけが埋める)
});
const [errors, setErrors] = _useState({});
const [touched, setTouched] = _useState({});
const [submitting, setSubmitting] = _useState(false);
const [done, setDone] = _useState(false);
const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
const toggleType = (t) => {
setForm((f) => ({
...f,
type: f.type.includes(t) ? f.type.filter((x) => x !== t) : [...f.type, t],
}));
};
const validate = (f) => {
const e = {};
if (!f.type || f.type.length === 0) e.type = L.errType;
if (!f.name.trim()) e.name = L.errReq;
if (!f.email.trim()) e.email = L.errReq;
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(f.email)) e.email = L.errEmail;
if (!f.message.trim()) e.message = L.errReq;
return e;
};
_useEffect(() => {
setErrors(validate(form));
}, [form, lang]);
const handleBlur = (k) => setTouched((t) => ({ ...t, [k]: true }));
// ★ お問い合わせの送信先(Google Apps Script のウェブアプリURL)
// 受信先のメールアドレス・自動返信の文面は Apps Script 側の「コード.gs」で設定します。
// デプロイしたURL(https://script.google.com/macros/s/.../exec)をここに貼ってください。
// 差し替え手順は 受信フォーム_設定手順.txt を参照。
const FORM_ENDPOINT = "https://script.google.com/macros/s/AKfycbzdO8zKshvMRxsjdp-_JSj3kdKfYaR2zzKqVpvMCGwtJTVO8XTh8zGcr0wRDNHlp-8EAg/exec";
const submit = async (e) => {
e.preventDefault();
setTouched({ type: true, name: true, email: true, message: true });
const errs = validate(form);
setErrors(errs);
if (Object.keys(errs).length > 0) return;
setSubmitting(true);
try {
// Content-Type を text/plain にすると CORS の事前確認(preflight)が走らず、
// Apps Script のウェブアプリへそのまま送れる。中身は JSON のまま。
const res = await fetch(FORM_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "text/plain;charset=utf-8" },
body: JSON.stringify({
type: form.type.join("、"),
name: form.name,
company: form.company,
email: form.email,
message: form.message,
website: form.website, // 人には見えない欄。入っていれば bot として破棄される
page: location.pathname,
lang: lang,
}),
});
const out = await res.json().catch(() => ({ ok: res.ok }));
setSubmitting(false);
if (out.ok) {
setDone(true);
} else {
alert("送信に失敗しました。お手数ですが partner@teppan-jp.com までご連絡ください。");
}
} catch (err) {
setSubmitting(false);
alert("通信エラーが発生しました。お手数ですが partner@teppan-jp.com までご連絡ください。");
}
};
const showErr = (k) => touched[k] && errors[k];
return (
);
}
// ---------- Company ----------
function CompanySection({ content }) {
const c = content.company;
return (
{c.historyTitle}
{c.history.map((h, i) => (
{h.year}
{h.items
? h.items.map((t, j) =>
{t}
)
: h.text}
))}
{c.factsTitle}
{c.facts.map((f, i) => (
))}
);
}
// ---------- FAQ / よくあるご質問 ----------
function FaqSection({ content }) {
const c = content.faq;
if (!c || !c.items) return null;
return (
{c.items.map((it, i) => (
{it.q}
))}
);
}
// ---------- Footer ----------
function SiteFooter({ content }) {
const f = content.footer;
return (
);
}
Object.assign(window, {
NewsSection,
FaqSection,
BusinessSection,
BrandsSection,
CeoSection,
CooSection,
CareersSection,
ContactSection,
CompanySection,
SiteFooter,
});