Files
invyone/frontend/styles/mobile-alarm.css
T
gbpark 7635412b7b feat: SCADA 시연용 모바일 알람 동반 화면 + Spring WebSocket
- 작업자 폰(/mobile)을 SCADA 데모와 ws 로 연결, 알람 발생 시 풀스크린 푸시
  · v5 솔리드+글로우 톤, 진동/Web Audio 비프/Wake Lock/auto reconnect
  · 시연 안전망: ?test=1 자동 발동, 우상단 hidden 트리거
- backend: com.erp.alarm 신규 패키지 (WebSocketConfig + Handshake + Handler + Controller)
  · JWT 토큰 핸드셰이크 검증, userId 기반 채널 매핑 (멀티 디바이스 지원)
  · spring-boot-starter-websocket 의존성 추가
  · path 를 /api/demo/* 안에 두어 Traefik 라우트 추가 불필요 + 정식 알람과 분리
- SCADA scenario.js 의 emergency 시퀀스(2700ms)에 fetch('/api/demo/alarm/trigger') 배선
  · /scada?worker=<user_id> query 로 target user 지정 (iframe src 로 전달)
- 운영 시연 URL: siflex.invyone.com/mobile (siflex_user) ↔ /scada?worker=siflex_user

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 01:18:12 +09:00

168 lines
5.6 KiB
CSS

/* INVYONE Mobile Alarm — 작업자 동반 화면 (siflex.invyone.com/mobile)
* v5 토큰만 사용. 메인 화면 이후 영역이라 backdrop-filter 금지 — solid + glow.
* 알람 모드는 위험 분위기 (red/orange) 로 전환. */
.m-shell{
position:fixed; inset:0;
display:flex; flex-direction:column;
background:var(--v5-surface-solid);
color:var(--v5-text);
overflow:hidden;
font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Apple SD Gothic Neo","Malgun Gothic",sans-serif;
-webkit-tap-highlight-color:transparent;
user-select:none;
touch-action:manipulation;
}
/* ===== 헤더 (브랜드 + 연결 상태 + 사용자) ===== */
.m-head{
flex:0 0 auto;
display:flex; align-items:center; justify-content:space-between; gap:10px;
padding:14px 18px;
border-bottom:1px solid var(--v5-border);
font-size:13px;
}
.m-brand{
font-weight:700; letter-spacing:0.02em;
background:linear-gradient(135deg,var(--v5-primary),var(--v5-cyan));
-webkit-background-clip:text; background-clip:text; color:transparent;
}
.m-conn{display:inline-flex; align-items:center; gap:6px; font-size:11px; color:#888;}
.m-conn.ok{color:#4ade80}
.m-conn.off{color:#f87171}
.m-dot{width:8px; height:8px; border-radius:50%; background:currentColor; box-shadow:0 0 8px currentColor;}
.m-conn.ok .m-dot{animation:m-pulse 1.5s ease-in-out infinite}
@keyframes m-pulse{0%,100%{opacity:1} 50%{opacity:0.45}}
/* ===== IDLE — 평상시 ===== */
.m-idle{
flex:1 1 auto;
display:flex; flex-direction:column; align-items:center; justify-content:center;
gap:14px; position:relative;
}
.m-clock{
font-size:clamp(56px,16vw,110px);
font-weight:200; letter-spacing:0.04em;
font-variant-numeric:tabular-nums;
background:linear-gradient(135deg,var(--v5-primary),var(--v5-cyan));
-webkit-background-clip:text; background-clip:text; color:transparent;
text-shadow:0 0 30px rgba(var(--v5-primary-rgb),0.18);
z-index:1;
}
.m-user{font-size:16px; color:var(--v5-text); z-index:1}
.m-user-dept{font-size:12px; color:#888; margin-top:-8px; z-index:1}
.m-idle-hint{
font-size:12px; color:#888;
margin-top:24px; padding:0 24px; text-align:center; line-height:1.6;
z-index:1;
}
.m-glow-orb{
position:absolute;
width:340px; height:340px; border-radius:50%;
background:radial-gradient(circle,
rgba(var(--v5-primary-rgb),0.20),
rgba(var(--v5-cyan-rgb),0.10) 50%,
transparent 75%);
animation:m-orb 6s ease-in-out infinite;
z-index:0;
pointer-events:none;
}
@keyframes m-orb{
0%,100%{transform:scale(1); opacity:0.85}
50% {transform:scale(1.15); opacity:1}
}
/* ===== ALARM — 위험 모드 ===== */
.m-shell-alarm{background:#0d0202; color:#fff}
.m-shell-alarm .m-head{
background:rgba(255,45,107,0.08);
border-bottom-color:rgba(255,79,154,0.4);
}
.m-shell-alarm .m-brand{
background:linear-gradient(135deg,#ff2d6b,#ff8a3a);
-webkit-background-clip:text; background-clip:text; color:transparent;
}
.m-alarm{flex:1 1 auto; position:relative; overflow:hidden}
.m-alarm-bg{
position:absolute; inset:0;
background:
radial-gradient(circle at 50% 30%, rgba(255,79,154,0.35), transparent 60%),
repeating-linear-gradient(135deg,
rgba(255,79,154,0.05) 0 14px,
transparent 14px 28px);
animation:m-bg-pulse 1.4s ease-in-out infinite;
pointer-events:none;
}
@keyframes m-bg-pulse{0%,100%{opacity:0.85} 50%{opacity:1}}
.m-alarm-card{
position:relative; height:100%;
display:flex; flex-direction:column; align-items:center; justify-content:center;
padding:32px 22px; text-align:center; gap:12px;
animation:m-shake 0.55s ease-in-out 0s 3;
}
@keyframes m-shake{
0%,100%{transform:translateX(0)}
20%{transform:translateX(-6px)}
40%{transform:translateX(6px)}
60%{transform:translateX(-4px)}
80%{transform:translateX(4px)}
}
.m-alarm-sev{
font-size:11px; letter-spacing:0.18em; font-weight:700;
color:#fff; background:#ff2d6b;
padding:4px 12px; border-radius:4px;
box-shadow:0 0 14px rgba(255,45,107,0.6);
}
.m-alarm-icon{
font-size:92px; line-height:1;
filter:drop-shadow(0 0 22px rgba(255,79,154,0.7));
animation:m-icon-pulse 0.8s ease-in-out infinite;
}
@keyframes m-icon-pulse{
0%,100%{transform:scale(1)}
50% {transform:scale(1.08)}
}
.m-alarm-code{font-size:28px; font-weight:800; letter-spacing:0.08em; color:#ff8a3a}
.m-alarm-title{font-size:18px; font-weight:600; color:#fff; max-width:340px; line-height:1.4}
.m-alarm-loc{
font-size:13px; color:#ffb380;
background:rgba(255,138,58,0.1);
padding:6px 14px; border-radius:14px;
border:1px solid rgba(255,138,58,0.4);
}
.m-alarm-msg{
font-size:14px; color:#ddd; line-height:1.6;
white-space:pre-wrap; max-width:360px; margin-top:8px;
}
.m-alarm-actions{
display:flex; flex-direction:column; gap:10px;
width:100%; max-width:320px; margin-top:22px;
}
.m-btn-ack{
background:linear-gradient(135deg,#ff2d6b,#ff8a3a);
color:#fff; font-size:16px; font-weight:700; letter-spacing:0.04em;
padding:16px; border:0; border-radius:12px;
box-shadow:0 6px 20px rgba(255,45,107,0.4), 0 0 30px rgba(255,138,58,0.3);
cursor:pointer;
}
.m-btn-ack:active{transform:translateY(1px)}
.m-btn-skip{
background:transparent; color:#aaa; font-size:13px;
padding:10px; border:1px solid rgba(255,255,255,0.15); border-radius:10px;
cursor:pointer;
}
.m-alarm-time{margin-top:14px; font-size:11px; color:#888; font-variant-numeric:tabular-nums}
/* 시연 안전망 — 우상단 hidden trigger */
.m-test-trigger{
position:fixed; top:8px; right:8px;
width:18px; height:18px; border-radius:50%;
background:rgba(var(--v5-primary-rgb),0.05);
border:0; cursor:pointer; opacity:0.4;
z-index:50;
}
.m-test-trigger:hover{opacity:0.9}