Compare commits

..

4 Commits

17 changed files with 619 additions and 619 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

View File

@ -1,126 +1,126 @@
/* ─── Header ──────────────────────────────────────────────── */ /* ─── Header ──────────────────────────────────────────────── */
.skip-link { .skip-link {
position: absolute; top: -60px; left: 0; z-index: 9999; position: absolute; top: -60px; left: 0; z-index: 9999;
background: var(--primary); color: #fff; background: var(--primary); color: #fff;
padding: 10px 20px; border-radius: 0 0 8px 0; padding: 10px 20px; border-radius: 0 0 8px 0;
transition: top .2s; transition: top .2s;
} }
.skip-link:focus { top: 0; } .skip-link:focus { top: 0; }
.header { .header {
position: fixed; top: 0; left: 0; right: 0; position: fixed; top: 0; left: 0; right: 0;
z-index: 1000; height: var(--header-h); z-index: 1000; height: var(--header-h);
background: rgba(26, 26, 46, 0.96); background: rgba(26, 26, 46, 0.96);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(255,255,255,.08); border-bottom: 1px solid rgba(255,255,255,.08);
transition: all var(--mid) var(--ease); transition: all var(--mid) var(--ease);
} }
.header.scrolled { .header.scrolled {
background: rgba(26, 26, 46, 0.99); background: rgba(26, 26, 46, 0.99);
box-shadow: 0 4px 24px rgba(0,0,0,.3); box-shadow: 0 4px 24px rgba(0,0,0,.3);
} }
.header-inner { .header-inner {
display: flex; align-items: center; gap: 32px; display: flex; align-items: center; gap: 32px;
height: 100%; height: 100%;
} }
/* 로고 */ /* 로고 */
.logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0; cursor: pointer; text-decoration: none; } .logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0; cursor: pointer; text-decoration: none; }
.logo img { height: 40px; width: auto; } .logo img { height: 40px; width: auto; }
.logo-text { color: #fff; font-size: 20px; font-weight: 700; } .logo-text { color: #fff; font-size: 20px; font-weight: 700; }
.logo-text strong { color: var(--accent); } .logo-text strong { color: var(--accent); }
/* 데스크톱 메뉴 */ /* 데스크톱 메뉴 */
.nav-desktop { .nav-desktop {
display: flex; align-items: center; gap: 4px; display: flex; align-items: center; gap: 4px;
margin-left: 24px; flex: 1; margin-left: 24px; flex: 1;
} }
.nav-item { position: relative; } .nav-item { position: relative; }
.nav-trigger { .nav-trigger {
height: var(--header-h); height: var(--header-h);
padding: 0 16px; padding: 0 16px;
color: rgba(255,255,255,.85); color: rgba(255,255,255,.85);
font-size: 15px; font-weight: 500; font-size: 15px; font-weight: 500;
transition: color var(--fast); transition: color var(--fast);
display: flex; align-items: center; display: flex; align-items: center;
} }
.nav-trigger:hover, .nav-trigger:hover,
.nav-item.active .nav-trigger { color: #fff; } .nav-item.active .nav-trigger { color: #fff; }
.nav-item.active .nav-trigger { border-bottom: 2px solid var(--accent); } .nav-item.active .nav-trigger { border-bottom: 2px solid var(--accent); }
/* 드롭다운 */ /* 드롭다운 */
.dropdown { .dropdown {
position: absolute; top: calc(var(--header-h) - 2px); left: 0; position: absolute; top: calc(var(--header-h) - 2px); left: 0;
min-width: 180px; min-width: 180px;
background: #fff; background: #fff;
border-radius: 0 0 var(--radius) var(--radius); border-radius: 0 0 var(--radius) var(--radius);
box-shadow: var(--shadow-lg); box-shadow: var(--shadow-lg);
border-top: 3px solid var(--primary); border-top: 3px solid var(--primary);
padding: 8px 0; padding: 8px 0;
animation: fadeDown .18s ease; animation: fadeDown .18s ease;
} }
@keyframes fadeDown { @keyframes fadeDown {
from { opacity:0; transform:translateY(-8px); } from { opacity:0; transform:translateY(-8px); }
to { opacity:1; transform:translateY(0); } to { opacity:1; transform:translateY(0); }
} }
.dropdown-item { .dropdown-item {
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px;
padding: 10px 20px; padding: 10px 20px;
font-size: 14px; color: var(--gray-700); font-size: 14px; color: var(--gray-700);
transition: all var(--fast); transition: all var(--fast);
} }
.dropdown-item:hover, .dropdown-item.current { .dropdown-item:hover, .dropdown-item.current {
background: var(--primary-light); background: var(--primary-light);
color: var(--primary); color: var(--primary);
} }
/* CTA 버튼 */ /* CTA 버튼 */
.header-cta { margin-left: auto; flex-shrink: 0; } .header-cta { margin-left: auto; flex-shrink: 0; }
/* 햄버거 */ /* 햄버거 */
.hamburger { .hamburger {
display: none; display: none;
flex-direction: column; flex-direction: column;
gap: 5px; gap: 5px;
padding: 8px; padding: 8px;
margin-left: auto; margin-left: auto;
} }
.hamburger span { .hamburger span {
display: block; width: 24px; height: 2px; display: block; width: 24px; height: 2px;
background: #fff; background: #fff;
border-radius: 2px; border-radius: 2px;
transition: all var(--mid); transition: all var(--mid);
} }
/* 모바일 메뉴 */ /* 모바일 메뉴 */
.nav-mobile { .nav-mobile {
display: none; display: none;
flex-direction: column; flex-direction: column;
background: var(--secondary); background: var(--secondary);
border-top: 1px solid rgba(255,255,255,.1); border-top: 1px solid rgba(255,255,255,.1);
max-height: calc(100vh - var(--header-h)); max-height: calc(100vh - var(--header-h));
overflow-y: auto; overflow-y: auto;
} }
.mobile-group { border-bottom: 1px solid rgba(255,255,255,.08); } .mobile-group { border-bottom: 1px solid rgba(255,255,255,.08); }
.mobile-group-header { .mobile-group-header {
display: flex; align-items: center; display: flex; align-items: center;
padding: 14px 24px; padding: 14px 24px;
color: rgba(255,255,255,.85); color: rgba(255,255,255,.85);
font-size: 15px; font-weight: 500; font-size: 15px; font-weight: 500;
cursor: pointer; cursor: pointer;
} }
.mobile-children { background: rgba(0,0,0,.2); } .mobile-children { background: rgba(0,0,0,.2); }
.mobile-child { .mobile-child {
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px;
padding: 10px 36px; padding: 10px 36px;
font-size: 14px; color: rgba(255,255,255,.7); font-size: 14px; color: rgba(255,255,255,.7);
} }
.mobile-child:hover { color: #fff; } .mobile-child:hover { color: #fff; }
/* 반응형 */ /* 반응형 */
@media (max-width: 1024px) { @media (max-width: 1024px) {
.nav-desktop, .header-cta { display: none; } .nav-desktop, .header-cta { display: none; }
.hamburger { display: flex; } .hamburger { display: flex; }
.header.mobile-open .nav-mobile { display: flex; } .header.mobile-open .nav-mobile { display: flex; }
.header.mobile-open { height: auto; } .header.mobile-open { height: auto; }
} }

View File

@ -1,200 +1,200 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom'; import { Link, useLocation, useNavigate } from 'react-router-dom';
import './Header.css'; import './Header.css';
const MENU = [ const MENU = [
{ {
id: 'company', label: '회사소개', id: 'company', label: '회사소개',
children: [ children: [
{ label: 'CEO 인사말', path: '/company/greeting' }, { label: 'CEO 인사말', path: '/company/greeting' },
{ label: '연혁', path: '/company/history' }, { label: '연혁', path: '/company/history' },
{ label: '조직도', path: '/company/organization' }, { label: '조직도', path: '/company/organization' },
{ label: 'CI 소개', path: '/company/ci' }, { label: 'CI 소개', path: '/company/ci' },
{ label: '오시는 길', path: '/company/location' }, { label: '오시는 길', path: '/company/location' },
] ]
}, },
{ {
id: 'solution', label: '솔루션', id: 'solution', label: '솔루션',
children: [ children: [
{ label: 'GUARDiA ITSM', path: '/solution/guardia', badge: 'NEW' }, { label: 'GUARDiA ITSM', path: '/solution/guardia', badge: 'NEW' },
{ label: 'ERP', path: '/solution/erp' }, { label: 'ERP', path: '/solution/erp' },
{ label: 'CRM', path: '/solution/crm' }, { label: 'CRM', path: '/solution/crm' },
{ label: 'BI', path: '/solution/bi' }, { label: 'BI', path: '/solution/bi' },
] ]
}, },
{ {
id: 'business', label: '사업실적', id: 'business', label: '사업실적',
children: [ children: [
{ label: '구축 레퍼런스', path: '/business/reference' }, { label: '구축 레퍼런스', path: '/business/reference' },
{ label: '파트너', path: '/business/partner' }, { label: '파트너', path: '/business/partner' },
] ]
}, },
{ {
id: 'support', label: '고객지원', id: 'support', label: '고객지원',
children: [ children: [
{ label: '공지사항', path: '/support/notice' }, { label: '공지사항', path: '/support/notice' },
{ label: 'FAQ', path: '/support/faq' }, { label: 'FAQ', path: '/support/faq' },
{ label: '카탈로그', path: '/support/catalog' }, { label: '카탈로그', path: '/support/catalog' },
{ label: '문의하기', path: '/support/contact' }, { label: '문의하기', path: '/support/contact' },
] ]
}, },
{ {
id: 'recruit', label: '채용', id: 'recruit', label: '채용',
children: [ children: [
{ label: '채용공고', path: '/recruit/jobs' }, { label: '채용공고', path: '/recruit/jobs' },
{ label: '복리후생', path: '/recruit/welfare' }, { label: '복리후생', path: '/recruit/welfare' },
{ label: '지원하기', path: '/recruit/apply' }, { label: '지원하기', path: '/recruit/apply' },
] ]
}, },
{ {
id: 'news', label: '뉴스', id: 'news', label: '뉴스',
children: [ children: [
{ label: '뉴스룸', path: '/news/newsroom' }, { label: '뉴스룸', path: '/news/newsroom' },
{ label: '기술 블로그', path: '/news/blog' }, { label: '기술 블로그', path: '/news/blog' },
] ]
}, },
]; ];
export default function Header() { export default function Header() {
const [scrolled, setScrolled] = useState(false); const [scrolled, setScrolled] = useState(false);
const [activeMenu, setActiveMenu] = useState(null); const [activeMenu, setActiveMenu] = useState(null);
const [mobileOpen, setMobileOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false);
const [member, setMember] = useState(null); const [member, setMember] = useState(null);
const closeTimer = React.useRef(null); const closeTimer = React.useRef(null);
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
// //
useEffect(() => { useEffect(() => {
const sync = () => { const sync = () => {
const u = localStorage.getItem('member_user'); const u = localStorage.getItem('member_user');
setMember(u ? JSON.parse(u) : null); setMember(u ? JSON.parse(u) : null);
}; };
sync(); sync();
window.addEventListener('storage', sync); window.addEventListener('storage', sync);
return () => window.removeEventListener('storage', sync); return () => window.removeEventListener('storage', sync);
}, [location]); }, [location]);
const logout = () => { const logout = () => {
localStorage.removeItem('member_token'); localStorage.removeItem('member_token');
localStorage.removeItem('member_user'); localStorage.removeItem('member_user');
setMember(null); setMember(null);
navigate('/'); navigate('/');
}; };
useEffect(() => { useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 60); const onScroll = () => setScrolled(window.scrollY > 60);
window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll);
}, []); }, []);
useEffect(() => { useEffect(() => {
setMobileOpen(false); setMobileOpen(false);
setActiveMenu(null); setActiveMenu(null);
}, [location]); }, [location]);
const isActive = (menu) => const isActive = (menu) =>
menu.children?.some(c => location.pathname.startsWith(c.path)); menu.children?.some(c => location.pathname.startsWith(c.path));
return ( return (
<> <>
{/* 접근성 스킵 링크 */} {/* 접근성 스킵 링크 */}
<a href="#main-content" className="skip-link">본문 바로가기</a> <a href="#main-content" className="skip-link">본문 바로가기</a>
<header className={`header ${scrolled ? 'scrolled' : ''} ${mobileOpen ? 'mobile-open' : ''}`} <header className={`header ${scrolled ? 'scrolled' : ''} ${mobileOpen ? 'mobile-open' : ''}`}
role="banner"> role="banner">
<div className="header-inner container"> <div className="header-inner container">
{/* 로고 */} {/* 로고 */}
<Link to="/" className="logo" aria-label="(주)지오정보기술 홈으로"> <Link to="/" className="logo" aria-label="(주)지오정보기술 홈으로">
<img src="/zioinfo-logo-dark.png" alt="(주)지오정보기술 로고" height="40" <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'; }; }} /> 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'}}> <span className="logo-text" style={{display:'none'}}>
<strong>Zio</strong>Info <strong>Zio</strong>Info
</span> </span>
</Link> </Link>
{/* 데스크톱 메뉴 */} {/* 데스크톱 메뉴 */}
<nav className="nav-desktop" role="navigation" aria-label="주요 메뉴"> <nav className="nav-desktop" role="navigation" aria-label="주요 메뉴">
{MENU.map(menu => ( {MENU.map(menu => (
<div key={menu.id} <div key={menu.id}
className={`nav-item ${isActive(menu) ? 'active' : ''}`} className={`nav-item ${isActive(menu) ? 'active' : ''}`}
onMouseEnter={() => { clearTimeout(closeTimer.current); setActiveMenu(menu.id); }} onMouseEnter={() => { clearTimeout(closeTimer.current); setActiveMenu(menu.id); }}
onMouseLeave={() => { closeTimer.current = setTimeout(() => setActiveMenu(null), 200); }}> onMouseLeave={() => { closeTimer.current = setTimeout(() => setActiveMenu(null), 200); }}>
<button className="nav-trigger" aria-haspopup="true" <button className="nav-trigger" aria-haspopup="true"
aria-expanded={activeMenu === menu.id} aria-expanded={activeMenu === menu.id}
onClick={() => navigate(menu.children[0].path)}> onClick={() => navigate(menu.children[0].path)}>
{menu.label} {menu.label}
</button> </button>
{activeMenu === menu.id && ( {activeMenu === menu.id && (
<div className="dropdown" role="menu"> <div className="dropdown" role="menu">
{menu.children.map(child => ( {menu.children.map(child => (
<Link key={child.path} to={child.path} <Link key={child.path} to={child.path}
className={`dropdown-item ${location.pathname === child.path ? 'current' : ''}`} className={`dropdown-item ${location.pathname === child.path ? 'current' : ''}`}
role="menuitem"> role="menuitem">
{child.label} {child.label}
{child.badge && <span className="badge badge-new">{child.badge}</span>} {child.badge && <span className="badge badge-new">{child.badge}</span>}
</Link> </Link>
))} ))}
</div> </div>
)} )}
</div> </div>
))} ))}
</nav> </nav>
{/* 우측 버튼 영역 */} {/* 우측 버튼 영역 */}
<div style={{ display:'flex', alignItems:'center', gap:8 }}> <div style={{ display:'flex', alignItems:'center', gap:8 }}>
<Link to="/support/contact" className="btn btn-outline btn-sm">문의하기</Link> <Link to="/support/contact" className="btn btn-outline btn-sm">문의하기</Link>
{member ? ( {member ? (
<div style={{ display:'flex', alignItems:'center', gap:8 }}> <div style={{ display:'flex', alignItems:'center', gap:8 }}>
<span style={{ fontSize:13, color:'var(--gray-600)', maxWidth:100, <span style={{ fontSize:13, color:'var(--gray-600)', maxWidth:100,
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}> overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
{member.name} {member.name}
</span> </span>
<button onClick={logout} <button onClick={logout}
style={{ padding:'6px 14px', background:'none', border:'1px solid #e2e8f0', style={{ padding:'6px 14px', background:'none', border:'1px solid #e2e8f0',
borderRadius:8, fontSize:12, color:'#64748b', cursor:'pointer' }}> borderRadius:8, fontSize:12, color:'#64748b', cursor:'pointer' }}>
로그아웃 로그아웃
</button> </button>
</div> </div>
) : ( ) : (
<Link to="/login" className="btn btn-primary btn-sm">로그인</Link> <Link to="/login" className="btn btn-primary btn-sm">로그인</Link>
)} )}
</div> </div>
{/* 햄버거 (모바일) */} {/* 햄버거 (모바일) */}
<button className="hamburger" aria-label="모바일 메뉴" <button className="hamburger" aria-label="모바일 메뉴"
aria-expanded={mobileOpen} aria-expanded={mobileOpen}
onClick={() => setMobileOpen(v => !v)}> onClick={() => setMobileOpen(v => !v)}>
<span/><span/><span/> <span/><span/><span/>
</button> </button>
</div> </div>
{/* 모바일 메뉴 */} {/* 모바일 메뉴 */}
{mobileOpen && ( {mobileOpen && (
<nav className="nav-mobile" role="navigation" aria-label="모바일 메뉴"> <nav className="nav-mobile" role="navigation" aria-label="모바일 메뉴">
{MENU.map(menu => ( {MENU.map(menu => (
<details key={menu.id} className="mobile-group"> <details key={menu.id} className="mobile-group">
<summary className="mobile-group-header">{menu.label}</summary> <summary className="mobile-group-header">{menu.label}</summary>
<div className="mobile-children"> <div className="mobile-children">
{menu.children.map(child => ( {menu.children.map(child => (
<Link key={child.path} to={child.path} className="mobile-child"> <Link key={child.path} to={child.path} className="mobile-child">
{child.label} {child.label}
{child.badge && <span className="badge badge-new">{child.badge}</span>} {child.badge && <span className="badge badge-new">{child.badge}</span>}
</Link> </Link>
))} ))}
</div> </div>
</details> </details>
))} ))}
<div style={{ display:'flex', gap:8, margin:'16px' }}> <div style={{ display:'flex', gap:8, margin:'16px' }}>
<Link to="/support/contact" className="btn btn-outline" style={{ flex:1 }}>문의하기</Link> <Link to="/support/contact" className="btn btn-outline" style={{ flex:1 }}>문의하기</Link>
{member {member
? <button onClick={logout} className="btn btn-primary" style={{ flex:1 }}>로그아웃</button> ? <button onClick={logout} className="btn btn-primary" style={{ flex:1 }}>로그아웃</button>
: <Link to="/login" className="btn btn-primary" style={{ flex:1 }}>로그인 / 가입</Link> : <Link to="/login" className="btn btn-primary" style={{ flex:1 }}>로그인 / 가입</Link>
} }
</div> </div>
</nav> </nav>
)} )}
</header> </header>
</> </>
); );
} }

View File

@ -1,293 +1,293 @@
import React from 'react'; import React from 'react';
import { Routes, Route, NavLink } from 'react-router-dom'; import { Routes, Route, NavLink } from 'react-router-dom';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import './Common.css'; import './Common.css';
import './SolutionPage.css'; import './SolutionPage.css';
const SUB_NAV = [ const SUB_NAV = [
{ path: '/solution/guardia', label: 'GUARDiA ITSM', badge: 'NEW' }, { path: '/solution/guardia', label: 'GUARDiA ITSM', badge: 'NEW' },
{ path: '/solution/erp', label: 'ERP' }, { path: '/solution/erp', label: 'ERP' },
{ path: '/solution/crm', label: 'CRM' }, { path: '/solution/crm', label: 'CRM' },
{ path: '/solution/bi', label: 'BI' }, { path: '/solution/bi', label: 'BI' },
]; ];
function SubNav({ title }) { function SubNav({ title }) {
return ( return (
<> <>
<div className="page-hero"> <div className="page-hero">
<div className="container"> <div className="container">
<span className="section-label">Solution</span> <span className="section-label">Solution</span>
<h1 className="page-hero-title">{title}</h1> <h1 className="page-hero-title">{title}</h1>
</div> </div>
</div> </div>
<nav className="sub-nav"> <nav className="sub-nav">
<div className="container"> <div className="container">
{SUB_NAV.map(n => ( {SUB_NAV.map(n => (
<NavLink key={n.path} to={n.path} <NavLink key={n.path} to={n.path}
className={({ isActive }) => 'sub-nav-item' + (isActive ? ' active' : '')}> className={({ isActive }) => 'sub-nav-item' + (isActive ? ' active' : '')}>
{n.label} {n.label}
{n.badge && <span className="badge badge-new" style={{ marginLeft: '6px', fontSize: '10px' }}>{n.badge}</span>} {n.badge && <span className="badge badge-new" style={{ marginLeft: '6px', fontSize: '10px' }}>{n.badge}</span>}
</NavLink> </NavLink>
))} ))}
</div> </div>
</nav> </nav>
</> </>
); );
} }
/* ── ERP ── */ /* ── ERP ── */
function ERP() { function ERP() {
const modules = [ const modules = [
{ icon: '💰', name: '재무·회계', desc: '전표처리, 결산, 세무신고, 원가계산 자동화' }, { icon: '💰', name: '재무·회계', desc: '전표처리, 결산, 세무신고, 원가계산 자동화' },
{ icon: '🏭', name: '생산관리', desc: 'BOM 관리, 생산계획, 공정관리, 품질관리' }, { icon: '🏭', name: '생산관리', desc: 'BOM 관리, 생산계획, 공정관리, 품질관리' },
{ icon: '📦', name: '구매·재고', desc: '발주, 입출고, 재고 현황, 협력사 포털' }, { icon: '📦', name: '구매·재고', desc: '발주, 입출고, 재고 현황, 협력사 포털' },
{ icon: '👥', name: '인사·급여', desc: '근태관리, 급여계산, 조직도, 인사평가' }, { icon: '👥', name: '인사·급여', desc: '근태관리, 급여계산, 조직도, 인사평가' },
{ icon: '🛒', name: '영업·물류', desc: '수주관리, 배송, 매출 분석, 고객 관리' }, { icon: '🛒', name: '영업·물류', desc: '수주관리, 배송, 매출 분석, 고객 관리' },
{ icon: '📊', name: '경영 분석', desc: 'KPI 대시보드, 예산 vs 실적, 경영 보고서' }, { icon: '📊', name: '경영 분석', desc: 'KPI 대시보드, 예산 vs 실적, 경영 보고서' },
]; ];
return ( return (
<main id="main-content" className="inner-page"> <main id="main-content" className="inner-page">
<SubNav title="ERP 솔루션" /> <SubNav title="ERP 솔루션" />
<section className="section"> <section className="section">
<div className="container"> <div className="container">
<div className="sol-hero-grid"> <div className="sol-hero-grid">
<div> <div>
<span className="section-label">Enterprise Resource Planning</span> <span className="section-label">Enterprise Resource Planning</span>
<h2 className="sol-title">공공·중견기업 맞춤형<br /><em>통합 ERP 솔루션</em></h2> <h2 className="sol-title">공공·중견기업 맞춤형<br /><em>통합 ERP 솔루션</em></h2>
<p className="sol-desc"> <p className="sol-desc">
20 이상 현대모비스, 한화그룹, 이마트 국내 주요 기업의 핵심 업무 시스템을 구축한 경험을 바탕으로, 20 이상 현대모비스, 한화그룹, 이마트 국내 주요 기업의 핵심 업무 시스템을 구축한 경험을 바탕으로,
고객사의 업무 프로세스에 최적화된 맞춤형 ERP를 제공합니다. 고객사의 업무 프로세스에 최적화된 맞춤형 ERP를 제공합니다.
</p> </p>
<div className="sol-features"> <div className="sol-features">
{['공공기관 표준 회계 기준 적용', 'Oracle / Tibero DB 지원', '모바일 결재·보고 지원', '기존 레거시 시스템 연계'].map((f, i) => ( {['공공기관 표준 회계 기준 적용', 'Oracle / Tibero DB 지원', '모바일 결재·보고 지원', '기존 레거시 시스템 연계'].map((f, i) => (
<div key={i} className="sol-feature-item"> <div key={i} className="sol-feature-item">
<span className="sol-check"></span> {f} <span className="sol-check"></span> {f}
</div> </div>
))} ))}
</div> </div>
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}> <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/contact?type=데모 신청" className="btn btn-primary btn-lg">무료 데모 신청</Link>
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그 다운로드</Link> <Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그 다운로드</Link>
</div> </div>
</div> </div>
<div className="sol-visual erp-visual"> <div className="sol-visual erp-visual">
<div className="sol-screen"> <div className="sol-screen">
<div className="sol-screen-header"><span />재무 대시보드</div> <div className="sol-screen-header"><span />재무 대시보드</div>
<div className="sol-chart-bar-wrap"> <div className="sol-chart-bar-wrap">
{[80, 65, 90, 72, 88, 55, 95].map((h, i) => ( {[80, 65, 90, 72, 88, 55, 95].map((h, i) => (
<div key={i} className="sol-chart-bar" style={{ height: h + '%' }} /> <div key={i} className="sol-chart-bar" style={{ height: h + '%' }} />
))} ))}
</div> </div>
<div className="sol-stat-row"> <div className="sol-stat-row">
<div className="sol-stat"><strong>12.4</strong><span>이번달 매출</span></div> <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>98.2%</strong><span>예산 집행률</span></div>
<div className="sol-stat"><strong>+18%</strong><span>전월 대비</span></div> <div className="sol-stat"><strong>+18%</strong><span>전월 대비</span></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* 모듈 */} {/* 모듈 */}
<div style={{ marginTop: '80px' }}> <div style={{ marginTop: '80px' }}>
<div className="section-header"> <div className="section-header">
<span className="section-label">Modules</span> <span className="section-label">Modules</span>
<h2 className="section-title">6 핵심 모듈</h2> <h2 className="section-title">6 핵심 모듈</h2>
</div> </div>
<div className="grid-3"> <div className="grid-3">
{modules.map((m, i) => ( {modules.map((m, i) => (
<div key={i} className="card sol-module-card"> <div key={i} className="card sol-module-card">
<div className="sol-module-icon">{m.icon}</div> <div className="sol-module-icon">{m.icon}</div>
<h3>{m.name}</h3> <h3>{m.name}</h3>
<p>{m.desc}</p> <p>{m.desc}</p>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</main> </main>
); );
} }
/* ── CRM ── */ /* ── CRM ── */
function CRM() { function CRM() {
const features = [ const features = [
{ icon: '📇', name: '고객 360˚', desc: '고객 정보, 구매이력, 상담이력, 선호도를 단일 뷰로 통합' }, { icon: '📇', name: '고객 360˚', desc: '고객 정보, 구매이력, 상담이력, 선호도를 단일 뷰로 통합' },
{ icon: '📞', name: '멀티채널 상담', desc: '전화·이메일·채팅·SNS 통합 인입, 상담 이력 자동 기록' }, { icon: '📞', name: '멀티채널 상담', desc: '전화·이메일·채팅·SNS 통합 인입, 상담 이력 자동 기록' },
{ icon: '🎯', name: '영업 파이프라인', desc: '리드 발굴부터 계약까지 전 단계 시각화 관리' }, { icon: '🎯', name: '영업 파이프라인', desc: '리드 발굴부터 계약까지 전 단계 시각화 관리' },
{ icon: '📨', name: '마케팅 자동화', desc: '고객 세그먼트별 자동 캠페인, 이메일·SMS 발송' }, { icon: '📨', name: '마케팅 자동화', desc: '고객 세그먼트별 자동 캠페인, 이메일·SMS 발송' },
{ icon: '🤖', name: 'AI 상담 추천', desc: 'Ollama LLM 기반 최적 답변 자동 추천 및 요약' }, { icon: '🤖', name: 'AI 상담 추천', desc: 'Ollama LLM 기반 최적 답변 자동 추천 및 요약' },
{ icon: '📈', name: '성과 분석', desc: '상담사별·채널별 KPI, 고객 만족도, 전환율 리포트' }, { icon: '📈', name: '성과 분석', desc: '상담사별·채널별 KPI, 고객 만족도, 전환율 리포트' },
]; ];
return ( return (
<main id="main-content" className="inner-page"> <main id="main-content" className="inner-page">
<SubNav title="CRM 솔루션" /> <SubNav title="CRM 솔루션" />
<section className="section"> <section className="section">
<div className="container"> <div className="container">
<div className="sol-hero-grid"> <div className="sol-hero-grid">
<div> <div>
<span className="section-label">Customer Relationship Management</span> <span className="section-label">Customer Relationship Management</span>
<h2 className="sol-title">AI 기반<br /><em>고객 관계 관리 플랫폼</em></h2> <h2 className="sol-title">AI 기반<br /><em>고객 관계 관리 플랫폼</em></h2>
<p className="sol-desc"> <p className="sol-desc">
삼성전자 차세대 CRM, LG U+ VAN 고도화, 현대캐피탈 차세대 시스템 삼성전자 차세대 CRM, LG U+ VAN 고도화, 현대캐피탈 차세대 시스템
국내 최대 규모 CRM 프로젝트를 성공적으로 수행한 전문 역량으로 구축합니다. 국내 최대 규모 CRM 프로젝트를 성공적으로 수행한 전문 역량으로 구축합니다.
온프레미스 AI(Ollama) 연동으로 데이터 외부 유출 없이 지능형 상담을 실현합니다. 온프레미스 AI(Ollama) 연동으로 데이터 외부 유출 없이 지능형 상담을 실현합니다.
</p> </p>
<div className="sol-features"> <div className="sol-features">
{['삼성전자·LG·현대 구축 레퍼런스', '온프레미스 AI 상담 추천', 'CTI 연동 (콜센터 솔루션)', '공공기관 개인정보보호법 준수'].map((f, i) => ( {['삼성전자·LG·현대 구축 레퍼런스', '온프레미스 AI 상담 추천', 'CTI 연동 (콜센터 솔루션)', '공공기관 개인정보보호법 준수'].map((f, i) => (
<div key={i} className="sol-feature-item"> <div key={i} className="sol-feature-item">
<span className="sol-check"></span> {f} <span className="sol-check"></span> {f}
</div> </div>
))} ))}
</div> </div>
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}> <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/contact?type=데모 신청" className="btn btn-primary btn-lg">데모 신청</Link>
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link> <Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link>
</div> </div>
</div> </div>
<div className="sol-visual crm-visual"> <div className="sol-visual crm-visual">
<div className="sol-screen"> <div className="sol-screen">
<div className="sol-screen-header"><span />고객 상담 현황</div> <div className="sol-screen-header"><span />고객 상담 현황</div>
<div className="crm-items"> <div className="crm-items">
{[ {[
{ name: '김민준', type: '제품문의', status: '처리중', color: '#f59e0b' }, { name: '김민준', type: '제품문의', status: '처리중', color: '#f59e0b' },
{ name: '이서연', type: '기술지원', status: '완료', color: '#10b981' }, { name: '이서연', type: '기술지원', status: '완료', color: '#10b981' },
{ name: '박지후', type: '불만접수', status: '대기', color: '#ef4444' }, { name: '박지후', type: '불만접수', status: '대기', color: '#ef4444' },
{ name: '최수아', type: '데모신청', status: '완료', color: '#10b981' }, { name: '최수아', type: '데모신청', status: '완료', color: '#10b981' },
].map((c, i) => ( ].map((c, i) => (
<div key={i} className="crm-item"> <div key={i} className="crm-item">
<div className="crm-avatar">{c.name[0]}</div> <div className="crm-avatar">{c.name[0]}</div>
<div className="crm-info"> <div className="crm-info">
<strong>{c.name}</strong> <strong>{c.name}</strong>
<span>{c.type}</span> <span>{c.type}</span>
</div> </div>
<span className="crm-status" style={{ color: c.color }}>{c.status}</span> <span className="crm-status" style={{ color: c.color }}>{c.status}</span>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div style={{ marginTop: '80px' }}> <div style={{ marginTop: '80px' }}>
<div className="section-header"> <div className="section-header">
<span className="section-label">Features</span> <span className="section-label">Features</span>
<h2 className="section-title">주요 기능</h2> <h2 className="section-title">주요 기능</h2>
</div> </div>
<div className="grid-3"> <div className="grid-3">
{features.map((f, i) => ( {features.map((f, i) => (
<div key={i} className="card sol-module-card"> <div key={i} className="card sol-module-card">
<div className="sol-module-icon">{f.icon}</div> <div className="sol-module-icon">{f.icon}</div>
<h3>{f.name}</h3> <h3>{f.name}</h3>
<p>{f.desc}</p> <p>{f.desc}</p>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</main> </main>
); );
} }
/* ── BI ── */ /* ── BI ── */
function BI() { function BI() {
const charts = [ const charts = [
{ icon: '📊', name: '경영 대시보드', desc: '실시간 KPI 모니터링, 부서별 성과 지표, 경영진 요약 보고' }, { icon: '📊', name: '경영 대시보드', desc: '실시간 KPI 모니터링, 부서별 성과 지표, 경영진 요약 보고' },
{ icon: '📉', name: '매출·비용 분석', desc: '기간별·제품별·채널별 매출 트렌드, 비용 구조 분석' }, { icon: '📉', name: '매출·비용 분석', desc: '기간별·제품별·채널별 매출 트렌드, 비용 구조 분석' },
{ icon: '🗺️', name: '지역별 분석', desc: '지도 기반 시각화, 공공기관 지역별 서비스 현황' }, { icon: '🗺️', name: '지역별 분석', desc: '지도 기반 시각화, 공공기관 지역별 서비스 현황' },
{ icon: '🔮', name: 'AI 예측 분석', desc: '머신러닝 기반 수요 예측, 이상 패턴 자동 탐지' }, { icon: '🔮', name: 'AI 예측 분석', desc: '머신러닝 기반 수요 예측, 이상 패턴 자동 탐지' },
{ icon: '📋', name: '자동 보고서', desc: '일·주·월 보고서 자동 생성, 이메일·메신저 배포' }, { icon: '📋', name: '자동 보고서', desc: '일·주·월 보고서 자동 생성, 이메일·메신저 배포' },
{ icon: '🔗', name: 'ETL 파이프라인', desc: 'Oracle, SAP, 공공DB 등 다양한 소스 데이터 연계' }, { icon: '🔗', name: 'ETL 파이프라인', desc: 'Oracle, SAP, 공공DB 등 다양한 소스 데이터 연계' },
]; ];
return ( return (
<main id="main-content" className="inner-page"> <main id="main-content" className="inner-page">
<SubNav title="BI 솔루션" /> <SubNav title="BI 솔루션" />
<section className="section"> <section className="section">
<div className="container"> <div className="container">
<div className="sol-hero-grid"> <div className="sol-hero-grid">
<div> <div>
<span className="section-label">Business Intelligence</span> <span className="section-label">Business Intelligence</span>
<h2 className="sol-title">데이터 기반<br /><em>의사결정 플랫폼</em></h2> <h2 className="sol-title">데이터 기반<br /><em>의사결정 플랫폼</em></h2>
<p className="sol-desc"> <p className="sol-desc">
OZ Report, MiPlatform, JasperReports 다양한 보고 도구와의 연동 경험을 바탕으로, OZ Report, MiPlatform, JasperReports 다양한 보고 도구와의 연동 경험을 바탕으로,
공공기관·중견기업 맞춤형 BI 플랫폼을 구축합니다. 공공기관·중견기업 맞춤형 BI 플랫폼을 구축합니다.
기존 레거시 DB에서 실시간 데이터를 수집해 경영 인사이트를 제공합니다. 기존 레거시 DB에서 실시간 데이터를 수집해 경영 인사이트를 제공합니다.
</p> </p>
<div className="sol-features"> <div className="sol-features">
{['OZ·MiPlatform·JasperReport 연동', '실시간 대시보드 (WebSocket)', 'Oracle·Tibero·PostgreSQL 지원', '공공기관 표준 보고서 양식'].map((f, i) => ( {['OZ·MiPlatform·JasperReport 연동', '실시간 대시보드 (WebSocket)', 'Oracle·Tibero·PostgreSQL 지원', '공공기관 표준 보고서 양식'].map((f, i) => (
<div key={i} className="sol-feature-item"> <div key={i} className="sol-feature-item">
<span className="sol-check"></span> {f} <span className="sol-check"></span> {f}
</div> </div>
))} ))}
</div> </div>
<div style={{ display: 'flex', gap: '12px', marginTop: '32px', flexWrap: 'wrap' }}> <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/contact?type=데모 신청" className="btn btn-primary btn-lg">데모 신청</Link>
<Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link> <Link to="/support/catalog" className="btn btn-outline btn-lg">카탈로그</Link>
</div> </div>
</div> </div>
<div className="sol-visual bi-visual"> <div className="sol-visual bi-visual">
<div className="sol-screen"> <div className="sol-screen">
<div className="sol-screen-header"><span />경영 대시보드</div> <div className="sol-screen-header"><span />경영 대시보드</div>
<div className="bi-kpis"> <div className="bi-kpis">
{[ {[
{ label:'매출', val:'₩48.2억', up:true, delta:'+12%' }, { label:'매출', val:'₩48.2억', up:true, delta:'+12%' },
{ label:'비용', val:'₩31.7억', up:false, delta:'-3%' }, { label:'비용', val:'₩31.7억', up:false, delta:'-3%' },
{ label:'이익', val:'₩16.5억', up:true, delta:'+28%' }, { label:'이익', val:'₩16.5억', up:true, delta:'+28%' },
{ label:'고객', val:'1,240명', up:true, delta:'+8%' }, { label:'고객', val:'1,240명', up:true, delta:'+8%' },
].map((k, i) => ( ].map((k, i) => (
<div key={i} className="bi-kpi"> <div key={i} className="bi-kpi">
<span className="bi-kpi-label">{k.label}</span> <span className="bi-kpi-label">{k.label}</span>
<strong className="bi-kpi-val">{k.val}</strong> <strong className="bi-kpi-val">{k.val}</strong>
<span className="bi-kpi-delta" style={{ color: k.up ? '#10b981' : '#ef4444' }}> <span className="bi-kpi-delta" style={{ color: k.up ? '#10b981' : '#ef4444' }}>
{k.delta} {k.delta}
</span> </span>
</div> </div>
))} ))}
</div> </div>
<div className="bi-bar-chart"> <div className="bi-bar-chart">
{['1Q','2Q','3Q','4Q'].map((q, i) => ( {['1Q','2Q','3Q','4Q'].map((q, i) => (
<div key={i} className="bi-bar-group"> <div key={i} className="bi-bar-group">
<div className="bi-bar-pair"> <div className="bi-bar-pair">
<div className="bi-bar revenue" style={{ height: [60, 75, 85, 100][i] + '%' }} /> <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 className="bi-bar cost" style={{ height: [70, 65, 60, 55][i] + '%' }} />
</div> </div>
<span>{q}</span> <span>{q}</span>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div style={{ marginTop: '80px' }}> <div style={{ marginTop: '80px' }}>
<div className="section-header"> <div className="section-header">
<span className="section-label">Features</span> <span className="section-label">Features</span>
<h2 className="section-title">주요 기능</h2> <h2 className="section-title">주요 기능</h2>
</div> </div>
<div className="grid-3"> <div className="grid-3">
{charts.map((c, i) => ( {charts.map((c, i) => (
<div key={i} className="card sol-module-card"> <div key={i} className="card sol-module-card">
<div className="sol-module-icon">{c.icon}</div> <div className="sol-module-icon">{c.icon}</div>
<h3>{c.name}</h3> <h3>{c.name}</h3>
<p>{c.desc}</p> <p>{c.desc}</p>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</main> </main>
); );
} }
export default function SolutionPage() { export default function SolutionPage() {
return ( return (
<Routes> <Routes>
<Route path="erp" element={<ERP />} /> <Route path="erp" element={<ERP />} />
<Route path="crm" element={<CRM />} /> <Route path="crm" element={<CRM />} />
<Route path="bi" element={<BI />} /> <Route path="bi" element={<BI />} />
<Route path="*" element={<ERP />} /> <Route path="*" element={<ERP />} />
</Routes> </Routes>
); );
} }