Browse Source

build

remotes/origin/main
김지은 2 weeks ago
parent
commit
fef1e30b3b
  1. 43
      docs/assets/index-Co9kRHX5.js
  2. 2
      docs/assets/index-DFlth-pl.css
  3. 45
      docs/assets/index-DvqRPidd.js
  4. BIN
      docs/assets/main_utm_img-DD881ce8.png
  5. BIN
      docs/images/domain_img1.jpg
  6. BIN
      docs/images/domain_img2.jpg
  7. BIN
      docs/images/domain_img3.jpg
  8. BIN
      docs/images/domain_img3.png
  9. BIN
      docs/images/domain_img4.jpg
  10. BIN
      docs/images/domain_img5.jpg
  11. BIN
      docs/images/domain_img5.png
  12. BIN
      docs/images/main_utm_img.png
  13. BIN
      docs/images/rnd_img6.png
  14. BIN
      docs/images/si_injustice_drone.png
  15. BIN
      docs/images/si_kac_utm.png
  16. BIN
      docs/images/utm_what_img.png
  17. 4
      docs/index.html
  18. 201
      src/components/main/MainUtm.jsx
  19. 3
      src/css/common.css

43
docs/assets/index-Co9kRHX5.js

File diff suppressed because one or more lines are too long

2
docs/assets/index-uDnQuu9U.css → docs/assets/index-DFlth-pl.css

File diff suppressed because one or more lines are too long

45
docs/assets/index-DvqRPidd.js

File diff suppressed because one or more lines are too long

BIN
docs/assets/main_utm_img-DD881ce8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/images/domain_img1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 464 KiB

BIN
docs/images/domain_img2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
docs/images/domain_img3.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 MiB

After

Width:  |  Height:  |  Size: 908 KiB

BIN
docs/images/domain_img3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

BIN
docs/images/domain_img4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
docs/images/domain_img5.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
docs/images/domain_img5.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

BIN
docs/images/main_utm_img.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/images/rnd_img6.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/images/si_injustice_drone.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 MiB

BIN
docs/images/si_kac_utm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
docs/images/utm_what_img.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

4
docs/index.html

@ -55,8 +55,8 @@
<link href="https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&family=Outfit:wght@100..900&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&family=Outfit:wght@100..900&display=swap" rel="stylesheet" />
<title>PAL Networks</title> <title>PAL Networks</title>
<script type="module" crossorigin src="/PALNetworks/assets/index-Co9kRHX5.js"></script> <script type="module" crossorigin src="/PALNetworks/assets/index-DvqRPidd.js"></script>
<link rel="stylesheet" crossorigin href="/PALNetworks/assets/index-uDnQuu9U.css"> <link rel="stylesheet" crossorigin href="/PALNetworks/assets/index-DFlth-pl.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

201
src/components/main/MainUtm.jsx

@ -27,14 +27,15 @@ const DETAIL = {
date: "2025-06-10 14:30", date: "2025-06-10 14:30",
}; };
const PHASES = ["list", "detail", "confirm", "done"];
const PHASE_DURATION = { list: 1800, detail: 2200, confirm: 1800, done: 2000 };
function UtmSystemPanel({ phase, activeRow }) { function UtmSystemPanel({ phase, activeRow }) {
return ( return (
<div className="utm-panel"> <div className="utm-panel">
<div className="utm-panel__header"> <div className="utm-panel__header">
<span className="utm-panel__title">비행계획 승인관리</span> <span className="utm-panel__title">비행계획 승인관리</span>
<span className="utm-panel__badge"> <span className="utm-panel__badge">검색결과 {FLIGHT_PLANS.length}</span>
검색결과 {FLIGHT_PLANS.length}
</span>
</div> </div>
<div className="utm-panel__table"> <div className="utm-panel__table">
@ -46,32 +47,20 @@ function UtmSystemPanel({ phase, activeRow }) {
<span></span> <span></span>
</div> </div>
{FLIGHT_PLANS.map((row, i) => ( {FLIGHT_PLANS.map((row, i) => (
<div <div key={row.id} className={`utm-panel__row${activeRow === i ? " utm-panel__row--active" : ""}`}>
key={row.id}
className={`utm-panel__row${activeRow === i ? " utm-panel__row--active" : ""}`}
>
<span className="utm-panel__cell">{row.id}</span> <span className="utm-panel__cell">{row.id}</span>
<span className="utm-panel__cell">{row.pilot}</span> <span className="utm-panel__cell">{row.pilot}</span>
<span className="utm-panel__cell">{row.route}</span> <span className="utm-panel__cell">{row.route}</span>
<span <span className={`utm-panel__status utm-panel__status--${row.status === "승인" ? "done" : "wait"}`}>{row.status}</span>
className={`utm-panel__status utm-panel__status--${row.status === "승인" ? "done" : "wait"}`}
>
{row.status}
</span>
<span className="utm-panel__cell"> <span className="utm-panel__cell">
<span <span className={`utm-panel__btn${activeRow === i && phase === "list" ? " utm-panel__btn--hover" : ""}`}>상세보기</span>
className={`utm-panel__btn${activeRow === i && phase === "list" ? " utm-panel__btn--hover" : ""}`}
>
상세보기
</span>
</span> </span>
</div> </div>
))} ))}
</div> </div>
<div {/* 상세: 아래로 펼쳐짐 */}
className={`utm-panel__detail${phase === "detail" || phase === "confirm" || phase === "done" ? " utm-panel__detail--show" : ""}`} <div className={`utm-panel__detail${phase === "detail" || phase === "confirm" || phase === "done" ? " utm-panel__detail--show" : ""}`}>
>
<div className="utm-panel__detail-title">비행계획 상세</div> <div className="utm-panel__detail-title">비행계획 상세</div>
<div className="utm-panel__detail-rows"> <div className="utm-panel__detail-rows">
<div className="utm-panel__detail-row"> <div className="utm-panel__detail-row">
@ -100,31 +89,19 @@ function UtmSystemPanel({ phase, activeRow }) {
</div> </div>
<div className="utm-panel__detail-row"> <div className="utm-panel__detail-row">
<span>상태</span> <span>상태</span>
<span <span className={`utm-panel__status utm-panel__status--${phase === "done" ? "done" : "wait"}`}>{phase === "done" ? "승인" : "대기"}</span>
className={`utm-panel__status utm-panel__status--${phase === "done" ? "done" : "wait"}`}
>
{phase === "done" ? "승인" : "대기"}
</span>
</div> </div>
</div> </div>
{phase === "detail" && ( {phase === "detail" && (
<div className="utm-panel__detail-actions"> <div className="utm-panel__detail-actions">
<span className="utm-panel__approve-btn utm-panel__approve-btn--hover"> <span className="utm-panel__approve-btn utm-panel__approve-btn--hover">승인처리</span>
승인처리
</span>
</div> </div>
)} )}
<AnimatePresence> <AnimatePresence>
{phase === "done" && ( {phase === "done" && (
<motion.div <motion.div className="utm-panel__toast" initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0 }} transition={{ duration: 0.35, ease }}>
className="utm-panel__toast"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.35, ease }}
>
<span className="utm-panel__toast-icon"></span> <span className="utm-panel__toast-icon"></span>
FP-001 승인이 완료되었습니다. FP-001 승인이 완료되었습니다.
</motion.div> </motion.div>
@ -132,29 +109,14 @@ function UtmSystemPanel({ phase, activeRow }) {
</AnimatePresence> </AnimatePresence>
</div> </div>
{/* 컨펌: 패널 안에서 블러 오버레이 */}
<AnimatePresence> <AnimatePresence>
{phase === "confirm" && ( {phase === "confirm" && (
<motion.div <motion.div className="utm-panel__confirm-overlay" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.25 }}>
className="utm-panel__confirm-overlay" <motion.div className="utm-confirm" initial={{ opacity: 0, scale: 0.92, y: 16 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.95 }} transition={{ duration: 0.35, ease }}>
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.25 }}
>
<motion.div
className="utm-confirm"
initial={{ opacity: 0, scale: 0.92, y: 16 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.35, ease }}
>
<div className="utm-confirm__icon"></div> <div className="utm-confirm__icon"></div>
<div className="utm-confirm__title"> <div className="utm-confirm__title">비행계획을 승인하시겠습니까?</div>
비행계획을 승인하시겠습니까? <div className="utm-confirm__desc">FP-001 · 홍길동 · 서울 김포</div>
</div>
<div className="utm-confirm__desc">
FP-001 · 홍길동 · 서울 김포
</div>
<div className="utm-confirm__btns"> <div className="utm-confirm__btns">
<span className="utm-confirm__cancel">취소</span> <span className="utm-confirm__cancel">취소</span>
<span className="utm-confirm__ok">확인</span> <span className="utm-confirm__ok">확인</span>
@ -174,7 +136,21 @@ function MainUtm() {
const [activeRow] = useState(0); const [activeRow] = useState(0);
const cursorRef = useRef(null); const cursorRef = useRef(null);
// useEffect ! useEffect(() => {
if (!inView) return;
let t;
const run = (current) => {
const next = PHASES[(PHASES.indexOf(current) + 1) % PHASES.length];
t = setTimeout(() => {
setPhase(next);
run(next);
}, PHASE_DURATION[current]);
};
t = setTimeout(() => run("list"), 1000);
return () => clearTimeout(t);
}, [inView]);
//
useEffect(() => { useEffect(() => {
if (!inView) return; if (!inView) return;
@ -182,13 +158,7 @@ function MainUtm() {
const cursor = cursorRef.current; const cursor = cursorRef.current;
if (!showcase || !cursor) return; if (!showcase || !cursor) return;
let stopped = false; let timers = [];
const ids = [];
function sid(id) {
ids.push(id);
return id;
}
function getPos(selector) { function getPos(selector) {
const el = ref.current?.querySelector(selector); const el = ref.current?.querySelector(selector);
@ -205,110 +175,86 @@ function MainUtm() {
cursor.style.transform = `translate(${x}px, ${y}px)`; cursor.style.transform = `translate(${x}px, ${y}px)`;
} }
function doClick() { function click() {
cursor.classList.add("utm-cursor--click"); cursor.classList.add("utm-cursor--click");
sid(setTimeout(() => cursor.classList.remove("utm-cursor--click"), 250)); setTimeout(() => cursor.classList.remove("utm-cursor--click"), 250);
} }
function schedule(ms, fn) { function after(ms, fn) {
sid( const t = setTimeout(fn, ms);
setTimeout(() => { timers.push(t);
if (!stopped) fn();
}, ms),
);
} }
function run() { function loop() {
if (stopped) return; timers.forEach(clearTimeout);
timers = [];
setPhase("list"); setPhase("list");
moveTo(180, 160); after(1000, () => {
//
schedule(1000, () => {
const p = getPos(".utm-panel__row--active .utm-panel__btn"); const p = getPos(".utm-panel__row--active .utm-panel__btn");
if (p) moveTo(p.x, p.y); if (p) moveTo(p.x, p.y);
}); });
// detail after(2000, () => {
schedule(2000, () => doClick()); click();
schedule(2200, () => setPhase("detail")); after(200, () => setPhase("detail"));
});
// after(3200, () => {
schedule(3200, () => {
const p = getPos(".utm-panel__approve-btn"); const p = getPos(".utm-panel__approve-btn");
if (p) moveTo(p.x, p.y); if (p) moveTo(p.x, p.y);
}); });
// confirm after(4200, () => {
schedule(4200, () => doClick()); click();
schedule(4400, () => setPhase("confirm")); after(200, () => setPhase("confirm"));
});
// after(5000, () => {
schedule(5000, () => {
const p = getPos(".utm-confirm__ok"); const p = getPos(".utm-confirm__ok");
if (p) moveTo(p.x, p.y); if (p) moveTo(p.x, p.y);
}); });
// done after(5800, () => {
schedule(5800, () => doClick()); click();
schedule(6000, () => setPhase("done")); after(200, () => setPhase("done"));
});
// after(7500, () => loop());
schedule(7500, () => run());
} }
run(); moveTo(180, 160);
after(500, () => loop());
return () => { return () => {
stopped = true; timers.forEach(clearTimeout);
ids.forEach(clearTimeout); timers = [];
cursor.classList.remove("utm-cursor--click"); cursor.classList.remove("utm-cursor--click");
setPhase("list"); // cleanup
}; };
}, [inView]); }, [inView]);
return ( return (
<section className="utm-hero" ref={ref}> <section className="utm-hero" ref={ref}>
<div className="utm-hero__blob utm-hero__blob--1" /> <div className="utm-hero__blob utm-hero__blob--1" />
<div className="utm-hero__blob utm-hero__blob--2" /> <div className="utm-hero__blob utm-hero__blob--2" />
<div className="utm-hero__inner"> <div className="utm-hero__inner">
<motion.span <motion.span className="utm-hero__eyebrow" initial={{ opacity: 0, y: 12 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, ease }}>
className="utm-hero__eyebrow"
initial={{ opacity: 0, y: 12 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, ease }}
>
UTM / UATM PLATFORM UTM / UATM PLATFORM
</motion.span> </motion.span>
<motion.h2 <motion.h2 className="utm-hero__title" initial={{ opacity: 0, y: 24 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.7, delay: 0.08, ease }}>
className="utm-hero__title"
initial={{ opacity: 0, y: 24 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.7, delay: 0.08, ease }}
>
드론 비행의 모든 과정을 드론 비행의 모든 과정을
<br /> <br />
<em>하나의 플랫폼에서</em> <em>하나의 플랫폼에서</em>
</motion.h2> </motion.h2>
<motion.p <motion.p className="utm-hero__desc" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.16, ease }}>
className="utm-hero__desc"
initial={{ opacity: 0, y: 16 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.16, ease }}
>
비행 계획부터 승인, 실시간 관제, 데이터 관리까지 비행 계획부터 승인, 실시간 관제, 데이터 관리까지
<br /> 안전하고 효율적인 하늘길을 만듭니다. <br /> 안전하고 효율적인 하늘길을 만듭니다.
</motion.p> </motion.p>
<motion.ul <motion.ul className="utm-hero__chips" initial={{ opacity: 0, y: 16 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, delay: 0.24, ease }}>
className="utm-hero__chips"
initial={{ opacity: 0, y: 16 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.24, ease }}
>
{FEATURES.map((f) => ( {FEATURES.map((f) => (
<li key={f.num} className="utm-hero__chip"> <li key={f.num} className="utm-hero__chip">
<span className="utm-hero__chip-num">{f.num}</span> <span className="utm-hero__chip-num">{f.num}</span>
@ -317,23 +263,14 @@ function MainUtm() {
))} ))}
</motion.ul> </motion.ul>
<motion.div <motion.div className="utm-hero__showcase" initial={{ opacity: 0, y: 48 }} animate={inView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.9, delay: 0.32, ease }}>
className="utm-hero__showcase"
initial={{ opacity: 0, y: 48 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.9, delay: 0.32, ease }}
>
<div className="utm-hero__map-wrap"> <div className="utm-hero__map-wrap">
<img <img src={utmMapImg} alt="UTM 관제 지도" className="utm-hero__map-img" draggable="false" />
src={utmMapImg}
alt="UTM 관제 지도"
className="utm-hero__map-img"
draggable="false"
/>
</div> </div>
<UtmSystemPanel phase={phase} activeRow={activeRow} /> <UtmSystemPanel phase={phase} activeRow={activeRow} />
{/* 커서 */}
<div className="utm-cursor" ref={cursorRef}> <div className="utm-cursor" ref={cursorRef}>
<div className="utm-cursor__dot" /> <div className="utm-cursor__dot" />
</div> </div>

3
src/css/common.css

@ -1382,4 +1382,5 @@ body{overflow-x:hidden;}
.utm-cap__cards { padding: 16px; } .utm-cap__cards { padding: 16px; }
.utm-cap__card { padding: 20px 16px; } .utm-cap__card { padding: 20px 16px; }
} }

Loading…
Cancel
Save