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. 1
      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" />
<title>PAL Networks</title>
<script type="module" crossorigin src="/PALNetworks/assets/index-Co9kRHX5.js"></script>
<link rel="stylesheet" crossorigin href="/PALNetworks/assets/index-uDnQuu9U.css">
<script type="module" crossorigin src="/PALNetworks/assets/index-DvqRPidd.js"></script>
<link rel="stylesheet" crossorigin href="/PALNetworks/assets/index-DFlth-pl.css">
</head>
<body>
<div id="root"></div>

201
src/components/main/MainUtm.jsx

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

1
src/css/common.css

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

Loading…
Cancel
Save