feat: POP v2 하드코딩 화면 — 홈 + 입고 프로세스
- 홈 화면: KPI 캐러셀, 메뉴 아이콘, 최근 활동, 공지 배너
- 입고유형선택: 외부 7개 + 내부 3개 아이콘, 금일 입고 KPI
- 구매입고: 거래처 선택, 발주 품목 카드, 담기/취소
- 장바구니: 체크박스, 포장 정보, 검사 상태, 확정
- 숫자 키패드: 터치 입력, MAX, 포장등록
- 포장 선택: 6종 단위 (박스/포대/팩/묶음/롤/통)
- 검사 모달: 마스터 기반 체크리스트, 측정값, 양품/불량
- 공통 PopShell: 헤더(시계+프로필), 배너, 푸터
- 반응형 4모드 (태블릿/핸드폰 가로세로)
2026-04-01 17:19:12 +09:00
"use client" ;
import React , { useState , useEffect , ReactNode } from "react" ;
import { useRouter } from "next/navigation" ;
interface PopShellProps {
children : ReactNode ;
showBanner? : boolean ;
title? : string ;
showBack? : boolean ;
2026-04-01 17:41:01 +09:00
headerRight? : ReactNode ;
feat: POP v2 하드코딩 화면 — 홈 + 입고 프로세스
- 홈 화면: KPI 캐러셀, 메뉴 아이콘, 최근 활동, 공지 배너
- 입고유형선택: 외부 7개 + 내부 3개 아이콘, 금일 입고 KPI
- 구매입고: 거래처 선택, 발주 품목 카드, 담기/취소
- 장바구니: 체크박스, 포장 정보, 검사 상태, 확정
- 숫자 키패드: 터치 입력, MAX, 포장등록
- 포장 선택: 6종 단위 (박스/포대/팩/묶음/롤/통)
- 검사 모달: 마스터 기반 체크리스트, 측정값, 양품/불량
- 공통 PopShell: 헤더(시계+프로필), 배너, 푸터
- 반응형 4모드 (태블릿/핸드폰 가로세로)
2026-04-01 17:19:12 +09:00
}
2026-04-01 17:41:01 +09:00
export function PopShell ( { children , showBanner = true , title , showBack = false , headerRight } : PopShellProps ) {
feat: POP v2 하드코딩 화면 — 홈 + 입고 프로세스
- 홈 화면: KPI 캐러셀, 메뉴 아이콘, 최근 활동, 공지 배너
- 입고유형선택: 외부 7개 + 내부 3개 아이콘, 금일 입고 KPI
- 구매입고: 거래처 선택, 발주 품목 카드, 담기/취소
- 장바구니: 체크박스, 포장 정보, 검사 상태, 확정
- 숫자 키패드: 터치 입력, MAX, 포장등록
- 포장 선택: 6종 단위 (박스/포대/팩/묶음/롤/통)
- 검사 모달: 마스터 기반 체크리스트, 측정값, 양품/불량
- 공통 PopShell: 헤더(시계+프로필), 배너, 푸터
- 반응형 4모드 (태블릿/핸드폰 가로세로)
2026-04-01 17:19:12 +09:00
const router = useRouter ( ) ;
const [ mounted , setMounted ] = useState ( false ) ;
const [ hours , setHours ] = useState ( "00" ) ;
const [ minutes , setMinutes ] = useState ( "00" ) ;
const [ seconds , setSeconds ] = useState ( "00" ) ;
const [ dateStr , setDateStr ] = useState ( "2026-01-01" ) ;
const [ colonVisible , setColonVisible ] = useState ( true ) ;
useEffect ( ( ) = > {
setMounted ( true ) ;
function tick() {
const now = new Date ( ) ;
setHours ( String ( now . getHours ( ) ) . padStart ( 2 , "0" ) ) ;
setMinutes ( String ( now . getMinutes ( ) ) . padStart ( 2 , "0" ) ) ;
setSeconds ( String ( now . getSeconds ( ) ) . padStart ( 2 , "0" ) ) ;
setDateStr (
` ${ now . getFullYear ( ) } - ${ String ( now . getMonth ( ) + 1 ) . padStart ( 2 , "0" ) } - ${ String ( now . getDate ( ) ) . padStart ( 2 , "0" ) } `
) ;
}
tick ( ) ;
const clockInterval = setInterval ( tick , 1000 ) ;
const blinkInterval = setInterval ( ( ) = > {
setColonVisible ( ( v ) = > ! v ) ;
} , 500 ) ;
return ( ) = > {
clearInterval ( clockInterval ) ;
clearInterval ( blinkInterval ) ;
} ;
} , [ ] ) ;
const marqueeText =
"[공지] 금일 오후 3시 전체 안전교육 실시 예정입니다. 전 직원 필참 바랍니다. \u00a0\u00a0|\u00a0\u00a0 [알림] 내일 설비 정기점검으로 인한 3호기 가동 중지 예정 \u00a0\u00a0|\u00a0\u00a0 [안내] 4월 생산실적 우수팀 발표 - 생산1팀 축하드립니다!" ;
return (
< div className = "min-h-screen min-h-dvh flex flex-col" style = { { background : "#F5F5F5" } } >
{ /* ===== HEADER ===== */ }
< header
className = "sticky top-0 z-50 flex items-center justify-between px-4 sm:px-6 lg:px-8 py-3"
style = { { background : "#1a1a2e" } }
>
{ /* Left: Back + Logo + Company */ }
< div className = "flex items-center gap-3 min-w-0" >
{ showBack && (
< button
onClick = { ( ) = > router . back ( ) }
className = "w-10 h-10 rounded-xl bg-white/10 flex items-center justify-center shrink-0 hover:bg-white/20 active:scale-95 transition-all"
>
< svg className = "w-5 h-5 text-white" fill = "none" stroke = "currentColor" strokeWidth = { 2 } viewBox = "0 0 24 24" >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M15.75 19.5L8.25 12l7.5-7.5" / >
< / svg >
< / button >
) }
< div
className = "flex items-center gap-3 min-w-0 cursor-pointer"
onClick = { ( ) = > router . push ( "/pop/home" ) }
>
< div
className = "w-10 h-10 rounded-xl bg-blue-500 flex items-center justify-center shrink-0"
style = { { boxShadow : "0 4px 12px rgba(59,130,246,.35)" } }
>
< svg
className = "w-5 h-5 text-white"
fill = "none"
stroke = "currentColor"
strokeWidth = { 2 }
viewBox = "0 0 24 24"
>
< path
strokeLinecap = "round"
strokeLinejoin = "round"
d = "M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z"
/ >
< / svg >
< / div >
< div className = "flex flex-col min-w-0" >
{ title ? (
< span className = "text-white text-lg font-bold tracking-tight leading-tight truncate" >
{ title }
< / span >
) : (
< >
< span className = "text-white text-lg font-bold tracking-tight leading-tight truncate" >
탑 씰
< / span >
< span className = "text-white/50 text-xs font-medium leading-tight" >
현 장 관 리 시 스 템
< / span >
< / >
) }
< / div >
< / div >
< / div >
{ /* Center: Clock (desktop) */ }
< div className = "hidden sm:flex flex-col items-center" >
{ mounted && (
< >
< div
className = "flex items-center text-white font-bold text-2xl tracking-wider"
style = { { fontVariantNumeric : "tabular-nums" } }
>
< span > { hours } < / span >
< span
className = "transition-opacity duration-100"
style = { { opacity : colonVisible ? 1 : 0 } }
>
:
< / span >
< span > { minutes } < / span >
< span
className = "transition-opacity duration-100"
style = { { opacity : colonVisible ? 1 : 0 } }
>
:
< / span >
< span > { seconds } < / span >
< / div >
< span className = "text-white/50 text-xs font-medium mt-0.5" > { dateStr } < / span >
< / >
) }
< / div >
{ /* Right: Mobile clock + Profile */ }
< div className = "flex items-center gap-3" >
{ /* Mobile clock */ }
{ mounted && (
< div className = "sm:hidden flex items-center gap-1.5 text-white/60 text-sm" >
< svg
className = "w-4 h-4"
fill = "none"
stroke = "currentColor"
strokeWidth = { 2 }
viewBox = "0 0 24 24"
>
< path
strokeLinecap = "round"
strokeLinejoin = "round"
d = "M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"
/ >
< / svg >
< span >
{ hours } : { minutes }
< / span >
< / div >
) }
2026-04-01 17:41:01 +09:00
{ /* Custom header right content (e.g. cart icon) */ }
{ headerRight }
feat: POP v2 하드코딩 화면 — 홈 + 입고 프로세스
- 홈 화면: KPI 캐러셀, 메뉴 아이콘, 최근 활동, 공지 배너
- 입고유형선택: 외부 7개 + 내부 3개 아이콘, 금일 입고 KPI
- 구매입고: 거래처 선택, 발주 품목 카드, 담기/취소
- 장바구니: 체크박스, 포장 정보, 검사 상태, 확정
- 숫자 키패드: 터치 입력, MAX, 포장등록
- 포장 선택: 6종 단위 (박스/포대/팩/묶음/롤/통)
- 검사 모달: 마스터 기반 체크리스트, 측정값, 양품/불량
- 공통 PopShell: 헤더(시계+프로필), 배너, 푸터
- 반응형 4모드 (태블릿/핸드폰 가로세로)
2026-04-01 17:19:12 +09:00
< div className = "hidden sm:block h-5 w-px bg-white/20" / >
{ /* Profile */ }
< div className = "flex items-center gap-2.5" >
< div className = "hidden sm:flex flex-col items-end" >
< span className = "text-sm text-white/90 font-semibold leading-tight" > 김 철 수 < / span >
< span className = "text-xs text-white/40 font-medium leading-tight" > 생 산 1 팀 < / span >
< / div >
< div
className = "w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-sm font-bold text-white shrink-0"
style = { { boxShadow : "0 2px 8px rgba(59,130,246,.35)" } }
>
김
< / div >
< / div >
< / div >
< / header >
{ /* ===== NOTICE BANNER (Marquee) ===== */ }
{ showBanner && < div className = "bg-amber-50 border-b border-amber-200 px-4 py-2 flex items-center gap-3" >
< div className = "flex items-center gap-1.5 shrink-0" >
< span className = "text-amber-600 text-sm" > 📢 < / span >
< span className = "text-xs font-bold text-amber-700" > 공 지 < / span >
< / div >
< div className = "overflow-hidden whitespace-nowrap flex-1" >
< div
className = "inline-block text-sm text-amber-800"
style = { {
animation : "popMarquee 30s linear infinite" ,
} }
>
{ marqueeText }
< / div >
< / div >
< / div > }
{ /* ===== MAIN CONTENT ===== */ }
< main className = "max-w-[1400px] mx-auto w-full px-4 sm:px-6 lg:px-8 py-5 sm:py-6 flex flex-col gap-5 sm:gap-6 flex-1 overflow-y-auto" >
{ children }
< / main >
{ /* ===== FOOTER ===== */ }
< footer className = "border-t border-gray-200 bg-white px-4 sm:px-6 lg:px-8 py-3 sm:py-4" >
< div className = "max-w-[1400px] mx-auto flex flex-col sm:flex-row items-center justify-between gap-2 text-xs text-gray-400" >
< span > & copy ; 2026 탑 씰 . All rights reserved . < / span >
< div className = "flex items-center gap-3 sm:gap-4" >
< span > Version 1.0 . 0 < / span >
< span className = "hidden sm:inline" > | < / span >
< span > 긴급연락 : 042 - XXX - XXXX < / span >
< / div >
< / div >
< / footer >
{ /* Marquee keyframes */ }
< style jsx global > { `
@keyframes popMarquee {
0 % {
transform : translateX ( 100 % ) ;
}
100 % {
transform : translateX ( - 100 % ) ;
}
}
` }</style>
< / div >
) ;
}