Browse Source

feat : si 페이지 추가

remotes/origin/main
이시연 3 weeks ago
parent
commit
595ebcede2
  1. BIN
      public/images/si_img1.png
  2. BIN
      public/images/si_img2.png
  3. BIN
      public/images/si_img3.png
  4. BIN
      public/images/si_img4.png
  5. BIN
      public/images/si_img5.png
  6. BIN
      public/images/si_img6.png
  7. BIN
      public/images/si_img7.png
  8. 121
      src/css/common.css
  9. 368
      src/pages/business/SiPage.jsx

BIN
public/images/si_img1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/images/si_img2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
public/images/si_img3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
public/images/si_img4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
public/images/si_img5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
public/images/si_img6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

BIN
public/images/si_img7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

121
src/css/common.css

@ -662,9 +662,6 @@ body{overflow-x:hidden;}
.fc-eyebrow--light::before { background: rgba(255,255,255,0.4); }
.fc-section-title {display:block;font-size:1.5rem;font-weight:700;color:var(--color-primary);margin-bottom:2.5rem;letter-spacing:-.01em;}
/*
1. 개요
*/
.fc-overview { position: relative; border-radius: 1.25rem; overflow: hidden; margin: 3rem 0 6rem; min-height: 520px; display: flex; align-items: flex-end; }
.fc-overview__bg {position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;filter:brightness(.38) saturate(.6);}
.fc-overview__overlay {position:absolute;inset:0;background:linear-gradient(170deg, rgba(26,31,94,.6) 0%, rgba(26,31,94,.92) 60%);pointer-events:none;}
@ -680,10 +677,6 @@ body{overflow-x:hidden;}
.fc-badge--light {border-color:rgba(255,255,255,.18);background:rgba(255,255,255,.08);color:rgba(255,255,255,.75);}
.fc-badge--light:hover {background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.3);color:#fff;}
/*
2. intro
*/
.fc-intro { display: flex; gap: 48px; align-items: center; padding: 60px 0;}
.fc-intro__left { flex: 1; }
.fc-intro__title { font-size: 2rem; font-weight: 800; color: #1a1f3a; line-height: 1.35; letter-spacing: -0.02em; margin: 16px 0 28px; }
@ -695,9 +688,6 @@ body{overflow-x:hidden;}
.fc-intro__right { flex: 0 0 52%; }
.fc-intro__monitor { width: 100%; object-fit: contain; display: block; }
/*
3. 기능 하이라이트
*/
.fc-highlight { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; margin-bottom: 8rem; align-items: center; padding: 60px 0;}
.fc-highlight__item { display: flex; gap: 24px; align-items: center; }
.fc-highlight__text { flex: 0 0 40%; }
@ -709,21 +699,14 @@ body{overflow-x:hidden;}
.fc-highlight__tablet { width: 100%; object-fit: contain; display: block; }
.fc-highlight__float-icon { position: absolute; width: 72px; height: 72px; object-fit: contain; filter: drop-shadow(0 8px 16px rgba(0,0,0,0.15)); }
.fc-situation { position: relative; width: 100%; }
/* 메인 이미지 크기 */
.fc-situation__main { width: 100%; object-fit: contain; display: block; }
/* 왼쪽 탭 위치/크기 */
.fc-situation__main { width: 100%; object-fit: contain; display: block; }
.fc-situation__left { position: absolute; left: -20%; top: 10%; width: 60% !important; object-fit: contain; }
/* 오른쪽 탭 위치/크기 */
.fc-situation__right { position: absolute; right: -20%; top: 20%; width: 60% !important; object-fit: contain; }
/* 아래 탭 위치/크기 */
.fc-situation__bottom { position: absolute; bottom: -40%; left: -5%; width: 60%; object-fit: contain; }
/*
4. 수치 지표
*/
.fc-stats { display: grid; grid-template-columns: repeat(4,1fr); gap: 0; margin-bottom: 8rem; border: 1px solid var(--color-primary-soft-border); border-radius: 1rem; overflow: hidden; }
.fc-stat-item {display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:2.5rem 1.5rem;text-align:center;border-right:1px solid var(--color-primary-soft-border);transition:background .2s;}
.fc-stat-item:last-child {border-right:none;}
@ -731,9 +714,7 @@ body{overflow-x:hidden;}
.fc-stat-item__value {font-size:2.25rem;font-weight:800;color:var(--color-primary);letter-spacing:-.03em;line-height:1;}
.fc-stat-item__label {font-size:.8rem;font-weight:500;color:#888;line-height:1.4;}
/*
5. 적용 분야
*/
.fc-domains { margin-bottom: 8rem; }
.fc-domains__grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1.25rem; }
@ -750,9 +731,7 @@ body{overflow-x:hidden;}
@media (max-width: 1024px) { .fc-domains__grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 640px) { .fc-domains__grid { grid-template-columns: repeat(2, 1fr); } }
/*
6. 주요기능
*/
.fc-functions { margin-bottom: 8rem; }
.fc-functions__body {display:flex;gap:5rem;align-items:flex-start;}
@ -773,9 +752,6 @@ body{overflow-x:hidden;}
.fc-func-display__num {font-size:1rem;font-weight:800;color:var(--pink);line-height:1;}
.fc-func-display__label {font-size:.78rem;font-weight:600;color:#fff;}
/*
7. 시스템 구성
*/
.fc-flow { padding-bottom: 10rem; }
.fc-flow__row {display:flex;align-items:flex-start;gap:0;}
.fc-flow__item {position:relative;flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:.6rem;padding:2rem 1rem;}
@ -784,9 +760,6 @@ body{overflow-x:hidden;}
.fc-flow__desc {font-size:.76rem;line-height:1.65;color:#888;margin:0;}
.fc-flow__arrow {position:absolute;top:2.75rem;right:-1px;width:2px;height:1.5rem;background:var(--color-primary-soft-border);transform-origin:top;}
/*
반응형
*/
@media (max-width: 1024px) {
.fc-overview { min-height: 420px; margin: 2rem 0 3rem; }
.fc-overview__content { padding: 3rem 2.5rem 3.5rem; }
@ -813,7 +786,6 @@ body{overflow-x:hidden;}
.fc-highlight__img-wrap { width: 100%; }
}
@media (max-width: 640px) {
.fc-overview {
border-radius: .875rem;
@ -936,3 +908,90 @@ body{overflow-x:hidden;}
@keyframes ibe-booking-flow { from { left: -40%; } to { left: 100%; } }
/* ========================================
SI PAGE - PROJECT ARCHIVE SECTION
======================================== */
.si_archive { padding:100px 0 120px; max-width:1660px; margin-left: auto; margin-right: 0;}
.si_archive__main { display:flex; align-items:flex-start; gap:48px; }
.si_archive__header { flex: 0 0 320px; padding-top: 24px; }
.si_archive__right { flex:1; min-width:0; }
.si_archive__eyebrow { display:block; font-size:12px; font-weight:600; letter-spacing:.12em; color:#2563eb; text-transform:uppercase; margin-bottom:16px; }
.si_archive__title { font-size:clamp(28px,3vw,40px); font-weight:700; line-height:1.25; color:var(--color-primary); letter-spacing:-.02em; margin:0 0 20px; }
.si_archive__desc { font-size:14px; line-height:1.7; color:#555; margin:0 0 32px; }
.si_archive__more { display:inline-flex; align-items:center; gap:6px; font-size:13px; font-weight:600; color:#111; text-decoration:none; border-bottom:1.5px solid #111; padding-bottom:2px; width:fit-content; transition:.2s; }
.si_archive__more:hover { color:#2563eb; border-color:#2563eb; }
.si_archive__more-arrow { transition:transform .2s; }
.si_archive__more:hover .si_archive__more-arrow { transform:translateX(4px); }
.si_archive__slider { overflow: hidden; user-select: none; cursor: grab}
.si_archive__slider:active { cursor: grabbing; }
.si_archive__track { display:flex; gap:18px; will-change:transform; }
.si_archive__card { flex: 0 0 65%; padding: 20px; overflow: hidden; background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; box-sizing: border-box; display: flex; flex-direction: column; }
.si_archive__card-header { display: flex; align-items: center; gap: 12px; }
.si_archive__card-img { width: 100%; aspect-ratio: 16/8; flex-shrink: 0; background: #eef2f7; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; }
.si_archive__card-img img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; border-radius: 8px; }
.si_archive__card-img-placeholder { position:absolute; inset:0; background:linear-gradient(135deg,#cbd5e1 0%,#94a3b8 100%); }
.si_archive__card-img img + .si_archive__card-img-placeholder { display:none; }
.si_archive__card-body { padding-top: 16px; display: flex; flex-direction: column; gap: 12px; }
.si_archive__card-body-left { flex: 0 0 130px; }
.si_archive__card-num { font-size: 16px; font-weight: 700; color: #fff; background: linear-gradient(135deg, #c026d3, #7c3aed); width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.si_archive__card-title { font-size: 16px; font-weight: 700; line-height: 1.4; color: #111; margin: 0; }
.si_archive__card-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.si_archive__tag { padding: 3px 8px; border: 1px solid #d1d5db; border-radius: 999px; font-size: 10px; color: #555; background: #fff; }
.si_archive__card-desc { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 0; padding: 0; list-style: none; border-top: 1px solid #f1f1f1; padding-top: 14px; }
.si_archive__card-desc li { display: flex; flex-direction: column; gap: 6px; }
.si_archive__card-desc-icon { width: 28px; height: 28px; background: #f3f4f6; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 14px; }
.si_archive__card-desc-title { font-size: 12px; font-weight: 700; color: #111; }
.si_archive__card-desc-text { font-size: 11px; line-height: 1.5; color: #888; }
.si_archive__nav { width: 100%; margin-top: 40px; display: flex; align-items: center; gap: 16px; }
.si_archive__nav-btn { width: 40px; height: 40px; border-radius: 50%; border: 1.5px solid #e5e7eb; background: transparent; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #111; flex-shrink: 0; transition: all .25s; }
.si_archive__nav-btn:hover { background: linear-gradient(135deg, #c026d3, #7c3aed); border-color: transparent; color: #fff; }
.si_archive__nav-btn:disabled { opacity: .3; cursor: not-allowed; }
.si_archive__nav-btn:disabled:hover { background: transparent; border-color: #e5e7eb; color: #111; }
.si_archive__progress { display: flex; align-items: center; gap: 8px; }
.si_archive__progress-cur { font-size: 22px; font-weight: 700; color: #111; min-width: 28px; }
.si_archive__progress-divider { font-size: 14px; color: #ccc; }
.si_archive__progress-total { font-size: 14px; font-weight: 500; color: #bbb; min-width: 28px; }
@media (max-width:1200px) {
.si_archive__main { gap:36px; }
.si_archive__header { flex: 0 0 260px; }
.si_archive__card { flex: 0 0 72%; }
.si_archive__nav { width:70%; }
}
@media (max-width:1024px) {
.si_archive { padding:72px 0 80px; }
.si_archive__main { flex-direction:column; gap:36px; }
.si_archive__header { flex:none; width:100%; padding-top:0; }
.si_archive__title br,
.si_archive__desc br { display:none; }
.si_archive__right { width:100%; }
.si_archive__card { flex: 0 0 80%; }
.si_archive__nav { width:100%; margin-top:30px; }
}
@media (max-width:768px) {
.si_archive { padding:56px 0 64px; }
.si_archive__main { gap:28px; }
.si_archive__slider { gap:14px; overflow-x:auto; padding-bottom:4px; scrollbar-width:none; }
.si_archive__slider::-webkit-scrollbar { display:none; }
.si_archive__card { flex: 0 0 82%; }
.si_archive__card-img { aspect-ratio:16/10; }
.si_archive__card-body { padding:18px 18px 20px; }
.si_archive__nav { gap:14px; margin-top:26px; }
.si_archive__nav-btn { width:40px; height:40px; font-size:16px; }
}
@media (max-width:480px) {
.si_archive__card { flex:0 0 86%; }
.si_archive__title { font-size:30px; }
.si_archive__desc { font-size:13px; }
.si_archive__progress { gap:8px; }
}

368
src/pages/business/SiPage.jsx

@ -1,49 +1,375 @@
import { useRef, useState, useEffect } from "react";
import { motion, AnimatePresence, useInView } from "framer-motion";
import SubHero from "../../components/SubHero";
import useFadeIn from "../../hooks/useFadeIn";
const ease = [0.25, 0.1, 0.25, 1];
const PROJECTS = [
{
id: "01",
title: "제주패스 OTA 항공 서비스 구축",
tags: ["항공/여행", "OTA"],
image: "/images/si_img1.png",
desc: [
{
icon: "✈",
title: "통합 여행 포털",
text: "렌터카·숙박·항공 통합 서비스 구축",
},
{
icon: "🌏",
title: "해외시장 진출",
text: "국제선 항공으로 해외시장 초석 마련",
},
{
icon: "🍽",
title: "종합 서비스",
text: "맛집·카페로 이어지는 제주패스 구현",
},
],
},
{
id: "02",
title: "안면관광 방역 시스템 구축",
tags: ["공공/방역", "방역/보안"],
image: "/images/si_img2.png",
desc: [
{
icon: "🌡",
title: "건강 상태 관리",
text: "체류 기간 중 건강 상태 체크 및 관리",
},
{
icon: "📍",
title: "동선 파악",
text: "이동 동선 파악 및 정보 관리 시스템",
},
{
icon: "🔗",
title: "시스템 연계",
text: "중국전담여행사 전자관리시스템 연계",
},
],
},
{
id: "03",
title: "클린인천 출입인증 시스템 구축",
tags: ["공공/출입", "인증/보안"],
image: "/images/si_img3.png",
desc: [
{
icon: "📱",
title: "QR 방역 관리",
text: "QR코드 활용 방문자 방역 관리 구축",
},
{
icon: "👥",
title: "방문자 관리",
text: "체계적 방문자 관리 및 출입 정보 제공",
},
{
icon: "🚨",
title: "신속 대응",
text: "확진자 발생 시 신속한 방역 대응 지원",
},
],
},
{
id: "04",
title: "SSG.COM 항공서비스 운영 및 유지보수",
tags: ["항공/이커머스", "운영 · 유지보수"],
image: "/images/si_img4.png",
desc: [
{
icon: "🔧",
title: "오류 수정",
text: "시스템 오류 수정 및 불편 요소 개선",
},
{
icon: "➕",
title: "기능 추가",
text: "필요 기능 추가 개발 및 데이터 추출",
},
{
icon: "⚙",
title: "운영 안정화",
text: "시스템 최적화를 통한 운영 안정화",
},
],
},
{
id: "05",
title: "현대자동차 출입인증 시스템 구축",
tags: ["기업/보안", "인증/보안"],
image: "/images/si_img5.png",
desc: [
{
icon: "📱",
title: "QR 방문자 관리",
text: "QR코드 활용 방문자 방역 관리 시스템",
},
{
icon: "🚨",
title: "출입 정보 제공",
text: "확진자 발생 시 정확한 출입 정보 제공",
},
{
icon: "🖥",
title: "미디어 월 연동",
text: "미디어 월 연동을 통한 고객 응대",
},
],
},
{
id: "06",
title: "하이에어 항공문항 시스템 구축",
tags: ["항공", "인증/스케줄"],
image: "/images/si_img6.png",
desc: [
{
icon: "🏗",
title: "인프라 구축",
text: "항공 운송사업자 필수 서비스 및 인프라",
},
{
icon: "✈",
title: "국제선 대응",
text: "국제선 취항 대응 및 부가서비스 매출 증대",
},
{
icon: "📋",
title: "고도화 체계",
text: "국토부 필수 시스템 및 서비스 고도화",
},
],
},
{
id: "07",
title: "하이에어 항공 시스템 운영",
tags: ["항공", "운영/유지보수"],
image: "/images/si_img7.png",
desc: [
{
icon: "🔧",
title: "오류 수정",
text: "시스템 오류 수정 및 불편 요소 개선",
},
{
icon: "➕",
title: "기능 추가",
text: "필요 기능 추가 개발 및 데이터 추출",
},
{
icon: "⚙",
title: "운영 안정화",
text: "시스템 최적화를 통한 운영 안정화",
},
],
},
];
const AUTO_DELAY = 4000;
function SiPage() {
const basePath = import.meta.env.BASE_URL;
const ref = useFadeIn();
const sectionRef = useRef(null);
const dragStartX = useRef(null);
const dragStartCurrent = useRef(null);
const inView = useInView(sectionRef, { once: true, margin: "-100px" });
const [current, setCurrent] = useState(0);
const [paused, setPaused] = useState(false);
const total = PROJECTS.length;
const canPrev = current > 0;
const canNext = current < total - 1;
const prev = () => {
if (!canPrev) return;
setCurrent((c) => c - 1);
};
const next = () => {
if (!canNext) return;
setCurrent((c) => c + 1);
};
useEffect(() => {
if (paused) return;
const timer = setInterval(() => {
setCurrent((c) => (c + 1) % total);
}, AUTO_DELAY);
return () => clearInterval(timer);
}, [paused, total]);
const handleDragStart = (e) => {
dragStartX.current =
e.type === "touchstart" ? e.touches[0].clientX : e.clientX;
dragStartCurrent.current = current;
};
const handleDragEnd = (e) => {
if (dragStartX.current === null) return;
const endX =
e.type === "touchend" ? e.changedTouches[0].clientX : e.clientX;
const diff = dragStartX.current - endX;
if (diff > 50) next();
else if (diff < -50) prev();
dragStartX.current = null;
};
const BUSINESS_NAV = [
{ label: "System Integration", to: "/business/si" },
{ label: "R&D", to: "/business/rnd" },
{ label: "운영 · 유지보수", to: "/business/maintenance" },
];
return (
<article ref={ref}>
<SubHero
label="BUSINESS"
title={
<>
{/* <span style={{ color: "#111" }}> </span>
<br /> */}
<em>SI Solutions</em>
</>
}
// desc="· SI ."
navItems={BUSINESS_NAV}
/>
<div className="sub-content">
<section className="sub-section">
<span className="sub-section-eyebrow sub-fade-in">시스템 통합 구축</span>
{/* <main className="sub-page">
<section className="sub-visual">
<div className="inner">
<h2>System Integration</h2>
<p>시스템 통합 구축</p>
<section
className="si_archive"
ref={sectionRef}
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<div className="si_archive__main">
{/* 헤더 */}
<div className="si_archive__header">
<motion.span
className="fc-eyebrow"
initial={{ opacity: 0, y: 16 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
PROJECT ARCHIVE
</motion.span>
<motion.h2
className="si_archive__title"
initial={{ opacity: 0, y: 20 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1, ease }}
>
수행사업
<br />
아카이브
</motion.h2>
<motion.p
className="si_archive__desc"
initial={{ opacity: 0, y: 16 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2, ease }}
>
PAL Networks가 구축한
<br />
주요 프로젝트를 소개합니다.
</motion.p>
{/* 네비게이션 */}
<div className="si_archive__nav">
<button
className="si_archive__nav-btn"
onClick={prev}
disabled={!canPrev}
aria-label="이전"
>
</button>
<div className="si_archive__progress">
<span className="si_archive__progress-cur">
{String(current + 1).padStart(2, "0")}
</span>
<span className="si_archive__progress-divider">/</span>
<span className="si_archive__progress-total">
{String(total).padStart(2, "0")}
</span>
</div>
<button
className="si_archive__nav-btn"
onClick={next}
disabled={!canNext}
aria-label="다음"
>
</button>
</div>
</section>
<section className="sub-content-section">
<div className="inner">
<h3>맞춤형 정보시스템 구축</h3>
<p>
공공·민간 분야의 정보시스템 구축부터 운영까지
<br /> 과정을 책임지는 SI 서비스를 제공합니다.
</p>
</div>
{/* 슬라이더 */}
<div className="si_archive__right">
<div
className="si_archive__slider"
style={{ display: "flex" }}
onMouseDown={handleDragStart}
onMouseUp={handleDragEnd}
onTouchStart={handleDragStart}
onTouchEnd={handleDragEnd}
>
<motion.div
className="si_archive__track"
animate={{ x: `calc(-${current} * (65% + 18px))` }}
transition={{ duration: 0.55, ease }}
style={{ alignItems: "stretch" }}
>
{PROJECTS.map((project) => (
<div key={project.id} className="si_archive__card">
<div className="si_archive__card-img">
<img
src={`${basePath}images/si_img${parseInt(project.id)}.png`}
alt={project.title}
/>
<div className="si_archive__card-img-placeholder" />
</div>
<div className="si_archive__card-body">
<div className="si_archive__card-header">
<div className="si_archive__card-num">
{project.id}
</div>
<h3 className="si_archive__card-title">
{project.title}
</h3>
</div>
<div className="si_archive__card-tags">
{project.tags.map((tag) => (
<span key={tag} className="si_archive__tag">
{tag}
</span>
))}
</div>
<ul className="si_archive__card-desc">
{project.desc.map((item, i) => (
<li key={i}>
<div className="si_archive__card-desc-icon">
{item.icon}
</div>
<div className="si_archive__card-desc-title">
{item.title}
</div>
<div className="si_archive__card-desc-text">
{item.text}
</div>
</li>
))}
</ul>
</div>
</div>
))}
</motion.div>
</div>
</section>
</main> */}
</div>
</div>
</section>
</div>
</article>

Loading…
Cancel
Save