[feat] landing: 데모 영상 3종 실화면 재촬영(라이브 협상·견적생성·협상결과, 줌인 연출) + 도입문의·데모요청 폼 mailto 임시연결
@ -175,9 +175,9 @@ export function Contact() {
|
||||
<div className="w-12 h-12 bg-primary text-white rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<Check className="w-6 h-6" strokeWidth={2.5} />
|
||||
</div>
|
||||
<h3 className="text-[24px] font-semibold tracking-[-0.02em] text-ink mb-3">도입 문의 신청이 접수되었습니다</h3>
|
||||
<h3 className="text-[24px] font-semibold tracking-[-0.02em] text-ink mb-3">메일 앱에서 문의 내용을 전송해 주세요</h3>
|
||||
<p className="text-ink-soft text-[15px] leading-[1.6] max-w-md mx-auto mb-8">
|
||||
{formData.contactName} 님께 영업일 기준 하루 안에 연락드리겠습니다.
|
||||
도입 문의 메일 작성 창을 열었습니다. 내용을 확인하고 전송해 주시면, {formData.contactName} 님께 영업일 기준 하루 안에 연락드리겠습니다.
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@ -238,11 +238,11 @@ const DEMO_TABS: DemoTab[] = [
|
||||
],
|
||||
checks: ["협력사별 1:1 비대면 협상 포털", "같은 기준으로 응대해 관계 부담 없음"],
|
||||
mockup: "phone",
|
||||
/* mp4 를 붙이기 전까지 이 탭만 1.7MB GIF(390x780)를 내려받고 있었다. 같은 내용의
|
||||
mp4 가 저장소에 있는데 참조가 빠져 있었다 — 786K, 540x1080 으로 더 가볍고 더 크다.
|
||||
(원본 캡쳐가 좌우로 잘려 문장이 끊기는 문제는 남아 있다. 재촬영이 필요하다.) */
|
||||
/* 현행 공급사 포털(토스풍 UI)에서 실제로 진행한 협상 스크린캐스트. 390x780 모바일 뷰포트로
|
||||
인트로→가격 밀당(협상 카드 4장)→타결(18,480원 합의)까지, 1.5배속. gif 는 무JS 폴백(폭 228). */
|
||||
gif: "/gifs/negotiation.gif",
|
||||
video: "/gifs/negotiation.mp4",
|
||||
poster: "/gifs/negotiation.jpg",
|
||||
gifAlt: "협력사 포털 모바일 협상 화면",
|
||||
fallback: {
|
||||
icon: Smartphone,
|
||||
|
||||
@ -125,12 +125,10 @@ export function DemoRequestModal({ open, onClose }: { open: boolean; onClose: ()
|
||||
<Check className="w-6 h-6" />
|
||||
</div>
|
||||
<Typography variant="cardTitle" as="h2" id={titleId} className="mb-3">
|
||||
데모 신청이 접수되었습니다
|
||||
메일 앱에서 요청을 전송해 주세요
|
||||
</Typography>
|
||||
{/* 폼에서 "Demo를 보내드립니다"라고 약속했으므로 성공 화면도 같은 약속을 지킨다.
|
||||
여기서 "일정을 잡아 연락드립니다"로 새면 방문자가 무엇을 기다려야 하는지 모른다. */}
|
||||
<Typography variant="small">
|
||||
입력하신 이메일로 사용해보실 수 있는 Demo를 보내드립니다.
|
||||
데모 요청 메일 작성 창을 열었습니다. 전송해 주시면 입력하신 이메일로 사용해보실 수 있는 Demo를 보내드립니다.
|
||||
</Typography>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
/*
|
||||
* 리드 전송 — 데모 요청 모달과 상담 신청 폼이 함께 쓴다.
|
||||
*
|
||||
* 목적지는 같은 오리진의 서버리스 함수(api/lead.ts)다. 랜딩은 ssr:false 정적 빌드지만
|
||||
* Vercel 의 /api 디렉터리 함수는 별개로 배포되므로 서버 없이도 POST 를 받을 수 있다.
|
||||
* 같은 오리진이라 엔드포인트 환경변수도, CORS 도 필요 없다.
|
||||
*
|
||||
* 성공을 위조하지 않는다. 예전 상담 신청 폼은 1.2초 뒤 성공 화면만 띄우고 아무 데도
|
||||
* 보내지 않았다 — 화면은 "접수되었습니다"인데 영업이 받을 리드는 없었다.
|
||||
* 여기서는 서버가 리드를 확보했을 때만 ok 를 돌려준다.
|
||||
* [임시] 백엔드/서버리스 연결 전까지는 mailto 로 방문자의 메일 앱을 열어 문의를 보내게 한다.
|
||||
* 메일 앱이 '열릴' 뿐 실제 전송 여부는 알 수 없으므로, UI 도 "접수 완료"가 아니라
|
||||
* "메일 앱을 열었습니다 — 전송해 주세요"로 정직하게 안내한다(fake success 금지). 나중에 API/시트로 교체.
|
||||
*/
|
||||
|
||||
const ENDPOINT = "/api/lead"
|
||||
// 리드 수신 주소 — 임시. 실제 영업 주소로 바꾸고, 이후 API 붙이면 mailto 자체를 교체할 것.
|
||||
const LEAD_EMAIL = "o2odev@o2o.kr"
|
||||
|
||||
export type LeadSource = "demo-request" | "contact"
|
||||
|
||||
@ -36,15 +33,26 @@ export function isEmailLike(value: string) {
|
||||
export async function submitLead(lead: Lead): Promise<LeadResult> {
|
||||
if (!lead.name.trim() || !isEmailLike(lead.email)) return { ok: false, reason: "invalid" }
|
||||
|
||||
try {
|
||||
const res = await fetch(ENDPOINT, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(lead),
|
||||
})
|
||||
return res.ok ? { ok: true } : { ok: false, reason: "rejected" }
|
||||
} catch {
|
||||
// 로컬 dev(react-router dev)에는 /api 가 없어서 여기로 온다. 확인은 `vercel dev` 로.
|
||||
return { ok: false, reason: "network" }
|
||||
const label = lead.source === "demo-request" ? "데모 요청" : "도입 문의"
|
||||
const body = [
|
||||
`[${label}]`,
|
||||
`회사명: ${lead.company || "-"}`,
|
||||
`담당자: ${lead.name}`,
|
||||
`이메일: ${lead.email}`,
|
||||
`연락처: ${lead.phone || "-"}`,
|
||||
`내용: ${lead.message || "-"}`,
|
||||
].join("\n")
|
||||
const href = `mailto:${LEAD_EMAIL}?subject=${encodeURIComponent(`[${label}] ${lead.company || lead.name}`)}&body=${encodeURIComponent(body)}`
|
||||
|
||||
// 방문자의 메일 앱을 연다 — 앵커 클릭이 location.href 할당보다 핸들러 발동이 확실하다.
|
||||
// 실제 전송은 방문자가 하므로 성공을 단정하지 않는다(호출부 UI 는 안내 문구).
|
||||
if (typeof document !== "undefined") {
|
||||
const a = document.createElement("a")
|
||||
a.href = href
|
||||
a.style.display = "none"
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
}
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.9 MiB |
BIN
landing/public/gifs/negotiation.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 4.1 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 42 KiB |