Compare commits
2 Commits
c9af386129
...
cb8a99cb8d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb8a99cb8d | ||
|
|
e768136e5d |
BIN
frontend/public/screenshots/01_itsm_login.png
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
frontend/public/screenshots/02_itsm_dashboard.png
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
frontend/public/screenshots/home_02_greeting.png
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
frontend/public/screenshots/home_03_ci.png
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
frontend/public/screenshots/home_04_history.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
frontend/public/screenshots/home_05_org.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
frontend/public/screenshots/home_06_location.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
frontend/public/screenshots/home_08_business.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
frontend/public/screenshots/home_09_news.png
Normal file
|
After Width: | Height: | Size: 364 KiB |
BIN
frontend/public/screenshots/home_10_recruit.png
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
frontend/public/screenshots/home_11_contact.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
frontend/public/screenshots/manager_06_audit.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
frontend/public/screenshots/manager_login_debug.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
frontend/public/screenshots/manager_login_fail.png
Normal file
|
After Width: | Height: | Size: 170 KiB |
@ -1,126 +1,126 @@
|
||||
/* ─── Header ──────────────────────────────────────────────── */
|
||||
.skip-link {
|
||||
position: absolute; top: -60px; left: 0; z-index: 9999;
|
||||
background: var(--primary); color: #fff;
|
||||
padding: 10px 20px; border-radius: 0 0 8px 0;
|
||||
transition: top .2s;
|
||||
}
|
||||
.skip-link:focus { top: 0; }
|
||||
|
||||
.header {
|
||||
position: fixed; top: 0; left: 0; right: 0;
|
||||
z-index: 1000; height: var(--header-h);
|
||||
background: rgba(26, 26, 46, 0.96);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid rgba(255,255,255,.08);
|
||||
transition: all var(--mid) var(--ease);
|
||||
}
|
||||
.header.scrolled {
|
||||
background: rgba(26, 26, 46, 0.99);
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,.3);
|
||||
}
|
||||
.header-inner {
|
||||
display: flex; align-items: center; gap: 32px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 로고 */
|
||||
.logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0; cursor: pointer; text-decoration: none; }
|
||||
.logo img { height: 40px; width: auto; }
|
||||
.logo-text { color: #fff; font-size: 20px; font-weight: 700; }
|
||||
.logo-text strong { color: var(--accent); }
|
||||
|
||||
/* 데스크톱 메뉴 */
|
||||
.nav-desktop {
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
margin-left: 24px; flex: 1;
|
||||
}
|
||||
.nav-item { position: relative; }
|
||||
.nav-trigger {
|
||||
height: var(--header-h);
|
||||
padding: 0 16px;
|
||||
color: rgba(255,255,255,.85);
|
||||
font-size: 15px; font-weight: 500;
|
||||
transition: color var(--fast);
|
||||
display: flex; align-items: center;
|
||||
}
|
||||
.nav-trigger:hover,
|
||||
.nav-item.active .nav-trigger { color: #fff; }
|
||||
.nav-item.active .nav-trigger { border-bottom: 2px solid var(--accent); }
|
||||
|
||||
/* 드롭다운 */
|
||||
.dropdown {
|
||||
position: absolute; top: calc(var(--header-h) - 2px); left: 0;
|
||||
min-width: 180px;
|
||||
background: #fff;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
box-shadow: var(--shadow-lg);
|
||||
border-top: 3px solid var(--primary);
|
||||
padding: 8px 0;
|
||||
animation: fadeDown .18s ease;
|
||||
}
|
||||
@keyframes fadeDown {
|
||||
from { opacity:0; transform:translateY(-8px); }
|
||||
to { opacity:1; transform:translateY(0); }
|
||||
}
|
||||
.dropdown-item {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px; color: var(--gray-700);
|
||||
transition: all var(--fast);
|
||||
}
|
||||
.dropdown-item:hover, .dropdown-item.current {
|
||||
background: var(--primary-light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* CTA 버튼 */
|
||||
.header-cta { margin-left: auto; flex-shrink: 0; }
|
||||
|
||||
/* 햄버거 */
|
||||
.hamburger {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.hamburger span {
|
||||
display: block; width: 24px; height: 2px;
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
transition: all var(--mid);
|
||||
}
|
||||
|
||||
/* 모바일 메뉴 */
|
||||
.nav-mobile {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
background: var(--secondary);
|
||||
border-top: 1px solid rgba(255,255,255,.1);
|
||||
max-height: calc(100vh - var(--header-h));
|
||||
overflow-y: auto;
|
||||
}
|
||||
.mobile-group { border-bottom: 1px solid rgba(255,255,255,.08); }
|
||||
.mobile-group-header {
|
||||
display: flex; align-items: center;
|
||||
padding: 14px 24px;
|
||||
color: rgba(255,255,255,.85);
|
||||
font-size: 15px; font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
.mobile-children { background: rgba(0,0,0,.2); }
|
||||
.mobile-child {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 10px 36px;
|
||||
font-size: 14px; color: rgba(255,255,255,.7);
|
||||
}
|
||||
.mobile-child:hover { color: #fff; }
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 1024px) {
|
||||
.nav-desktop, .header-cta { display: none; }
|
||||
.hamburger { display: flex; }
|
||||
.header.mobile-open .nav-mobile { display: flex; }
|
||||
.header.mobile-open { height: auto; }
|
||||
}
|
||||
/* ─── Header ──────────────────────────────────────────────── */
|
||||
.skip-link {
|
||||
position: absolute; top: -60px; left: 0; z-index: 9999;
|
||||
background: var(--primary); color: #fff;
|
||||
padding: 10px 20px; border-radius: 0 0 8px 0;
|
||||
transition: top .2s;
|
||||
}
|
||||
.skip-link:focus { top: 0; }
|
||||
|
||||
.header {
|
||||
position: fixed; top: 0; left: 0; right: 0;
|
||||
z-index: 1000; height: var(--header-h);
|
||||
background: rgba(26, 26, 46, 0.96);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid rgba(255,255,255,.08);
|
||||
transition: all var(--mid) var(--ease);
|
||||
}
|
||||
.header.scrolled {
|
||||
background: rgba(26, 26, 46, 0.99);
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,.3);
|
||||
}
|
||||
.header-inner {
|
||||
display: flex; align-items: center; gap: 32px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 로고 */
|
||||
.logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0; cursor: pointer; text-decoration: none; }
|
||||
.logo img { height: 40px; width: auto; }
|
||||
.logo-text { color: #fff; font-size: 20px; font-weight: 700; }
|
||||
.logo-text strong { color: var(--accent); }
|
||||
|
||||
/* 데스크톱 메뉴 */
|
||||
.nav-desktop {
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
margin-left: 24px; flex: 1;
|
||||
}
|
||||
.nav-item { position: relative; }
|
||||
.nav-trigger {
|
||||
height: var(--header-h);
|
||||
padding: 0 16px;
|
||||
color: rgba(255,255,255,.85);
|
||||
font-size: 15px; font-weight: 500;
|
||||
transition: color var(--fast);
|
||||
display: flex; align-items: center;
|
||||
}
|
||||
.nav-trigger:hover,
|
||||
.nav-item.active .nav-trigger { color: #fff; }
|
||||
.nav-item.active .nav-trigger { border-bottom: 2px solid var(--accent); }
|
||||
|
||||
/* 드롭다운 */
|
||||
.dropdown {
|
||||
position: absolute; top: calc(var(--header-h) - 2px); left: 0;
|
||||
min-width: 180px;
|
||||
background: #fff;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
box-shadow: var(--shadow-lg);
|
||||
border-top: 3px solid var(--primary);
|
||||
padding: 8px 0;
|
||||
animation: fadeDown .18s ease;
|
||||
}
|
||||
@keyframes fadeDown {
|
||||
from { opacity:0; transform:translateY(-8px); }
|
||||
to { opacity:1; transform:translateY(0); }
|
||||
}
|
||||
.dropdown-item {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px; color: var(--gray-700);
|
||||
transition: all var(--fast);
|
||||
}
|
||||
.dropdown-item:hover, .dropdown-item.current {
|
||||
background: var(--primary-light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* CTA 버튼 */
|
||||
.header-cta { margin-left: auto; flex-shrink: 0; }
|
||||
|
||||
/* 햄버거 */
|
||||
.hamburger {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.hamburger span {
|
||||
display: block; width: 24px; height: 2px;
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
transition: all var(--mid);
|
||||
}
|
||||
|
||||
/* 모바일 메뉴 */
|
||||
.nav-mobile {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
background: var(--secondary);
|
||||
border-top: 1px solid rgba(255,255,255,.1);
|
||||
max-height: calc(100vh - var(--header-h));
|
||||
overflow-y: auto;
|
||||
}
|
||||
.mobile-group { border-bottom: 1px solid rgba(255,255,255,.08); }
|
||||
.mobile-group-header {
|
||||
display: flex; align-items: center;
|
||||
padding: 14px 24px;
|
||||
color: rgba(255,255,255,.85);
|
||||
font-size: 15px; font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
.mobile-children { background: rgba(0,0,0,.2); }
|
||||
.mobile-child {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 10px 36px;
|
||||
font-size: 14px; color: rgba(255,255,255,.7);
|
||||
}
|
||||
.mobile-child:hover { color: #fff; }
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 1024px) {
|
||||
.nav-desktop, .header-cta { display: none; }
|
||||
.hamburger { display: flex; }
|
||||
.header.mobile-open .nav-mobile { display: flex; }
|
||||
.header.mobile-open { height: auto; }
|
||||
}
|
||||
|
||||
@ -1,200 +1,200 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import './Header.css';
|
||||
|
||||
const MENU = [
|
||||
{
|
||||
id: 'company', label: '회사소개',
|
||||
children: [
|
||||
{ label: 'CEO 인사말', path: '/company/greeting' },
|
||||
{ label: '연혁', path: '/company/history' },
|
||||
{ label: '조직도', path: '/company/organization' },
|
||||
{ label: 'CI 소개', path: '/company/ci' },
|
||||
{ label: '오시는 길', path: '/company/location' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'solution', label: '솔루션',
|
||||
children: [
|
||||
{ label: 'GUARDiA ITSM', path: '/solution/guardia', badge: 'NEW' },
|
||||
{ label: 'ERP', path: '/solution/erp' },
|
||||
{ label: 'CRM', path: '/solution/crm' },
|
||||
{ label: 'BI', path: '/solution/bi' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'business', label: '사업실적',
|
||||
children: [
|
||||
{ label: '구축 레퍼런스', path: '/business/reference' },
|
||||
{ label: '파트너', path: '/business/partner' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'support', label: '고객지원',
|
||||
children: [
|
||||
{ label: '공지사항', path: '/support/notice' },
|
||||
{ label: 'FAQ', path: '/support/faq' },
|
||||
{ label: '카탈로그', path: '/support/catalog' },
|
||||
{ label: '문의하기', path: '/support/contact' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'recruit', label: '채용',
|
||||
children: [
|
||||
{ label: '채용공고', path: '/recruit/jobs' },
|
||||
{ label: '복리후생', path: '/recruit/welfare' },
|
||||
{ label: '지원하기', path: '/recruit/apply' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'news', label: '뉴스',
|
||||
children: [
|
||||
{ label: '뉴스룸', path: '/news/newsroom' },
|
||||
{ label: '기술 블로그', path: '/news/blog' },
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default function Header() {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [activeMenu, setActiveMenu] = useState(null);
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [member, setMember] = useState(null);
|
||||
const closeTimer = React.useRef(null);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 로그인 상태 동기화
|
||||
useEffect(() => {
|
||||
const sync = () => {
|
||||
const u = localStorage.getItem('member_user');
|
||||
setMember(u ? JSON.parse(u) : null);
|
||||
};
|
||||
sync();
|
||||
window.addEventListener('storage', sync);
|
||||
return () => window.removeEventListener('storage', sync);
|
||||
}, [location]);
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem('member_token');
|
||||
localStorage.removeItem('member_user');
|
||||
setMember(null);
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 60);
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setMobileOpen(false);
|
||||
setActiveMenu(null);
|
||||
}, [location]);
|
||||
|
||||
const isActive = (menu) =>
|
||||
menu.children?.some(c => location.pathname.startsWith(c.path));
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 접근성 스킵 링크 */}
|
||||
<a href="#main-content" className="skip-link">본문 바로가기</a>
|
||||
|
||||
<header className={`header ${scrolled ? 'scrolled' : ''} ${mobileOpen ? 'mobile-open' : ''}`}
|
||||
role="banner">
|
||||
<div className="header-inner container">
|
||||
{/* 로고 */}
|
||||
<Link to="/" className="logo" aria-label="(주)지오정보기술 홈으로">
|
||||
<img src="/zioinfo-logo-dark.png" alt="(주)지오정보기술 로고" height="40"
|
||||
onError={e => { e.target.src='/zioinfo-logo.png'; e.target.onerror = () => { e.target.style.display='none'; e.target.nextSibling.style.display='flex'; }; }} />
|
||||
<span className="logo-text" style={{display:'none'}}>
|
||||
<strong>Zio</strong>Info
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* 데스크톱 메뉴 */}
|
||||
<nav className="nav-desktop" role="navigation" aria-label="주요 메뉴">
|
||||
{MENU.map(menu => (
|
||||
<div key={menu.id}
|
||||
className={`nav-item ${isActive(menu) ? 'active' : ''}`}
|
||||
onMouseEnter={() => { clearTimeout(closeTimer.current); setActiveMenu(menu.id); }}
|
||||
onMouseLeave={() => { closeTimer.current = setTimeout(() => setActiveMenu(null), 200); }}>
|
||||
<button className="nav-trigger" aria-haspopup="true"
|
||||
aria-expanded={activeMenu === menu.id}
|
||||
onClick={() => navigate(menu.children[0].path)}>
|
||||
{menu.label}
|
||||
</button>
|
||||
{activeMenu === menu.id && (
|
||||
<div className="dropdown" role="menu">
|
||||
{menu.children.map(child => (
|
||||
<Link key={child.path} to={child.path}
|
||||
className={`dropdown-item ${location.pathname === child.path ? 'current' : ''}`}
|
||||
role="menuitem">
|
||||
{child.label}
|
||||
{child.badge && <span className="badge badge-new">{child.badge}</span>}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* 우측 버튼 영역 */}
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||||
<Link to="/support/contact" className="btn btn-outline btn-sm">문의하기</Link>
|
||||
{member ? (
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||||
<span style={{ fontSize:13, color:'var(--gray-600)', maxWidth:100,
|
||||
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
|
||||
{member.name}님
|
||||
</span>
|
||||
<button onClick={logout}
|
||||
style={{ padding:'6px 14px', background:'none', border:'1px solid #e2e8f0',
|
||||
borderRadius:8, fontSize:12, color:'#64748b', cursor:'pointer' }}>
|
||||
로그아웃
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<Link to="/login" className="btn btn-primary btn-sm">로그인</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 햄버거 (모바일) */}
|
||||
<button className="hamburger" aria-label="모바일 메뉴"
|
||||
aria-expanded={mobileOpen}
|
||||
onClick={() => setMobileOpen(v => !v)}>
|
||||
<span/><span/><span/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 모바일 메뉴 */}
|
||||
{mobileOpen && (
|
||||
<nav className="nav-mobile" role="navigation" aria-label="모바일 메뉴">
|
||||
{MENU.map(menu => (
|
||||
<details key={menu.id} className="mobile-group">
|
||||
<summary className="mobile-group-header">{menu.label}</summary>
|
||||
<div className="mobile-children">
|
||||
{menu.children.map(child => (
|
||||
<Link key={child.path} to={child.path} className="mobile-child">
|
||||
{child.label}
|
||||
{child.badge && <span className="badge badge-new">{child.badge}</span>}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
))}
|
||||
<div style={{ display:'flex', gap:8, margin:'16px' }}>
|
||||
<Link to="/support/contact" className="btn btn-outline" style={{ flex:1 }}>문의하기</Link>
|
||||
{member
|
||||
? <button onClick={logout} className="btn btn-primary" style={{ flex:1 }}>로그아웃</button>
|
||||
: <Link to="/login" className="btn btn-primary" style={{ flex:1 }}>로그인 / 가입</Link>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
}
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import './Header.css';
|
||||
|
||||
const MENU = [
|
||||
{
|
||||
id: 'company', label: '회사소개',
|
||||
children: [
|
||||
{ label: 'CEO 인사말', path: '/company/greeting' },
|
||||
{ label: '연혁', path: '/company/history' },
|
||||
{ label: '조직도', path: '/company/organization' },
|
||||
{ label: 'CI 소개', path: '/company/ci' },
|
||||
{ label: '오시는 길', path: '/company/location' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'solution', label: '솔루션',
|
||||
children: [
|
||||
{ label: 'GUARDiA ITSM', path: '/solution/guardia', badge: 'NEW' },
|
||||
{ label: 'ERP', path: '/solution/erp' },
|
||||
{ label: 'CRM', path: '/solution/crm' },
|
||||
{ label: 'BI', path: '/solution/bi' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'business', label: '사업실적',
|
||||
children: [
|
||||
{ label: '구축 레퍼런스', path: '/business/reference' },
|
||||
{ label: '파트너', path: '/business/partner' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'support', label: '고객지원',
|
||||
children: [
|
||||
{ label: '공지사항', path: '/support/notice' },
|
||||
{ label: 'FAQ', path: '/support/faq' },
|
||||
{ label: '카탈로그', path: '/support/catalog' },
|
||||
{ label: '문의하기', path: '/support/contact' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'recruit', label: '채용',
|
||||
children: [
|
||||
{ label: '채용공고', path: '/recruit/jobs' },
|
||||
{ label: '복리후생', path: '/recruit/welfare' },
|
||||
{ label: '지원하기', path: '/recruit/apply' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'news', label: '뉴스',
|
||||
children: [
|
||||
{ label: '뉴스룸', path: '/news/newsroom' },
|
||||
{ label: '기술 블로그', path: '/news/blog' },
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default function Header() {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [activeMenu, setActiveMenu] = useState(null);
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [member, setMember] = useState(null);
|
||||
const closeTimer = React.useRef(null);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 로그인 상태 동기화
|
||||
useEffect(() => {
|
||||
const sync = () => {
|
||||
const u = localStorage.getItem('member_user');
|
||||
setMember(u ? JSON.parse(u) : null);
|
||||
};
|
||||
sync();
|
||||
window.addEventListener('storage', sync);
|
||||
return () => window.removeEventListener('storage', sync);
|
||||
}, [location]);
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem('member_token');
|
||||
localStorage.removeItem('member_user');
|
||||
setMember(null);
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 60);
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setMobileOpen(false);
|
||||
setActiveMenu(null);
|
||||
}, [location]);
|
||||
|
||||
const isActive = (menu) =>
|
||||
menu.children?.some(c => location.pathname.startsWith(c.path));
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 접근성 스킵 링크 */}
|
||||
<a href="#main-content" className="skip-link">본문 바로가기</a>
|
||||
|
||||
<header className={`header ${scrolled ? 'scrolled' : ''} ${mobileOpen ? 'mobile-open' : ''}`}
|
||||
role="banner">
|
||||
<div className="header-inner container">
|
||||
{/* 로고 */}
|
||||
<Link to="/" className="logo" aria-label="(주)지오정보기술 홈으로">
|
||||
<img src="/zioinfo-logo-dark.png" alt="(주)지오정보기술 로고" height="40"
|
||||
onError={e => { e.target.src='/zioinfo-logo.png'; e.target.onerror = () => { e.target.style.display='none'; e.target.nextSibling.style.display='flex'; }; }} />
|
||||
<span className="logo-text" style={{display:'none'}}>
|
||||
<strong>Zio</strong>Info
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* 데스크톱 메뉴 */}
|
||||
<nav className="nav-desktop" role="navigation" aria-label="주요 메뉴">
|
||||
{MENU.map(menu => (
|
||||
<div key={menu.id}
|
||||
className={`nav-item ${isActive(menu) ? 'active' : ''}`}
|
||||
onMouseEnter={() => { clearTimeout(closeTimer.current); setActiveMenu(menu.id); }}
|
||||
onMouseLeave={() => { closeTimer.current = setTimeout(() => setActiveMenu(null), 200); }}>
|
||||
<button className="nav-trigger" aria-haspopup="true"
|
||||
aria-expanded={activeMenu === menu.id}
|
||||
onClick={() => navigate(menu.children[0].path)}>
|
||||
{menu.label}
|
||||
</button>
|
||||
{activeMenu === menu.id && (
|
||||
<div className="dropdown" role="menu">
|
||||
{menu.children.map(child => (
|
||||
<Link key={child.path} to={child.path}
|
||||
className={`dropdown-item ${location.pathname === child.path ? 'current' : ''}`}
|
||||
role="menuitem">
|
||||
{child.label}
|
||||
{child.badge && <span className="badge badge-new">{child.badge}</span>}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* 우측 버튼 영역 */}
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||||
<Link to="/support/contact" className="btn btn-outline btn-sm">문의하기</Link>
|
||||
{member ? (
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||||
<span style={{ fontSize:13, color:'var(--gray-600)', maxWidth:100,
|
||||
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
|
||||
{member.name}님
|
||||
</span>
|
||||
<button onClick={logout}
|
||||
style={{ padding:'6px 14px', background:'none', border:'1px solid #e2e8f0',
|
||||
borderRadius:8, fontSize:12, color:'#64748b', cursor:'pointer' }}>
|
||||
로그아웃
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<Link to="/login" className="btn btn-primary btn-sm">로그인</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 햄버거 (모바일) */}
|
||||
<button className="hamburger" aria-label="모바일 메뉴"
|
||||
aria-expanded={mobileOpen}
|
||||
onClick={() => setMobileOpen(v => !v)}>
|
||||
<span/><span/><span/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 모바일 메뉴 */}
|
||||
{mobileOpen && (
|
||||
<nav className="nav-mobile" role="navigation" aria-label="모바일 메뉴">
|
||||
{MENU.map(menu => (
|
||||
<details key={menu.id} className="mobile-group">
|
||||
<summary className="mobile-group-header">{menu.label}</summary>
|
||||
<div className="mobile-children">
|
||||
{menu.children.map(child => (
|
||||
<Link key={child.path} to={child.path} className="mobile-child">
|
||||
{child.label}
|
||||
{child.badge && <span className="badge badge-new">{child.badge}</span>}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
))}
|
||||
<div style={{ display:'flex', gap:8, margin:'16px' }}>
|
||||
<Link to="/support/contact" className="btn btn-outline" style={{ flex:1 }}>문의하기</Link>
|
||||
{member
|
||||
? <button onClick={logout} className="btn btn-primary" style={{ flex:1 }}>로그아웃</button>
|
||||
: <Link to="/login" className="btn btn-primary" style={{ flex:1 }}>로그인 / 가입</Link>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,293 +1,293 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route, NavLink } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import './Common.css';
|
||||
import './SolutionPage.css';
|
||||
|
||||
const SUB_NAV = [
|
||||
{ path: '/solution/guardia', label: 'GUARDiA ITSM', badge: 'NEW' },
|
||||
{ path: '/solution/erp', label: 'ERP' },
|
||||
{ path: '/solution/crm', label: 'CRM' },
|
||||
{ path: '/solution/bi', label: 'BI' },
|
||||
];
|
||||
|
||||
function SubNav({ title }) {
|
||||
return (
|
||||
<>
|
||||
<div className="page-hero">
|
||||
<div className="container">
|
||||
<span className="section-label">Solution</span>
|
||||
<h1 className="page-hero-title">{title}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="sub-nav">
|
||||
<div className="container">
|
||||
{SUB_NAV.map(n => (
|
||||
<NavLink key={n.path} to={n.path}
|
||||
className={({ isActive }) => 'sub-nav-item' + (isActive ? ' active' : '')}>
|
||||
{n.label}
|
||||
{n.badge && <span className="badge badge-new" style={{ marginLeft: '6px', fontSize: '10px' }}>{n.badge}</span>}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── ERP ── */
|
||||
function ERP() {
|
||||
const modules = [
|
||||
{ icon: '💰', name: '재무·회계', desc: '전표처리, 결산, 세무신고, 원가계산 자동화' },
|
||||
{ icon: '🏭', name: '생산관리', desc: 'BOM 관리, 생산계획, 공정관리, 품질관리' },
|
||||
{ icon: '📦', name: '구매·재고', desc: '발주, 입출고, 재고 현황, 협력사 포털' },
|
||||
{ icon: '👥', name: '인사·급여', desc: '근태관리, 급여계산, 조직도, 인사평가' },
|
||||
{ icon: '🛒', name: '영업·물류', desc: '수주관리, 배송, 매출 분석, 고객 관리' },
|
||||
{ icon: '📊', name: '경영 분석', desc: 'KPI 대시보드, 예산 vs 실적, 경영 보고서' },
|
||||
];
|
||||
return (
|
||||
<main id="main-content" className="inner-page">
|
||||
<SubNav title="ERP 솔루션" />
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="sol-hero-grid">
|
||||
<div>
|
||||
<span className="section-label">Enterprise Resource Planning</span>
|
||||
<h2 className="sol-title">공공·중견기업 맞춤형<br /><em>통합 ERP 솔루션</em></h2>
|
||||
<p className="sol-desc">
|
||||
20년 이상 현대모비스, 한화그룹, 이마트 등 국내 주요 기업의 핵심 업무 시스템을 구축한 경험을 바탕으로,
|
||||
고객사의 업무 프로세스에 최적화된 맞춤형 ERP를 제공합니다.
|
||||
</p>
|
||||
<div className="sol-features">
|
||||
{['공공기관 표준 회계 기준 적용', 'Oracle / Tibero DB 지원', '모바일 결재·보고 지원', '기존 레거시 시스템 연계'].map((f, i) => (
|
||||
<div key={i} className="sol-feature-item">
|
||||
<span className="sol-check">✓</span> {f}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}>
|
||||
<Link to="/support/contact?type=데모 신청" className="btn btn-primary btn-lg">무료 데모 신청</Link>
|
||||
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그 다운로드</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sol-visual erp-visual">
|
||||
<div className="sol-screen">
|
||||
<div className="sol-screen-header"><span />재무 대시보드</div>
|
||||
<div className="sol-chart-bar-wrap">
|
||||
{[80, 65, 90, 72, 88, 55, 95].map((h, i) => (
|
||||
<div key={i} className="sol-chart-bar" style={{ height: h + '%' }} />
|
||||
))}
|
||||
</div>
|
||||
<div className="sol-stat-row">
|
||||
<div className="sol-stat"><strong>₩12.4억</strong><span>이번달 매출</span></div>
|
||||
<div className="sol-stat"><strong>98.2%</strong><span>예산 집행률</span></div>
|
||||
<div className="sol-stat"><strong>+18%</strong><span>전월 대비</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 모듈 */}
|
||||
<div style={{ marginTop: '80px' }}>
|
||||
<div className="section-header">
|
||||
<span className="section-label">Modules</span>
|
||||
<h2 className="section-title">6대 핵심 모듈</h2>
|
||||
</div>
|
||||
<div className="grid-3">
|
||||
{modules.map((m, i) => (
|
||||
<div key={i} className="card sol-module-card">
|
||||
<div className="sol-module-icon">{m.icon}</div>
|
||||
<h3>{m.name}</h3>
|
||||
<p>{m.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── CRM ── */
|
||||
function CRM() {
|
||||
const features = [
|
||||
{ icon: '📇', name: '고객 360˚', desc: '고객 정보, 구매이력, 상담이력, 선호도를 단일 뷰로 통합' },
|
||||
{ icon: '📞', name: '멀티채널 상담', desc: '전화·이메일·채팅·SNS 통합 인입, 상담 이력 자동 기록' },
|
||||
{ icon: '🎯', name: '영업 파이프라인', desc: '리드 발굴부터 계약까지 전 단계 시각화 관리' },
|
||||
{ icon: '📨', name: '마케팅 자동화', desc: '고객 세그먼트별 자동 캠페인, 이메일·SMS 발송' },
|
||||
{ icon: '🤖', name: 'AI 상담 추천', desc: 'Ollama LLM 기반 최적 답변 자동 추천 및 요약' },
|
||||
{ icon: '📈', name: '성과 분석', desc: '상담사별·채널별 KPI, 고객 만족도, 전환율 리포트' },
|
||||
];
|
||||
return (
|
||||
<main id="main-content" className="inner-page">
|
||||
<SubNav title="CRM 솔루션" />
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="sol-hero-grid">
|
||||
<div>
|
||||
<span className="section-label">Customer Relationship Management</span>
|
||||
<h2 className="sol-title">AI 기반<br /><em>고객 관계 관리 플랫폼</em></h2>
|
||||
<p className="sol-desc">
|
||||
삼성전자 차세대 CRM, LG U+ VAN 고도화, 현대캐피탈 차세대 시스템 등
|
||||
국내 최대 규모 CRM 프로젝트를 성공적으로 수행한 전문 역량으로 구축합니다.
|
||||
온프레미스 AI(Ollama) 연동으로 데이터 외부 유출 없이 지능형 상담을 실현합니다.
|
||||
</p>
|
||||
<div className="sol-features">
|
||||
{['삼성전자·LG·현대 구축 레퍼런스', '온프레미스 AI 상담 추천', 'CTI 연동 (콜센터 솔루션)', '공공기관 개인정보보호법 준수'].map((f, i) => (
|
||||
<div key={i} className="sol-feature-item">
|
||||
<span className="sol-check">✓</span> {f}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}>
|
||||
<Link to="/support/contact?type=데모 신청" className="btn btn-primary btn-lg">데모 신청</Link>
|
||||
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sol-visual crm-visual">
|
||||
<div className="sol-screen">
|
||||
<div className="sol-screen-header"><span />고객 상담 현황</div>
|
||||
<div className="crm-items">
|
||||
{[
|
||||
{ name: '김민준', type: '제품문의', status: '처리중', color: '#f59e0b' },
|
||||
{ name: '이서연', type: '기술지원', status: '완료', color: '#10b981' },
|
||||
{ name: '박지후', type: '불만접수', status: '대기', color: '#ef4444' },
|
||||
{ name: '최수아', type: '데모신청', status: '완료', color: '#10b981' },
|
||||
].map((c, i) => (
|
||||
<div key={i} className="crm-item">
|
||||
<div className="crm-avatar">{c.name[0]}</div>
|
||||
<div className="crm-info">
|
||||
<strong>{c.name}</strong>
|
||||
<span>{c.type}</span>
|
||||
</div>
|
||||
<span className="crm-status" style={{ color: c.color }}>{c.status}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '80px' }}>
|
||||
<div className="section-header">
|
||||
<span className="section-label">Features</span>
|
||||
<h2 className="section-title">주요 기능</h2>
|
||||
</div>
|
||||
<div className="grid-3">
|
||||
{features.map((f, i) => (
|
||||
<div key={i} className="card sol-module-card">
|
||||
<div className="sol-module-icon">{f.icon}</div>
|
||||
<h3>{f.name}</h3>
|
||||
<p>{f.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── BI ── */
|
||||
function BI() {
|
||||
const charts = [
|
||||
{ icon: '📊', name: '경영 대시보드', desc: '실시간 KPI 모니터링, 부서별 성과 지표, 경영진 요약 보고' },
|
||||
{ icon: '📉', name: '매출·비용 분석', desc: '기간별·제품별·채널별 매출 트렌드, 비용 구조 분석' },
|
||||
{ icon: '🗺️', name: '지역별 분석', desc: '지도 기반 시각화, 공공기관 지역별 서비스 현황' },
|
||||
{ icon: '🔮', name: 'AI 예측 분석', desc: '머신러닝 기반 수요 예측, 이상 패턴 자동 탐지' },
|
||||
{ icon: '📋', name: '자동 보고서', desc: '일·주·월 보고서 자동 생성, 이메일·메신저 배포' },
|
||||
{ icon: '🔗', name: 'ETL 파이프라인', desc: 'Oracle, SAP, 공공DB 등 다양한 소스 데이터 연계' },
|
||||
];
|
||||
return (
|
||||
<main id="main-content" className="inner-page">
|
||||
<SubNav title="BI 솔루션" />
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="sol-hero-grid">
|
||||
<div>
|
||||
<span className="section-label">Business Intelligence</span>
|
||||
<h2 className="sol-title">데이터 기반<br /><em>의사결정 플랫폼</em></h2>
|
||||
<p className="sol-desc">
|
||||
OZ Report, MiPlatform, JasperReports 등 다양한 보고 도구와의 연동 경험을 바탕으로,
|
||||
공공기관·중견기업 맞춤형 BI 플랫폼을 구축합니다.
|
||||
기존 레거시 DB에서 실시간 데이터를 수집해 경영 인사이트를 제공합니다.
|
||||
</p>
|
||||
<div className="sol-features">
|
||||
{['OZ·MiPlatform·JasperReport 연동', '실시간 대시보드 (WebSocket)', 'Oracle·Tibero·PostgreSQL 지원', '공공기관 표준 보고서 양식'].map((f, i) => (
|
||||
<div key={i} className="sol-feature-item">
|
||||
<span className="sol-check">✓</span> {f}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}>
|
||||
<Link to="/support/contact?type=데모 신청" className="btn btn-primary btn-lg">데모 신청</Link>
|
||||
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sol-visual bi-visual">
|
||||
<div className="sol-screen">
|
||||
<div className="sol-screen-header"><span />경영 대시보드</div>
|
||||
<div className="bi-kpis">
|
||||
{[
|
||||
{ label:'매출', val:'₩48.2억', up:true, delta:'+12%' },
|
||||
{ label:'비용', val:'₩31.7억', up:false, delta:'-3%' },
|
||||
{ label:'이익', val:'₩16.5억', up:true, delta:'+28%' },
|
||||
{ label:'고객', val:'1,240명', up:true, delta:'+8%' },
|
||||
].map((k, i) => (
|
||||
<div key={i} className="bi-kpi">
|
||||
<span className="bi-kpi-label">{k.label}</span>
|
||||
<strong className="bi-kpi-val">{k.val}</strong>
|
||||
<span className="bi-kpi-delta" style={{ color: k.up ? '#10b981' : '#ef4444' }}>
|
||||
{k.delta}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="bi-bar-chart">
|
||||
{['1Q','2Q','3Q','4Q'].map((q, i) => (
|
||||
<div key={i} className="bi-bar-group">
|
||||
<div className="bi-bar-pair">
|
||||
<div className="bi-bar revenue" style={{ height: [60, 75, 85, 100][i] + '%' }} />
|
||||
<div className="bi-bar cost" style={{ height: [70, 65, 60, 55][i] + '%' }} />
|
||||
</div>
|
||||
<span>{q}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '80px' }}>
|
||||
<div className="section-header">
|
||||
<span className="section-label">Features</span>
|
||||
<h2 className="section-title">주요 기능</h2>
|
||||
</div>
|
||||
<div className="grid-3">
|
||||
{charts.map((c, i) => (
|
||||
<div key={i} className="card sol-module-card">
|
||||
<div className="sol-module-icon">{c.icon}</div>
|
||||
<h3>{c.name}</h3>
|
||||
<p>{c.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SolutionPage() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="erp" element={<ERP />} />
|
||||
<Route path="crm" element={<CRM />} />
|
||||
<Route path="bi" element={<BI />} />
|
||||
<Route path="*" element={<ERP />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
import React from 'react';
|
||||
import { Routes, Route, NavLink } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import './Common.css';
|
||||
import './SolutionPage.css';
|
||||
|
||||
const SUB_NAV = [
|
||||
{ path: '/solution/guardia', label: 'GUARDiA ITSM', badge: 'NEW' },
|
||||
{ path: '/solution/erp', label: 'ERP' },
|
||||
{ path: '/solution/crm', label: 'CRM' },
|
||||
{ path: '/solution/bi', label: 'BI' },
|
||||
];
|
||||
|
||||
function SubNav({ title }) {
|
||||
return (
|
||||
<>
|
||||
<div className="page-hero">
|
||||
<div className="container">
|
||||
<span className="section-label">Solution</span>
|
||||
<h1 className="page-hero-title">{title}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="sub-nav">
|
||||
<div className="container">
|
||||
{SUB_NAV.map(n => (
|
||||
<NavLink key={n.path} to={n.path}
|
||||
className={({ isActive }) => 'sub-nav-item' + (isActive ? ' active' : '')}>
|
||||
{n.label}
|
||||
{n.badge && <span className="badge badge-new" style={{ marginLeft: '6px', fontSize: '10px' }}>{n.badge}</span>}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── ERP ── */
|
||||
function ERP() {
|
||||
const modules = [
|
||||
{ icon: '💰', name: '재무·회계', desc: '전표처리, 결산, 세무신고, 원가계산 자동화' },
|
||||
{ icon: '🏭', name: '생산관리', desc: 'BOM 관리, 생산계획, 공정관리, 품질관리' },
|
||||
{ icon: '📦', name: '구매·재고', desc: '발주, 입출고, 재고 현황, 협력사 포털' },
|
||||
{ icon: '👥', name: '인사·급여', desc: '근태관리, 급여계산, 조직도, 인사평가' },
|
||||
{ icon: '🛒', name: '영업·물류', desc: '수주관리, 배송, 매출 분석, 고객 관리' },
|
||||
{ icon: '📊', name: '경영 분석', desc: 'KPI 대시보드, 예산 vs 실적, 경영 보고서' },
|
||||
];
|
||||
return (
|
||||
<main id="main-content" className="inner-page">
|
||||
<SubNav title="ERP 솔루션" />
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="sol-hero-grid">
|
||||
<div>
|
||||
<span className="section-label">Enterprise Resource Planning</span>
|
||||
<h2 className="sol-title">공공·중견기업 맞춤형<br /><em>통합 ERP 솔루션</em></h2>
|
||||
<p className="sol-desc">
|
||||
20년 이상 현대모비스, 한화그룹, 이마트 등 국내 주요 기업의 핵심 업무 시스템을 구축한 경험을 바탕으로,
|
||||
고객사의 업무 프로세스에 최적화된 맞춤형 ERP를 제공합니다.
|
||||
</p>
|
||||
<div className="sol-features">
|
||||
{['공공기관 표준 회계 기준 적용', 'Oracle / Tibero DB 지원', '모바일 결재·보고 지원', '기존 레거시 시스템 연계'].map((f, i) => (
|
||||
<div key={i} className="sol-feature-item">
|
||||
<span className="sol-check">✓</span> {f}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}>
|
||||
<Link to="/support/contact?type=데모 신청" className="btn btn-primary btn-lg">무료 데모 신청</Link>
|
||||
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그 다운로드</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sol-visual erp-visual">
|
||||
<div className="sol-screen">
|
||||
<div className="sol-screen-header"><span />재무 대시보드</div>
|
||||
<div className="sol-chart-bar-wrap">
|
||||
{[80, 65, 90, 72, 88, 55, 95].map((h, i) => (
|
||||
<div key={i} className="sol-chart-bar" style={{ height: h + '%' }} />
|
||||
))}
|
||||
</div>
|
||||
<div className="sol-stat-row">
|
||||
<div className="sol-stat"><strong>₩12.4억</strong><span>이번달 매출</span></div>
|
||||
<div className="sol-stat"><strong>98.2%</strong><span>예산 집행률</span></div>
|
||||
<div className="sol-stat"><strong>+18%</strong><span>전월 대비</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 모듈 */}
|
||||
<div style={{ marginTop: '80px' }}>
|
||||
<div className="section-header">
|
||||
<span className="section-label">Modules</span>
|
||||
<h2 className="section-title">6대 핵심 모듈</h2>
|
||||
</div>
|
||||
<div className="grid-3">
|
||||
{modules.map((m, i) => (
|
||||
<div key={i} className="card sol-module-card">
|
||||
<div className="sol-module-icon">{m.icon}</div>
|
||||
<h3>{m.name}</h3>
|
||||
<p>{m.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── CRM ── */
|
||||
function CRM() {
|
||||
const features = [
|
||||
{ icon: '📇', name: '고객 360˚', desc: '고객 정보, 구매이력, 상담이력, 선호도를 단일 뷰로 통합' },
|
||||
{ icon: '📞', name: '멀티채널 상담', desc: '전화·이메일·채팅·SNS 통합 인입, 상담 이력 자동 기록' },
|
||||
{ icon: '🎯', name: '영업 파이프라인', desc: '리드 발굴부터 계약까지 전 단계 시각화 관리' },
|
||||
{ icon: '📨', name: '마케팅 자동화', desc: '고객 세그먼트별 자동 캠페인, 이메일·SMS 발송' },
|
||||
{ icon: '🤖', name: 'AI 상담 추천', desc: 'Ollama LLM 기반 최적 답변 자동 추천 및 요약' },
|
||||
{ icon: '📈', name: '성과 분석', desc: '상담사별·채널별 KPI, 고객 만족도, 전환율 리포트' },
|
||||
];
|
||||
return (
|
||||
<main id="main-content" className="inner-page">
|
||||
<SubNav title="CRM 솔루션" />
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="sol-hero-grid">
|
||||
<div>
|
||||
<span className="section-label">Customer Relationship Management</span>
|
||||
<h2 className="sol-title">AI 기반<br /><em>고객 관계 관리 플랫폼</em></h2>
|
||||
<p className="sol-desc">
|
||||
삼성전자 차세대 CRM, LG U+ VAN 고도화, 현대캐피탈 차세대 시스템 등
|
||||
국내 최대 규모 CRM 프로젝트를 성공적으로 수행한 전문 역량으로 구축합니다.
|
||||
온프레미스 AI(Ollama) 연동으로 데이터 외부 유출 없이 지능형 상담을 실현합니다.
|
||||
</p>
|
||||
<div className="sol-features">
|
||||
{['삼성전자·LG·현대 구축 레퍼런스', '온프레미스 AI 상담 추천', 'CTI 연동 (콜센터 솔루션)', '공공기관 개인정보보호법 준수'].map((f, i) => (
|
||||
<div key={i} className="sol-feature-item">
|
||||
<span className="sol-check">✓</span> {f}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}>
|
||||
<Link to="/support/contact?type=데모 신청" className="btn btn-primary btn-lg">데모 신청</Link>
|
||||
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sol-visual crm-visual">
|
||||
<div className="sol-screen">
|
||||
<div className="sol-screen-header"><span />고객 상담 현황</div>
|
||||
<div className="crm-items">
|
||||
{[
|
||||
{ name: '김민준', type: '제품문의', status: '처리중', color: '#f59e0b' },
|
||||
{ name: '이서연', type: '기술지원', status: '완료', color: '#10b981' },
|
||||
{ name: '박지후', type: '불만접수', status: '대기', color: '#ef4444' },
|
||||
{ name: '최수아', type: '데모신청', status: '완료', color: '#10b981' },
|
||||
].map((c, i) => (
|
||||
<div key={i} className="crm-item">
|
||||
<div className="crm-avatar">{c.name[0]}</div>
|
||||
<div className="crm-info">
|
||||
<strong>{c.name}</strong>
|
||||
<span>{c.type}</span>
|
||||
</div>
|
||||
<span className="crm-status" style={{ color: c.color }}>{c.status}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '80px' }}>
|
||||
<div className="section-header">
|
||||
<span className="section-label">Features</span>
|
||||
<h2 className="section-title">주요 기능</h2>
|
||||
</div>
|
||||
<div className="grid-3">
|
||||
{features.map((f, i) => (
|
||||
<div key={i} className="card sol-module-card">
|
||||
<div className="sol-module-icon">{f.icon}</div>
|
||||
<h3>{f.name}</h3>
|
||||
<p>{f.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── BI ── */
|
||||
function BI() {
|
||||
const charts = [
|
||||
{ icon: '📊', name: '경영 대시보드', desc: '실시간 KPI 모니터링, 부서별 성과 지표, 경영진 요약 보고' },
|
||||
{ icon: '📉', name: '매출·비용 분석', desc: '기간별·제품별·채널별 매출 트렌드, 비용 구조 분석' },
|
||||
{ icon: '🗺️', name: '지역별 분석', desc: '지도 기반 시각화, 공공기관 지역별 서비스 현황' },
|
||||
{ icon: '🔮', name: 'AI 예측 분석', desc: '머신러닝 기반 수요 예측, 이상 패턴 자동 탐지' },
|
||||
{ icon: '📋', name: '자동 보고서', desc: '일·주·월 보고서 자동 생성, 이메일·메신저 배포' },
|
||||
{ icon: '🔗', name: 'ETL 파이프라인', desc: 'Oracle, SAP, 공공DB 등 다양한 소스 데이터 연계' },
|
||||
];
|
||||
return (
|
||||
<main id="main-content" className="inner-page">
|
||||
<SubNav title="BI 솔루션" />
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="sol-hero-grid">
|
||||
<div>
|
||||
<span className="section-label">Business Intelligence</span>
|
||||
<h2 className="sol-title">데이터 기반<br /><em>의사결정 플랫폼</em></h2>
|
||||
<p className="sol-desc">
|
||||
OZ Report, MiPlatform, JasperReports 등 다양한 보고 도구와의 연동 경험을 바탕으로,
|
||||
공공기관·중견기업 맞춤형 BI 플랫폼을 구축합니다.
|
||||
기존 레거시 DB에서 실시간 데이터를 수집해 경영 인사이트를 제공합니다.
|
||||
</p>
|
||||
<div className="sol-features">
|
||||
{['OZ·MiPlatform·JasperReport 연동', '실시간 대시보드 (WebSocket)', 'Oracle·Tibero·PostgreSQL 지원', '공공기관 표준 보고서 양식'].map((f, i) => (
|
||||
<div key={i} className="sol-feature-item">
|
||||
<span className="sol-check">✓</span> {f}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}>
|
||||
<Link to="/support/contact?type=데모 신청" className="btn btn-primary btn-lg">데모 신청</Link>
|
||||
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sol-visual bi-visual">
|
||||
<div className="sol-screen">
|
||||
<div className="sol-screen-header"><span />경영 대시보드</div>
|
||||
<div className="bi-kpis">
|
||||
{[
|
||||
{ label:'매출', val:'₩48.2억', up:true, delta:'+12%' },
|
||||
{ label:'비용', val:'₩31.7억', up:false, delta:'-3%' },
|
||||
{ label:'이익', val:'₩16.5억', up:true, delta:'+28%' },
|
||||
{ label:'고객', val:'1,240명', up:true, delta:'+8%' },
|
||||
].map((k, i) => (
|
||||
<div key={i} className="bi-kpi">
|
||||
<span className="bi-kpi-label">{k.label}</span>
|
||||
<strong className="bi-kpi-val">{k.val}</strong>
|
||||
<span className="bi-kpi-delta" style={{ color: k.up ? '#10b981' : '#ef4444' }}>
|
||||
{k.delta}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="bi-bar-chart">
|
||||
{['1Q','2Q','3Q','4Q'].map((q, i) => (
|
||||
<div key={i} className="bi-bar-group">
|
||||
<div className="bi-bar-pair">
|
||||
<div className="bi-bar revenue" style={{ height: [60, 75, 85, 100][i] + '%' }} />
|
||||
<div className="bi-bar cost" style={{ height: [70, 65, 60, 55][i] + '%' }} />
|
||||
</div>
|
||||
<span>{q}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '80px' }}>
|
||||
<div className="section-header">
|
||||
<span className="section-label">Features</span>
|
||||
<h2 className="section-title">주요 기능</h2>
|
||||
</div>
|
||||
<div className="grid-3">
|
||||
{charts.map((c, i) => (
|
||||
<div key={i} className="card sol-module-card">
|
||||
<div className="sol-module-icon">{c.icon}</div>
|
||||
<h3>{c.name}</h3>
|
||||
<p>{c.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SolutionPage() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="erp" element={<ERP />} />
|
||||
<Route path="crm" element={<CRM />} />
|
||||
<Route path="bi" element={<BI />} />
|
||||
<Route path="*" element={<ERP />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||