get('logged_in')) { // 메인(/) — gov-portal 디자인 셸 + 종량제 실데이터 대시보드. helper('admin'); return view('bag/layout/portal', [ 'title' => '업무 현황', 'bare' => true, 'content' => view('bag/dashboard_portal', $this->portalDashboardData()), ]); } return view('welcome_message'); } /** * 메인 대시보드용 — 종량제 시스템에 실제 존재하는 데이터만 집계. * * @return array */ private function portalDashboardData(): array { helper('admin'); $db = \Config\Database::connect(); $lgIdx = function_exists('admin_effective_lg_idx') ? admin_effective_lg_idx() : null; if ($lgIdx === null) { $raw = session()->get('mb_lg_idx'); $lgIdx = ($raw !== null && $raw !== '') ? (int) $raw : null; } $inventory = []; $totalQty = 0; $orderCount = 0; $palette = ['#3b82f6', '#10b981', '#f59e0b', '#6366f1', '#ef4444', '#0ea5e9', '#14b8a6', '#a855f7', '#f97316']; try { if ($lgIdx !== null && $db->tableExists('bag_inventory')) { $rows = $db->table('bag_inventory') ->select('bi_bag_name, bi_bag_code, bi_qty') ->where('bi_lg_idx', $lgIdx) ->orderBy('bi_qty', 'DESC') ->get()->getResultArray(); foreach ($rows as $r) { $inventory[] = [ 'name' => (string) ($r['bi_bag_name'] ?? $r['bi_bag_code'] ?? ''), 'qty' => (int) ($r['bi_qty'] ?? 0), ]; $totalQty += (int) ($r['bi_qty'] ?? 0); } } } catch (\Throwable $e) { $inventory = []; } // 재고 구성(상위 품목 비율) $stockMix = []; foreach (array_slice($inventory, 0, 6) as $i => $item) { $stockMix[] = [ 'name' => $item['name'], 'value' => $totalQty > 0 ? (int) round($item['qty'] / $totalQty * 100) : 0, 'color' => $palette[$i % count($palette)], ]; } // 부족 재고(수량 적은 하위 품목) — 최대 재고 대비 비율 $maxQty = $inventory !== [] ? max(array_column($inventory, 'qty')) : 0; $lowStock = []; foreach (array_slice(array_reverse($inventory), 0, 5) as $item) { $lowStock[] = [ 'name' => $item['name'], 'qty' => $item['qty'], 'percent' => $maxQty > 0 ? (int) round($item['qty'] / $maxQty * 100) : 0, ]; } try { if ($lgIdx !== null && $db->tableExists('shop_order')) { $orderCount = (int) $db->table('shop_order') ->where('so_lg_idx', $lgIdx) ->where('so_status', 'normal') ->countAllResults(); } } catch (\Throwable $e) { $orderCount = 0; } $pendingApprovals = 0; try { if ($db->tableExists('member_approval_request')) { $pendingApprovals = (int) $db->table('member_approval_request') ->where('mar_status', 'pending') ->countAllResults(); } } catch (\Throwable $e) { $pendingApprovals = 0; } // 최근 활동(activity_log) — 실제 변경 이력 $actionLabel = ['create' => '등록', 'update' => '수정', 'delete' => '삭제', 'cancel' => '취소']; $tableLabel = [ 'bag_order' => '발주', 'bag_receiving' => '입고', 'bag_sale' => '판매', 'bag_issue' => '불출', 'bag_inventory' => '재고', 'shop_order' => '주문접수', 'designated_shop' => '지정판매소', 'bag_price' => '단가', 'member' => '회원', ]; $recent = []; try { if ($db->tableExists('activity_log')) { $logs = $db->table('activity_log') ->select('al_action, al_table, al_regdate') ->orderBy('al_idx', 'DESC')->limit(6)->get()->getResultArray(); foreach ($logs as $l) { $t = (string) ($l['al_regdate'] ?? ''); $recent[] = [ 'time' => $t !== '' ? date('m.d H:i', strtotime($t)) : '', 'text' => ($tableLabel[$l['al_table']] ?? (string) $l['al_table']) . ' ' . ($actionLabel[$l['al_action']] ?? (string) $l['al_action']), ]; } } } catch (\Throwable $e) { $recent = []; } return [ 'lgLabel' => $this->resolveLgLabel(), 'mbName' => (string) (session()->get('mb_name') ?? '담당자'), 'mbId' => (string) (session()->get('mb_id') ?? ''), 'levelName' => config(\Config\Roles::class)->getLevelName((int) session()->get('mb_level')), 'totalQty' => $totalQty, 'itemCount' => count($inventory), 'orderCount' => $orderCount, 'pendingApprovals' => $pendingApprovals, 'stockMix' => $stockMix, 'lowStock' => $lowStock, 'recentActivity' => $recent, ]; } /** * 로그인 후 메인 — site 메뉴 레이아웃 + 종합·그래프(blend) 본문 */ public function dashboard() { return view('bag/layout/main', [ 'title' => '업무 현황 · 종합·그래프', 'content' => view('bag/dashboard_blend_inner', [ 'lgLabel' => $this->resolveLgLabel(), ]), ]); } /** * 로그인 후 메인 — 단순형 요약 대시보드. URL: /dashboard/simple * 기존 /dashboard 화면이 복잡하다는 피드백용으로, 핵심 지표·링크만 노출. */ public function dashboardSimple() { return view('bag/layout/main', [ 'title' => '업무 현황 · 요약', 'content' => view('bag/lg_dashboard_simple', [ 'lgLabel' => $this->resolveLgLabel(), ]), ]); } /** * 로그인 후 메인 — 중간 밀도 대시보드. URL: /dashboard/compact * /dashboard 보다 단순하지만 simple 보다 정보량을 늘린 화면. */ public function dashboardCompact() { return view('bag/layout/main', [ 'title' => '업무 현황 · 컴팩트', 'content' => view('bag/lg_dashboard_compact', [ 'lgLabel' => $this->resolveLgLabel(), ]), ]); } /** * 디자인 시안(기존 /dashboard 연결 화면) */ public function dashboardClassicMock() { return $this->renderDashboard(); } /** * 로그인 후 메인 — 모던형(세로 사이드바) 레이아웃. URL: /dashboard/modern */ public function dashboardModern() { return view('bag/lg_dashboard_modern', [ 'lgLabel' => $this->resolveLgLabel(), ]); } /** * 로그인 후 메인 — 정보 집약형 종합 현황. URL: /dashboard/dense */ public function dashboardDense() { return view('bag/lg_dashboard_dense', [ 'lgLabel' => $this->resolveLgLabel(), ]); } /** * 로그인 후 메인 — 그래프 중심(Chart.js). URL: /dashboard/charts */ public function dashboardCharts() { return view('bag/lg_dashboard_charts', [ 'lgLabel' => $this->resolveLgLabel(), ]); } /** * /dashboard 와 동일 본문(호환 URL) */ public function dashboardBlend() { return $this->dashboard(); } /** * 로그인 후 메인 — 라이트(축약) 대시보드. URL: /dashboard/lite * dashboard_blend 의 일부 KPI/표/차트만 남긴 단순화 화면. */ public function dashboardLite() { return view('bag/layout/main', [ 'title' => '업무 현황 · 라이트', 'content' => view('bag/dashboard_blend_lite_inner', [ 'lgLabel' => $this->resolveLgLabel(), ]), ]); } /** * 공공 포털형(국가재난관리정보시스템 스타일) 메인 시안. * URL: /dashboard/gov-portal — 기능은 요약 대시보드와 동일, UI만 별도 레이아웃. */ public function dashboardGovPortal() { if (! session()->get('logged_in')) { return redirect()->to(base_url('login')); } helper('admin'); return view('home/dashboard_gov_portal', gov_portal_dashboard_view_data($this->resolveLgLabel(), 'base')); } /** * 공공 포털형 변형 — 가로 MY MENU·와이드 맵·KPI 띠. URL: /dashboard/gov-portal-strip */ public function dashboardGovPortalStrip() { if (! session()->get('logged_in')) { return redirect()->to(base_url('login')); } helper('admin'); return view('home/_dashboard_gov_portal_strip_layout', array_merge( gov_portal_dashboard_view_data($this->resolveLgLabel(), 'strip'), ['stripInnerView' => 'home/_dashboard_gov_portal_strip_home_inner'] )); } /** * 공공 포털형(기본) — 기본 코드관리 UI 시안. URL: /dashboard/gov-portal/code-kinds */ public function dashboardGovPortalCodeKinds() { return $this->renderGovPortalCodeKinds('base'); } /** * 공공 포털형(변형 strip) — 기본 코드관리 UI 시안. URL: /dashboard/gov-portal-strip/code-kinds */ public function dashboardGovPortalStripCodeKinds() { return $this->renderGovPortalCodeKinds('strip'); } /** * @return \CodeIgniter\HTTP\RedirectResponse|string */ private function renderGovPortalCodeKinds(string $variant) { if (! session()->get('logged_in')) { return redirect()->to(base_url('login')); } helper('admin'); $portalPath = gov_portal_code_kinds_portal_path($variant); $lgIdx = admin_effective_lg_idx(); $level = (int) session()->get('mb_level'); $filters = [ 'q_code' => $this->request->getGet('q_code'), 'q_name' => $this->request->getGet('q_name'), 'q_state' => $this->request->getGet('q_state'), ]; $builder = new GovPortalCodeKindsPage(); $ckIdx = (int) ($this->request->getGet('ck_idx') ?? 0); $pageData = $builder->buildPageData($lgIdx, $level, $lgIdx, $ckIdx, $filters); if ($ckIdx === 0 && $pageData['codeKinds'] !== []) { $pageData = $builder->buildPageData( $lgIdx, $level, $lgIdx, (int) $pageData['codeKinds'][0]->ck_idx, $filters ); } $viewData = array_merge( gov_portal_dashboard_view_data($this->resolveLgLabel(), $variant), $pageData, [ 'govActiveChildHref' => $portalPath, 'pageBaseUrl' => site_url($portalPath), ] ); if ($variant === 'strip') { return view('home/_dashboard_gov_portal_strip_layout', array_merge($viewData, [ 'stripInnerView' => 'home/_gov_portal_code_kinds_body', 'stripIncludeWorkCss' => true, 'stripShowProfileLinks' => true, ])); } return view('home/dashboard_gov_portal_code_kinds', $viewData); } /** * 재고 조회(수불) 화면 (목업) */ public function inventoryInquiry() { return view('bag/inventory_inquiry'); } /** * 종량제 수불 그리드 (엔터프라이즈형 목업, 상단 가로 메뉴 + 병합 헤더 표) */ public function wasteSuibalEnterprise() { return view('bag/waste_suibal_enterprise'); } protected function renderDashboard() { return view('bag/lg_dashboard', [ 'lgLabel' => $this->resolveLgLabel(), ]); } /** * 세션 mb_lg_idx 기준 지자체명 (DB 없거나 실패 시 데모용 문구) */ protected function resolveLgLabel(): string { try { $idx = session()->get('mb_lg_idx'); if ($idx === null || $idx === '') { return '로그인 지자체 (미지정)'; } $row = model(LocalGovernmentModel::class)->find((int) $idx); if ($row && isset($row->lg_name) && $row->lg_name !== '') { return (string) $row->lg_name; } } catch (\Throwable $e) { // 테이블 미생성 등 } return '북구 (데모)'; } }