diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index e040a88..ac18c43 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -38,6 +38,7 @@ import AiTools from './pages/AiTools'
import UserManagement from './pages/UserManagement'
import AuditLog from './pages/AuditLog'
import SystemSettings from './pages/SystemSettings'
+import MobileApp from './pages/MobileApp'
// 업무 (UIWS 이식) — 업무일지/일정/쪽지/업무통계
import UiwsWorklog from './pages/uiws/WorklogList'
import UiwsSchedule from './pages/uiws/ScheduleCalendar'
@@ -102,6 +103,7 @@ export default function App() {
} />
+ } />
} />
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 8b5588c..c495916 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -5,7 +5,7 @@ import {
Gauge, Network, CalendarRange, PackagePlus, Truck, Boxes, ClipboardCheck, Barcode,
ScanSearch, Ruler, AlertTriangle, ListChecks, LineChart, FileBadge, Package, ListTree,
Route as RouteIcon, Building2, Warehouse, BarChart3, Sparkles, ShieldCheck, ScrollText,
- Settings, Cpu, NotebookPen, CalendarDays, Mail, PieChart,
+ Settings, Cpu, NotebookPen, CalendarDays, Mail, PieChart, Smartphone,
} from 'lucide-react'
import { currentRole, hasRole } from './rbac'
import ThemeToggle from './uiws/ThemeToggle'
@@ -63,6 +63,10 @@ const adminLinks: Link[] = [
{ to: '/admin/audit', label: '감사 로그', icon: ScrollText, min: 'MANAGER' },
{ to: '/admin/settings', label: '시스템 설정', icon: Settings, min: 'MANAGER' },
]
+// 통합 메신저 앱(읽기전용 QR 설치) — 인증 사용자 전체
+const mobileLinks: Link[] = [
+ { to: '/admin/mobile-app', label: '모바일 앱 설치', icon: Smartphone },
+]
const linkClass = ({ isActive }: { isActive: boolean }) =>
`flex items-center gap-3 px-5 py-1.5 text-sm transition-colors ${
@@ -120,6 +124,9 @@ export default function Sidebar() {
+
+
+
{visibleAdmin.length > 0 && (
diff --git a/frontend/src/pages/MobileApp.tsx b/frontend/src/pages/MobileApp.tsx
new file mode 100644
index 0000000..e09ba86
--- /dev/null
+++ b/frontend/src/pages/MobileApp.tsx
@@ -0,0 +1,123 @@
+import { useEffect, useState } from 'react'
+import { Smartphone, Download, Copy, Check, ExternalLink, RefreshCw } from 'lucide-react'
+
+// 중앙 APK 저장소(ITSM)의 공개 엔드포인트 — 인증 불필요·CORS 허용 (읽기전용)
+// 업로드·버전 관리는 GUARDiA Manager/ITSM에서 일원화. 본 화면은 표시 전용.
+const ITSM_APP_BASE = 'https://zioinfo.co.kr:8443'
+
+interface AppInfo {
+ has_version: boolean
+ app_name?: string
+ version?: string
+ platform?: string
+ file_size_mb?: number
+ release_notes?: string
+ download_count?: number
+ qr_url?: string
+ landing_url?: string
+ download_url?: string
+ updated_at?: string
+}
+
+export default function MobileApp() {
+ const [info, setInfo] = useState
(null)
+ const [loading, setLoading] = useState(true)
+ const [err, setErr] = useState('')
+ const [copied, setCopied] = useState(false)
+
+ const load = () => {
+ setLoading(true); setErr('')
+ fetch(`${ITSM_APP_BASE}/api/app/public-latest`)
+ .then(r => r.json())
+ .then((d: AppInfo) => setInfo(d))
+ .catch(() => setErr('버전 정보를 불러올 수 없습니다. (중앙 앱 저장소 연결 실패)'))
+ .finally(() => setLoading(false))
+ }
+ useEffect(() => { load() }, [])
+
+ const copyLink = async () => {
+ if (!info?.landing_url) return
+ try {
+ await navigator.clipboard.writeText(info.landing_url)
+ setCopied(true); setTimeout(() => setCopied(false), 2000)
+ } catch { /* clipboard 권한 없을 때 무시 */ }
+ }
+
+ return (
+
+
+
+
+
GUARDiA 통합 메신저 앱 설치
+
+
+
+
+
+ QR 코드를 스캔하면 GUARDiA Messenger 앱 설치 페이지로 이동합니다.
+ 앱스토어 설치가 필요 없으며, 앱 업로드·버전 관리는 GUARDiA Manager에서 일원화되어 있습니다.
+
+
+ {loading &&
불러오는 중…
}
+ {err &&
{err}
}
+
+ {!loading && !err && info && !info.has_version && (
+
+
+ 아직 등록된 앱 버전이 없습니다.
+ GUARDiA Manager에서 APK를 업로드하면 여기에 QR이 표시됩니다.
+
+ )}
+
+ {!loading && !err && info?.has_version && (
+
+
+

+
+
+
+ {info.app_name || 'GUARDiA Messenger'}
+ v{info.version}
+
+
+ {info.platform} · {info.file_size_mb ? `${info.file_size_mb}MB` : ''}
+ {info.download_count != null && ` · 다운로드 ${info.download_count}회`}
+
+
+ {info.release_notes && (
+
+
변경점
+
+ {info.release_notes}
+
+
+ )}
+
+
+
+
+ 📱 Android: APK 다운로드 후 설정 → 보안 → "알 수 없는 소스" 허용 → 설치.
+ 앱스토어 등록이 필요 없습니다.
+
+
+
+ )}
+
+ )
+}