|
|
|
|
@ -1,10 +1,46 @@
|
|
|
|
|
import useFadeIn from "../../hooks/useFadeIn"; |
|
|
|
|
import SubHero from "../../components/SubHero"; |
|
|
|
|
import { useRef } from "react"; |
|
|
|
|
import { useRef, useState } from "react"; |
|
|
|
|
import { motion, useInView, AnimatePresence } from "framer-motion"; |
|
|
|
|
|
|
|
|
|
import { |
|
|
|
|
Search, |
|
|
|
|
Armchair, |
|
|
|
|
CalendarClock, |
|
|
|
|
Wallet, |
|
|
|
|
Ticket, |
|
|
|
|
ArrowUpRight, |
|
|
|
|
} from "lucide-react"; |
|
|
|
|
const ease = [0.22, 1, 0.36, 1]; |
|
|
|
|
|
|
|
|
|
const FUNCTIONS = [ |
|
|
|
|
{ num: "01", img: "./images/s1-01.jpg", label: "비행가능 지역 및 공역표출" }, |
|
|
|
|
{ num: "02", img: "./images/s1-02.jpg", label: "비행체 위치 표출" }, |
|
|
|
|
{ |
|
|
|
|
num: "03", |
|
|
|
|
img: "./images/s1-03.jpg", |
|
|
|
|
label: "항공기 비행경로 및 히스토리 표출", |
|
|
|
|
}, |
|
|
|
|
{ num: "04", img: "./images/s1-04.jpg", label: "항공기 비행정보 표출" }, |
|
|
|
|
{ num: "05", img: "./images/s1-05.jpg", label: "비행계획서 조회" }, |
|
|
|
|
{ num: "06", img: "./images/s1-06.jpg", label: "비정상 상황의 경보 표출" }, |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
const FLOW = [ |
|
|
|
|
{ |
|
|
|
|
step: "01", |
|
|
|
|
label: "비행체 식별", |
|
|
|
|
desc: "항공기·드론·선박 실시간 신호 수집", |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
step: "02", |
|
|
|
|
label: "데이터 수집", |
|
|
|
|
desc: "위치·속도·고도·식별코드 통합 처리", |
|
|
|
|
}, |
|
|
|
|
{ step: "03", label: "관제 서버", desc: "통합 관제 서버 분석 및 상황 판단" }, |
|
|
|
|
{ step: "04", label: "모니터링", desc: "관제사 화면 실시간 현황 표출" }, |
|
|
|
|
{ step: "05", label: "경보·대응", desc: "이상 상황 감지 즉시 경보 및 대응" }, |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
function IbePage() { |
|
|
|
|
const basePath = import.meta.env.BASE_URL; |
|
|
|
|
|
|
|
|
|
@ -13,12 +49,22 @@ function IbePage() {
|
|
|
|
|
const introInView = useInView(introRef, { once: true, margin: "-60px" }); |
|
|
|
|
const channelRef = useRef(null); |
|
|
|
|
const channelInView = useInView(channelRef, { once: true, margin: "-60px" }); |
|
|
|
|
const bookingRef = useRef(null); |
|
|
|
|
const bookingInView = useInView(bookingRef, { once: true, margin: "-60px" }); |
|
|
|
|
const funcRef = useRef(null); |
|
|
|
|
const funcInView = useInView(funcRef, { once: true, margin: "-80px" }); |
|
|
|
|
const flowRef = useRef(null); |
|
|
|
|
const flowInView = useInView(flowRef, { once: true, margin: "-60px" }); |
|
|
|
|
|
|
|
|
|
const SOLUTION_NAV = [ |
|
|
|
|
{ label: "비행상황관리 시스템", to: "/solution/flight-control" }, |
|
|
|
|
{ label: "IBE", to: "/solution/ibe" }, |
|
|
|
|
// { label: "스마트 관광 예약 플랫폼", to: "/solution/smart-tour" }, |
|
|
|
|
// { label: "KT G-cloud 인천총판", to: "/solution/kt-gcloud" }, |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
const [activeIdx, setActiveIdx] = useState(0); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<article ref={ref}> |
|
|
|
|
<SubHero |
|
|
|
|
@ -108,6 +154,118 @@ function IbePage() {
|
|
|
|
|
</motion.div> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
{/* BOOKING PROCESS */} |
|
|
|
|
<section className="ibe-booking-section" ref={bookingRef}> |
|
|
|
|
<motion.span |
|
|
|
|
className="fc-eyebrow" |
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
animate={bookingInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.6, ease }} |
|
|
|
|
> |
|
|
|
|
Booking Process |
|
|
|
|
</motion.span> |
|
|
|
|
<motion.h2 |
|
|
|
|
className="ibe-booking__title" |
|
|
|
|
initial={{ opacity: 0, y: 24 }} |
|
|
|
|
animate={bookingInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.7, ease, delay: 0.1 }} |
|
|
|
|
> |
|
|
|
|
직관적이고 간편한 예약 프로세스 |
|
|
|
|
</motion.h2> |
|
|
|
|
|
|
|
|
|
<div className="ibe-booking__flow"> |
|
|
|
|
{[ |
|
|
|
|
{ |
|
|
|
|
num: "01", |
|
|
|
|
icon: Search, |
|
|
|
|
label: "검색", |
|
|
|
|
desc: "출발지, 도착지, 일정,\n탑승객 정보를 입력하여\n최적의 항공편 검색", |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
num: "02", |
|
|
|
|
icon: Armchair, |
|
|
|
|
label: "선택", |
|
|
|
|
desc: "운임, 스케줄, 좌석 등\n다양한 옵션을 비교하고\n원하는 항공편 선택", |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
num: "03", |
|
|
|
|
icon: CalendarClock, |
|
|
|
|
label: "예약", |
|
|
|
|
desc: "승객 정보 입력 및\n부가 서비스 선택을 통해\n예약을 진행", |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
num: "04", |
|
|
|
|
icon: Wallet, |
|
|
|
|
label: "결제", |
|
|
|
|
desc: "다양한 결제 수단을 지원하여\n안전하고 간편하게\n결제 진행", |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
num: "05", |
|
|
|
|
icon: Ticket, |
|
|
|
|
label: "발권", |
|
|
|
|
desc: "E-ticket 발행 및\n예약 확인을 통해\n예약 완료", |
|
|
|
|
}, |
|
|
|
|
].map((item, i) => { |
|
|
|
|
const Icon = item.icon; |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
<motion.div |
|
|
|
|
key={i} |
|
|
|
|
className="ibe-booking__item" |
|
|
|
|
initial={{ opacity: 0, y: 24 }} |
|
|
|
|
animate={bookingInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.6, ease, delay: 0.2 + i * 0.1 }} |
|
|
|
|
> |
|
|
|
|
<div className="ibe-booking__circle"> |
|
|
|
|
<svg |
|
|
|
|
width="0" |
|
|
|
|
height="0" |
|
|
|
|
style={{ position: "absolute" }} |
|
|
|
|
> |
|
|
|
|
<defs> |
|
|
|
|
<linearGradient |
|
|
|
|
id="ibe-icon-grad" |
|
|
|
|
x1="0%" |
|
|
|
|
y1="0%" |
|
|
|
|
x2="100%" |
|
|
|
|
y2="100%" |
|
|
|
|
> |
|
|
|
|
<stop offset="0%" stopColor="#a78bfa" /> |
|
|
|
|
<stop offset="100%" stopColor="#ec4899" /> |
|
|
|
|
</linearGradient> |
|
|
|
|
</defs> |
|
|
|
|
</svg> |
|
|
|
|
<Icon |
|
|
|
|
size={32} |
|
|
|
|
strokeWidth={1.5} |
|
|
|
|
stroke="url(#ibe-icon-grad)" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
<span className="ibe-booking__num">{item.num}</span> |
|
|
|
|
<span className="ibe-booking__label">{item.label}</span> |
|
|
|
|
<p className="ibe-booking__desc"> |
|
|
|
|
{item.desc.split("\n").map((line, j) => ( |
|
|
|
|
<span key={j}> |
|
|
|
|
{line} |
|
|
|
|
<br /> |
|
|
|
|
</span> |
|
|
|
|
))} |
|
|
|
|
</p> |
|
|
|
|
</motion.div> |
|
|
|
|
{i < 4 && ( |
|
|
|
|
<div |
|
|
|
|
className="ibe-booking__connector" |
|
|
|
|
style={{ "--delay": `${i * 0.4}s` }} |
|
|
|
|
> |
|
|
|
|
<div className="ibe-booking__line-bg" /> |
|
|
|
|
<div className="ibe-booking__line-flow" /> |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
</section> |
|
|
|
|
{/* MULTI CHANNEL INTEGRATION */} |
|
|
|
|
<section className="ibe-channel-section" ref={channelRef}> |
|
|
|
|
<motion.span |
|
|
|
|
@ -222,6 +380,129 @@ function IbePage() {
|
|
|
|
|
</div> |
|
|
|
|
</motion.div> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
{/* 4. 주요기능 */} |
|
|
|
|
<section className="fc-functions" ref={funcRef}> |
|
|
|
|
<motion.span |
|
|
|
|
className="fc-section-title" |
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
animate={funcInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.6, ease }} |
|
|
|
|
> |
|
|
|
|
주요기능 |
|
|
|
|
</motion.span> |
|
|
|
|
<div className="fc-functions__body"> |
|
|
|
|
<ul className="fc-func-list"> |
|
|
|
|
{FUNCTIONS.map(({ num, label }, i) => ( |
|
|
|
|
<motion.li |
|
|
|
|
key={num} |
|
|
|
|
className={`fc-func-item${activeIdx === i ? " is-active" : ""}`} |
|
|
|
|
onMouseEnter={() => setActiveIdx(i)} |
|
|
|
|
initial={{ opacity: 0, y: 16 }} |
|
|
|
|
animate={funcInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.5, ease, delay: 0.05 * i }} |
|
|
|
|
> |
|
|
|
|
<span className="fc-func-item__num">{num}</span> |
|
|
|
|
<span className="fc-func-item__label">{label}</span> |
|
|
|
|
<motion.span |
|
|
|
|
className="fc-func-item__arrow" |
|
|
|
|
initial={{ opacity: 0, x: -6 }} |
|
|
|
|
animate={ |
|
|
|
|
activeIdx === i |
|
|
|
|
? { opacity: 1, x: 0 } |
|
|
|
|
: { opacity: 0, x: -6 } |
|
|
|
|
} |
|
|
|
|
transition={{ duration: 0.2 }} |
|
|
|
|
> |
|
|
|
|
<ArrowUpRight size={16} strokeWidth={1.5} /> |
|
|
|
|
</motion.span> |
|
|
|
|
<motion.div |
|
|
|
|
className="fc-func-item__line" |
|
|
|
|
animate={{ scaleX: activeIdx === i ? 1 : 0 }} |
|
|
|
|
transition={{ duration: 0.35, ease }} |
|
|
|
|
/> |
|
|
|
|
</motion.li> |
|
|
|
|
))} |
|
|
|
|
</ul> |
|
|
|
|
<div className="fc-func-display"> |
|
|
|
|
<AnimatePresence mode="wait"> |
|
|
|
|
<motion.div |
|
|
|
|
key={activeIdx} |
|
|
|
|
className="fc-func-display__inner" |
|
|
|
|
initial={{ opacity: 0 }} |
|
|
|
|
animate={{ opacity: 1 }} |
|
|
|
|
exit={{ opacity: 0 }} |
|
|
|
|
transition={{ duration: 0.35, ease: "easeInOut" }} |
|
|
|
|
> |
|
|
|
|
<img |
|
|
|
|
src={FUNCTIONS[activeIdx].img} |
|
|
|
|
alt={FUNCTIONS[activeIdx].label} |
|
|
|
|
className="fc-func-display__img" |
|
|
|
|
/> |
|
|
|
|
<div className="fc-func-display__caption"> |
|
|
|
|
<motion.span |
|
|
|
|
key={`num-${activeIdx}`} |
|
|
|
|
className="fc-func-display__num" |
|
|
|
|
initial={{ opacity: 0, y: 6 }} |
|
|
|
|
animate={{ opacity: 1, y: 0 }} |
|
|
|
|
transition={{ duration: 0.25 }} |
|
|
|
|
> |
|
|
|
|
{FUNCTIONS[activeIdx].num} |
|
|
|
|
</motion.span> |
|
|
|
|
<motion.span |
|
|
|
|
key={`label-${activeIdx}`} |
|
|
|
|
className="fc-func-display__label" |
|
|
|
|
initial={{ opacity: 0, y: 6 }} |
|
|
|
|
animate={{ opacity: 1, y: 0 }} |
|
|
|
|
transition={{ duration: 0.25, delay: 0.05 }} |
|
|
|
|
> |
|
|
|
|
{FUNCTIONS[activeIdx].label} |
|
|
|
|
</motion.span> |
|
|
|
|
</div> |
|
|
|
|
</motion.div> |
|
|
|
|
</AnimatePresence> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
{/* 5. 시스템 구성 흐름 */} |
|
|
|
|
<section className="fc-flow" ref={flowRef}> |
|
|
|
|
<motion.span |
|
|
|
|
className="fc-section-title" |
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
animate={flowInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.6, ease }} |
|
|
|
|
> |
|
|
|
|
시스템 구성 |
|
|
|
|
</motion.span> |
|
|
|
|
<div className="fc-flow__row"> |
|
|
|
|
{FLOW.map(({ step, label, desc }, i) => ( |
|
|
|
|
<motion.div |
|
|
|
|
key={step} |
|
|
|
|
className="fc-flow__item" |
|
|
|
|
initial={{ opacity: 0, y: 20 }} |
|
|
|
|
animate={flowInView ? { opacity: 1, y: 0 } : {}} |
|
|
|
|
transition={{ duration: 0.5, ease, delay: 0.08 * i }} |
|
|
|
|
> |
|
|
|
|
<span className="fc-flow__step">{step}</span> |
|
|
|
|
<span className="fc-flow__label">{label}</span> |
|
|
|
|
<p className="fc-flow__desc">{desc}</p> |
|
|
|
|
{i < FLOW.length - 1 && ( |
|
|
|
|
<motion.div |
|
|
|
|
className="fc-flow__arrow" |
|
|
|
|
initial={{ opacity: 0, scaleX: 0 }} |
|
|
|
|
animate={flowInView ? { opacity: 1, scaleX: 1 } : {}} |
|
|
|
|
transition={{ |
|
|
|
|
duration: 0.4, |
|
|
|
|
ease, |
|
|
|
|
delay: 0.08 * i + 0.3, |
|
|
|
|
}} |
|
|
|
|
/> |
|
|
|
|
)} |
|
|
|
|
</motion.div> |
|
|
|
|
))} |
|
|
|
|
</div> |
|
|
|
|
</section> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</article> |
|
|
|
|
|