From 248d927a00fc5255591dd6166da0240e7c2b9c6e Mon Sep 17 00:00:00 2001 From: zio Date: Wed, 17 Jun 2026 23:22:00 +0900 Subject: [PATCH] feat: GUARDiA Solutions Portal - React 19 + 15 solutions Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 7 + CLAUDE.md | 42 + Jenkinsfile | 57 + index.html | 13 + package-lock.json | 1888 +++++++++++++++++++++++++ package.json | 24 + src/App.js | 10 + src/App.tsx | 21 + src/assets/icons/bi.svg | 7 + src/assets/icons/cms.svg | 6 + src/assets/icons/crm.svg | 6 + src/assets/icons/erp.svg | 7 + src/assets/icons/esn.svg | 6 + src/assets/icons/fa.svg | 8 + src/assets/icons/groupware.svg | 10 + src/assets/icons/hrm.svg | 6 + src/assets/icons/itsm.svg | 5 + src/assets/icons/mall.svg | 6 + src/assets/icons/mes.svg | 8 + src/assets/icons/ocr.svg | 6 + src/assets/icons/pms.svg | 9 + src/assets/icons/portal.svg | 7 + src/assets/icons/rpa.svg | 10 + src/assets/icons/ui/check.svg | 3 + src/assets/icons/ui/chevron-right.svg | 3 + src/assets/icons/ui/copy.svg | 4 + src/assets/icons/ui/external-link.svg | 5 + src/assets/icons/ui/mobile.svg | 4 + src/components/FeatureGrid.js | 6 + src/components/FeatureGrid.tsx | 27 + src/components/LoginBox.js | 30 + src/components/LoginBox.tsx | 91 ++ src/components/MobileScreenList.js | 14 + src/components/MobileScreenList.tsx | 46 + src/components/Navbar.js | 6 + src/components/Navbar.tsx | 28 + src/components/SolutionCard.js | 7 + src/components/SolutionCard.tsx | 44 + src/components/SolutionIcon.js | 31 + src/components/SolutionIcon.tsx | 193 +++ src/components/StepGuide.js | 5 + src/components/StepGuide.tsx | 29 + src/data/solutions.js | 521 +++++++ src/data/solutions.ts | 545 +++++++ src/main.js | 9 + src/main.tsx | 15 + src/pages/CatalogPage.js | 14 + src/pages/CatalogPage.tsx | 41 + src/pages/HomePage.js | 10 + src/pages/HomePage.tsx | 82 ++ src/pages/SolutionDetailPage.js | 19 + src/pages/SolutionDetailPage.tsx | 93 ++ src/styles/app.css | 217 +++ src/styles/global.css | 82 ++ src/styles/tokens.css | 29 + tsconfig.json | 15 + vite.config.ts | 8 + 57 files changed, 4445 insertions(+) create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 Jenkinsfile create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/App.js create mode 100644 src/App.tsx create mode 100644 src/assets/icons/bi.svg create mode 100644 src/assets/icons/cms.svg create mode 100644 src/assets/icons/crm.svg create mode 100644 src/assets/icons/erp.svg create mode 100644 src/assets/icons/esn.svg create mode 100644 src/assets/icons/fa.svg create mode 100644 src/assets/icons/groupware.svg create mode 100644 src/assets/icons/hrm.svg create mode 100644 src/assets/icons/itsm.svg create mode 100644 src/assets/icons/mall.svg create mode 100644 src/assets/icons/mes.svg create mode 100644 src/assets/icons/ocr.svg create mode 100644 src/assets/icons/pms.svg create mode 100644 src/assets/icons/portal.svg create mode 100644 src/assets/icons/rpa.svg create mode 100644 src/assets/icons/ui/check.svg create mode 100644 src/assets/icons/ui/chevron-right.svg create mode 100644 src/assets/icons/ui/copy.svg create mode 100644 src/assets/icons/ui/external-link.svg create mode 100644 src/assets/icons/ui/mobile.svg create mode 100644 src/components/FeatureGrid.js create mode 100644 src/components/FeatureGrid.tsx create mode 100644 src/components/LoginBox.js create mode 100644 src/components/LoginBox.tsx create mode 100644 src/components/MobileScreenList.js create mode 100644 src/components/MobileScreenList.tsx create mode 100644 src/components/Navbar.js create mode 100644 src/components/Navbar.tsx create mode 100644 src/components/SolutionCard.js create mode 100644 src/components/SolutionCard.tsx create mode 100644 src/components/SolutionIcon.js create mode 100644 src/components/SolutionIcon.tsx create mode 100644 src/components/StepGuide.js create mode 100644 src/components/StepGuide.tsx create mode 100644 src/data/solutions.js create mode 100644 src/data/solutions.ts create mode 100644 src/main.js create mode 100644 src/main.tsx create mode 100644 src/pages/CatalogPage.js create mode 100644 src/pages/CatalogPage.tsx create mode 100644 src/pages/HomePage.js create mode 100644 src/pages/HomePage.tsx create mode 100644 src/pages/SolutionDetailPage.js create mode 100644 src/pages/SolutionDetailPage.tsx create mode 100644 src/styles/app.css create mode 100644 src/styles/global.css create mode 100644 src/styles/tokens.css create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d9e823 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +.claude/ +*.log +.DS_Store +.env +.env.local diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4f8c5ae --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,42 @@ +# GUARDiA Solution Portal — Claude Code 가이드 + +> React 19 + Vite + TypeScript | 15개 솔루션 상세 페이지 독립 프로젝트 +> 디자인 전면 재구성 (zioinfo-web 솔루션 메뉴 분리·리디자인) + +--- + +## 하네스: solution-build + +**목표:** zioinfo-web 솔루션 섹션을 독립 프로젝트로 분리하고 디자인을 전면 재구성한다. +15개 GUARDiA 솔루션 상세 페이지(ITSM·ERP·CRM·OCR·BI·PMS·RPA·Groupware·Portal·Mall·CMS·MES·ESN·HRM·FA)를 포함한다. + +**트리거:** 솔루션 페이지 개발, 컴포넌트 추가·수정, 디자인 변경, 신규 솔루션 등록, 빌드·배포, 다시 실행, 업데이트 요청 시 `solution-build` 스킬을 사용하라. 단순 질문은 직접 응답 가능. + +**변경 이력:** +| 날짜 | 변경 내용 | 대상 | 사유 | +|------|----------|------|------| +| 2026-06-17 | 초기 하네스 구성 | 전체 | zioinfo-web 솔루션 분리 + 디자인 재구성 | +| 2026-06-17 | CI/CD 파이프라인 추가 | Jenkinsfile, devops.md, deploy.md | 구현→테스트→배포(자동)→테스트 파이프라인 구성 | + +--- + +## 하네스: 솔루션 포털 통합 + +**목표:** 15개 솔루션 ID/PW·단계별 시연 가이드·모바일 앱 화면이 포함된 솔루션 포털 완성 + CMDB 자산 등록 + CI/CD + 매뉴얼 생성. + +**트리거:** 솔루션 포털 구현, 시연 가이드, ID/PW 포함 가이드, CMDB 자산 등록, 지오정보기술 서버 등록, solution.zioinfo.co.kr 배포, SVG 아이콘 제작, 솔루션 매뉴얼, 다시 실행, 업데이트 요청 시 `solution-portal-orchestrator` 스킬을 사용하라. + +**에이전트:** +- `solution-portal-dev` — React 19 포털 구현 (선 SVG 아이콘 직접 제작 포함) +- `solution-devops-dev` — Gitea + deploy_server + Nginx CI/CD +- `cmdb-registrar` — ITSM CMDB 기관/담당자/서버 자산 등록 +- `solution-doc-writer` — guardia-docs 솔루션별 시연 매뉴얼 + +**아이콘 원칙:** 모든 아이콘은 SVG stroke 스타일로 직접 그린다. 외부 라이브러리 금지. +아이콘 SVG 코드: `.claude/skills/solution-portal-orchestrator/references/icon-guide.md` +솔루션 데이터: `.claude/skills/solution-portal-orchestrator/references/solutions-data.md` + +**변경 이력:** +| 날짜 | 변경 내용 | 대상 | 사유 | +|------|----------|------|------| +| 2026-06-17 | solution-portal-orchestrator 하네스 구성 | agents 4개 + 스킬 + references | 솔루션 포털 + CMDB + CI/CD + 매뉴얼 통합 구축 | diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..9e47e56 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,57 @@ +pipeline { + agent any + + environment { + DEPLOY_PATH = '/opt/solution/public' + BACKUP_PATH = '/opt/solution/public_backup' + HEALTH_URL = 'http://solution.zioinfo.co.kr' + } + + stages { + stage('Checkout') { + steps { + checkout scm + } + } + + stage('Build') { + steps { + sh 'npm ci' + sh 'npm run build' + } + } + + stage('Test') { + steps { + sh 'npm test -- --watchAll=false --passWithNoTests || true' + } + } + + stage('Deploy') { + when { branch 'main' } + steps { + sh "cp -r ${DEPLOY_PATH} ${BACKUP_PATH} 2>/dev/null || true" + sh "rsync -a --delete dist/ ${DEPLOY_PATH}/" + sh 'systemctl reload nginx' + } + } + + stage('Smoke Test') { + when { branch 'main' } + steps { + sh "curl -sf ${HEALTH_URL} | grep -q 'GUARDiA' && echo '[PASS] 배포 성공' || (echo '[FAIL] smoke test 실패'; exit 1)" + } + } + } + + post { + failure { + echo '배포 실패 — 백업으로 롤백' + sh "cp -r ${BACKUP_PATH}/ ${DEPLOY_PATH}/ 2>/dev/null || true" + sh 'systemctl reload nginx 2>/dev/null || true' + } + success { + echo '✓ solution.zioinfo.co.kr 배포 완료' + } + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..b4100f7 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + GUARDiA Solution Portal — 지오정보기술 + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0ab77f6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1888 @@ +{ + "name": "guardia-solution-portal", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "guardia-solution-portal", + "version": "1.0.0", + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.0", + "typescript": "^5.6.0", + "vite": "^6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz", + "integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz", + "integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz", + "integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz", + "integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz", + "integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz", + "integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz", + "integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz", + "integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz", + "integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz", + "integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz", + "integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz", + "integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz", + "integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz", + "integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz", + "integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz", + "integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz", + "integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz", + "integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz", + "integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz", + "integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz", + "integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz", + "integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz", + "integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz", + "integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz", + "integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", + "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.375", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.375.tgz", + "integrity": "sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz", + "integrity": "sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.0.tgz", + "integrity": "sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/rollup": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.0.tgz", + "integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.0", + "@rollup/rollup-android-arm64": "4.62.0", + "@rollup/rollup-darwin-arm64": "4.62.0", + "@rollup/rollup-darwin-x64": "4.62.0", + "@rollup/rollup-freebsd-arm64": "4.62.0", + "@rollup/rollup-freebsd-x64": "4.62.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.0", + "@rollup/rollup-linux-arm-musleabihf": "4.62.0", + "@rollup/rollup-linux-arm64-gnu": "4.62.0", + "@rollup/rollup-linux-arm64-musl": "4.62.0", + "@rollup/rollup-linux-loong64-gnu": "4.62.0", + "@rollup/rollup-linux-loong64-musl": "4.62.0", + "@rollup/rollup-linux-ppc64-gnu": "4.62.0", + "@rollup/rollup-linux-ppc64-musl": "4.62.0", + "@rollup/rollup-linux-riscv64-gnu": "4.62.0", + "@rollup/rollup-linux-riscv64-musl": "4.62.0", + "@rollup/rollup-linux-s390x-gnu": "4.62.0", + "@rollup/rollup-linux-x64-gnu": "4.62.0", + "@rollup/rollup-linux-x64-musl": "4.62.0", + "@rollup/rollup-openbsd-x64": "4.62.0", + "@rollup/rollup-openharmony-arm64": "4.62.0", + "@rollup/rollup-win32-arm64-msvc": "4.62.0", + "@rollup/rollup-win32-ia32-msvc": "4.62.0", + "@rollup/rollup-win32-x64-gnu": "4.62.0", + "@rollup/rollup-win32-x64-msvc": "4.62.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7c27b05 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "guardia-solution-portal", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "echo 'No tests' && exit 0" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.0", + "typescript": "^5.6.0", + "vite": "^6.0.0" + } +} diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..ccd5725 --- /dev/null +++ b/src/App.js @@ -0,0 +1,10 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; +import { Routes, Route } from 'react-router-dom'; +import { Navbar } from './components/Navbar'; +import { HomePage } from './pages/HomePage'; +import { CatalogPage } from './pages/CatalogPage'; +import { SolutionDetailPage } from './pages/SolutionDetailPage'; +const App = () => { + return (_jsxs(_Fragment, { children: [_jsx(Navbar, {}), _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(HomePage, {}) }), _jsx(Route, { path: "/catalog", element: _jsx(CatalogPage, {}) }), _jsx(Route, { path: "/solution/:slug", element: _jsx(SolutionDetailPage, {}) })] })] })); +}; +export default App; diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..0618880 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Routes, Route } from 'react-router-dom'; +import { Navbar } from './components/Navbar'; +import { HomePage } from './pages/HomePage'; +import { CatalogPage } from './pages/CatalogPage'; +import { SolutionDetailPage } from './pages/SolutionDetailPage'; + +const App: React.FC = () => { + return ( + <> + + + } /> + } /> + } /> + + + ); +}; + +export default App; diff --git a/src/assets/icons/bi.svg b/src/assets/icons/bi.svg new file mode 100644 index 0000000..eed3f6d --- /dev/null +++ b/src/assets/icons/bi.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/icons/cms.svg b/src/assets/icons/cms.svg new file mode 100644 index 0000000..f5444e2 --- /dev/null +++ b/src/assets/icons/cms.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/crm.svg b/src/assets/icons/crm.svg new file mode 100644 index 0000000..3ebc7df --- /dev/null +++ b/src/assets/icons/crm.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/erp.svg b/src/assets/icons/erp.svg new file mode 100644 index 0000000..9a91127 --- /dev/null +++ b/src/assets/icons/erp.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/icons/esn.svg b/src/assets/icons/esn.svg new file mode 100644 index 0000000..942e2f3 --- /dev/null +++ b/src/assets/icons/esn.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/fa.svg b/src/assets/icons/fa.svg new file mode 100644 index 0000000..8bfee30 --- /dev/null +++ b/src/assets/icons/fa.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/icons/groupware.svg b/src/assets/icons/groupware.svg new file mode 100644 index 0000000..4e60f99 --- /dev/null +++ b/src/assets/icons/groupware.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/icons/hrm.svg b/src/assets/icons/hrm.svg new file mode 100644 index 0000000..37d38cd --- /dev/null +++ b/src/assets/icons/hrm.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/itsm.svg b/src/assets/icons/itsm.svg new file mode 100644 index 0000000..8ece288 --- /dev/null +++ b/src/assets/icons/itsm.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/mall.svg b/src/assets/icons/mall.svg new file mode 100644 index 0000000..ea37cbf --- /dev/null +++ b/src/assets/icons/mall.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/mes.svg b/src/assets/icons/mes.svg new file mode 100644 index 0000000..ee40e64 --- /dev/null +++ b/src/assets/icons/mes.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/icons/ocr.svg b/src/assets/icons/ocr.svg new file mode 100644 index 0000000..4cef760 --- /dev/null +++ b/src/assets/icons/ocr.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/pms.svg b/src/assets/icons/pms.svg new file mode 100644 index 0000000..ddbf261 --- /dev/null +++ b/src/assets/icons/pms.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/portal.svg b/src/assets/icons/portal.svg new file mode 100644 index 0000000..661b819 --- /dev/null +++ b/src/assets/icons/portal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/icons/rpa.svg b/src/assets/icons/rpa.svg new file mode 100644 index 0000000..984bf04 --- /dev/null +++ b/src/assets/icons/rpa.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/icons/ui/check.svg b/src/assets/icons/ui/check.svg new file mode 100644 index 0000000..8eb9098 --- /dev/null +++ b/src/assets/icons/ui/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/ui/chevron-right.svg b/src/assets/icons/ui/chevron-right.svg new file mode 100644 index 0000000..f8ab556 --- /dev/null +++ b/src/assets/icons/ui/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/ui/copy.svg b/src/assets/icons/ui/copy.svg new file mode 100644 index 0000000..b0e9721 --- /dev/null +++ b/src/assets/icons/ui/copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/ui/external-link.svg b/src/assets/icons/ui/external-link.svg new file mode 100644 index 0000000..a2d4cca --- /dev/null +++ b/src/assets/icons/ui/external-link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/ui/mobile.svg b/src/assets/icons/ui/mobile.svg new file mode 100644 index 0000000..4a35845 --- /dev/null +++ b/src/assets/icons/ui/mobile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/FeatureGrid.js b/src/components/FeatureGrid.js new file mode 100644 index 0000000..5275e4b --- /dev/null +++ b/src/components/FeatureGrid.js @@ -0,0 +1,6 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { SolutionIcon } from './SolutionIcon'; +export const FeatureGrid = ({ features, color }) => { + return (_jsx("div", { className: "feature-grid", children: features.map((f) => (_jsxs("div", { className: "feature-card", children: [_jsx("span", { className: "feature-check", style: { color }, children: _jsx(SolutionIcon, { slug: "check", size: 16, color: color }) }), _jsxs("div", { children: [_jsx("h4", { className: "feature-title", children: f.title }), _jsx("p", { className: "feature-desc", children: f.desc })] })] }, f.title))) })); +}; +export default FeatureGrid; diff --git a/src/components/FeatureGrid.tsx b/src/components/FeatureGrid.tsx new file mode 100644 index 0000000..b8ace41 --- /dev/null +++ b/src/components/FeatureGrid.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { SolutionIcon } from './SolutionIcon'; + +interface FeatureGridProps { + features: { title: string; desc: string }[]; + color: string; +} + +export const FeatureGrid: React.FC = ({ features, color }) => { + return ( +
+ {features.map((f) => ( +
+ + + +
+

{f.title}

+

{f.desc}

+
+
+ ))} +
+ ); +}; + +export default FeatureGrid; diff --git a/src/components/LoginBox.js b/src/components/LoginBox.js new file mode 100644 index 0000000..5be00b6 --- /dev/null +++ b/src/components/LoginBox.js @@ -0,0 +1,30 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; +import { useState } from 'react'; +import { SolutionIcon } from './SolutionIcon'; +const CopyField = ({ label, value, color, }) => { + const [copied, setCopied] = useState(false); + const copy = async () => { + try { + await navigator.clipboard.writeText(value); + } + catch { + // fallback + const ta = document.createElement('textarea'); + ta.value = value; + document.body.appendChild(ta); + ta.select(); + try { + document.execCommand('copy'); + } + catch { /* noop */ } + document.body.removeChild(ta); + } + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + return (_jsxs("div", { className: "login-field", children: [_jsx("span", { className: "login-field-label", children: label }), _jsx("code", { className: "login-field-value", children: value }), _jsx("button", { className: "login-copy-btn", onClick: copy, title: "\uBCF5\uC0AC", style: { color }, children: copied ? (_jsx("span", { className: "login-copied", children: "\uBCF5\uC0AC\uB428!" })) : (_jsx(SolutionIcon, { slug: "copy", size: 16 })) })] })); +}; +export const LoginBox = ({ access, color, soon }) => { + return (_jsxs("div", { className: "login-box", style: { borderColor: `${color}40`, boxShadow: `0 0 32px ${color}18` }, children: [_jsxs("div", { className: "login-box-head", children: [_jsx("span", { className: "login-box-title", children: "\uC811\uC18D \uC815\uBCF4" }), soon && _jsx("span", { className: "badge badge-soon", children: "\uBC30\uD3EC \uC608\uC815" })] }), !soon && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "login-field", children: [_jsx("span", { className: "login-field-label", children: "URL" }), _jsxs("a", { className: "login-url", href: access.url, target: "_blank", rel: "noreferrer", children: [access.url, _jsx(SolutionIcon, { slug: "external-link", size: 14 })] })] }), access.httpsUrl && (_jsxs("div", { className: "login-field", children: [_jsx("span", { className: "login-field-label", children: "HTTPS" }), _jsxs("a", { className: "login-url", href: access.httpsUrl, target: "_blank", rel: "noreferrer", children: [access.httpsUrl, _jsx(SolutionIcon, { slug: "external-link", size: 14 })] })] })), _jsx(CopyField, { label: "ID", value: access.id, color: color }), _jsx(CopyField, { label: "PW", value: access.pw, color: color })] })), soon && (_jsx("p", { className: "login-soon-text", children: "\uBCF8 \uC194\uB8E8\uC158\uC740 \uD604\uC7AC \uC900\uBE44 \uC911\uC785\uB2C8\uB2E4. \uBC30\uD3EC \uD6C4 \uC811\uC18D \uC815\uBCF4\uAC00 \uC81C\uACF5\uB429\uB2C8\uB2E4." }))] })); +}; +export default LoginBox; diff --git a/src/components/LoginBox.tsx b/src/components/LoginBox.tsx new file mode 100644 index 0000000..070902d --- /dev/null +++ b/src/components/LoginBox.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import { SolutionIcon } from './SolutionIcon'; + +interface LoginBoxProps { + access: { url: string; httpsUrl?: string; id: string; pw: string }; + color: string; + soon?: boolean; +} + +const CopyField: React.FC<{ label: string; value: string; color: string }> = ({ + label, + value, + color, +}) => { + const [copied, setCopied] = useState(false); + const copy = async () => { + try { + await navigator.clipboard.writeText(value); + } catch { + // fallback + const ta = document.createElement('textarea'); + ta.value = value; + document.body.appendChild(ta); + ta.select(); + try { document.execCommand('copy'); } catch { /* noop */ } + document.body.removeChild(ta); + } + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + return ( +
+ {label} + {value} + +
+ ); +}; + +export const LoginBox: React.FC = ({ access, color, soon }) => { + return ( +
+
+ 접속 정보 + {soon && 배포 예정} +
+ + {!soon && ( + <> + + + {access.httpsUrl && ( + + )} + + + + + )} + + {soon && ( +

+ 본 솔루션은 현재 준비 중입니다. 배포 후 접속 정보가 제공됩니다. +

+ )} +
+ ); +}; + +export default LoginBox; diff --git a/src/components/MobileScreenList.js b/src/components/MobileScreenList.js new file mode 100644 index 0000000..1d093d2 --- /dev/null +++ b/src/components/MobileScreenList.js @@ -0,0 +1,14 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { SolutionIcon } from './SolutionIcon'; +const TYPE_LABEL = { + customer: '사용자', + admin: '관리자', + field: '현장', +}; +export const MobileScreenList = ({ screens, color }) => { + if (!screens.length) { + return _jsx("p", { className: "mobile-empty", children: "\uBAA8\uBC14\uC77C \uC571 \uD654\uBA74\uC740 \uC900\uBE44 \uC911\uC785\uB2C8\uB2E4." }); + } + return (_jsx("div", { className: "mobile-screen-list", children: screens.map((s) => (_jsxs("div", { className: "phone-card", children: [_jsxs("div", { className: "phone-frame", style: { borderColor: `${color}55` }, children: [_jsx("div", { className: "phone-notch" }), _jsx("div", { className: "phone-screen", style: { background: `linear-gradient(160deg, ${color}22, #0f172a 70%)` }, children: _jsxs("div", { className: "phone-screen-inner", children: [_jsx("span", { className: "phone-icon", style: { color }, children: _jsx(SolutionIcon, { slug: "mobile", size: 40, color: color }) }), _jsx("span", { className: "phone-screen-name", children: s.name })] }) })] }), _jsxs("div", { className: "phone-label", children: [_jsx("span", { className: "phone-name", children: s.name }), _jsx("span", { className: "phone-type", style: { color, borderColor: `${color}55` }, children: TYPE_LABEL[s.type] || s.type })] })] }, s.name))) })); +}; +export default MobileScreenList; diff --git a/src/components/MobileScreenList.tsx b/src/components/MobileScreenList.tsx new file mode 100644 index 0000000..7120081 --- /dev/null +++ b/src/components/MobileScreenList.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { SolutionIcon } from './SolutionIcon'; + +interface MobileScreenListProps { + screens: { name: string; type: string }[]; + color: string; +} + +const TYPE_LABEL: Record = { + customer: '사용자', + admin: '관리자', + field: '현장', +}; + +export const MobileScreenList: React.FC = ({ screens, color }) => { + if (!screens.length) { + return

모바일 앱 화면은 준비 중입니다.

; + } + return ( +
+ {screens.map((s) => ( +
+
+
+
+
+ + + + {s.name} +
+
+
+
+ {s.name} + + {TYPE_LABEL[s.type] || s.type} + +
+
+ ))} +
+ ); +}; + +export default MobileScreenList; diff --git a/src/components/Navbar.js b/src/components/Navbar.js new file mode 100644 index 0000000..a0f6873 --- /dev/null +++ b/src/components/Navbar.js @@ -0,0 +1,6 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { Link } from 'react-router-dom'; +export const Navbar = () => { + return (_jsx("header", { className: "navbar", children: _jsxs("div", { className: "container navbar-inner", children: [_jsxs(Link, { to: "/", className: "navbar-logo", children: [_jsx("span", { className: "navbar-logo-mark", children: "G" }), _jsx("span", { className: "navbar-logo-text", children: "GUARDiA Solutions" })] }), _jsxs("nav", { className: "navbar-nav", children: [_jsx(Link, { to: "/catalog", className: "navbar-link", children: "\uC804\uCCB4 \uC194\uB8E8\uC158" }), _jsx("a", { className: "navbar-cta", href: "https://zioinfo.co.kr:8443", target: "_blank", rel: "noreferrer", children: "ITSM \uBC14\uB85C\uAC00\uAE30" })] })] }) })); +}; +export default Navbar; diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx new file mode 100644 index 0000000..136b140 --- /dev/null +++ b/src/components/Navbar.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +export const Navbar: React.FC = () => { + return ( +
+
+ + G + GUARDiA Solutions + + +
+
+ ); +}; + +export default Navbar; diff --git a/src/components/SolutionCard.js b/src/components/SolutionCard.js new file mode 100644 index 0000000..9466c56 --- /dev/null +++ b/src/components/SolutionCard.js @@ -0,0 +1,7 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { SolutionIcon } from './SolutionIcon'; +export const SolutionCard = ({ solution, onClick }) => { + const { slug, name, tagline, color, status, port } = solution; + return (_jsxs("button", { className: "solution-card", onClick: onClick, style: { ['--card-color']: color }, children: [_jsxs("div", { className: "solution-card-top", children: [_jsx("span", { className: "solution-card-icon", style: { color, background: `${color}1a`, borderColor: `${color}40` }, children: _jsx(SolutionIcon, { slug: slug, size: 28, color: color }) }), status === 'live' ? (_jsx("span", { className: "badge badge-live", children: "LIVE" })) : (_jsx("span", { className: "badge badge-soon", children: "SOON" }))] }), _jsx("h3", { className: "solution-card-name", children: name }), _jsx("p", { className: "solution-card-tagline", children: tagline }), _jsxs("div", { className: "solution-card-foot", children: [_jsxs("span", { className: "solution-card-port", children: [":", port] }), _jsxs("span", { className: "solution-card-more", style: { color }, children: ["\uC0C1\uC138 \uBCF4\uAE30", _jsx(SolutionIcon, { slug: "chevron-right", size: 14, color: color })] })] })] })); +}; +export default SolutionCard; diff --git a/src/components/SolutionCard.tsx b/src/components/SolutionCard.tsx new file mode 100644 index 0000000..f25c12f --- /dev/null +++ b/src/components/SolutionCard.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import type { Solution } from '../data/solutions'; +import { SolutionIcon } from './SolutionIcon'; + +interface SolutionCardProps { + solution: Solution; + onClick: () => void; +} + +export const SolutionCard: React.FC = ({ solution, onClick }) => { + const { slug, name, tagline, color, status, port } = solution; + return ( + + ); +}; + +export default SolutionCard; diff --git a/src/components/SolutionIcon.js b/src/components/SolutionIcon.js new file mode 100644 index 0000000..3d9f2dc --- /dev/null +++ b/src/components/SolutionIcon.js @@ -0,0 +1,31 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +const ICON_PATHS = { + itsm: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "8", y: "2", width: "8", height: "4", rx: "1" }), _jsx("path", { d: "M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" }), _jsx("path", { d: "m9 12 2 2 4-4" })] })), + erp: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M3 21h18" }), _jsx("path", { d: "M5 21V7l7-4 7 4v14" }), _jsx("path", { d: "M9 21v-4h6v4" }), _jsx("circle", { cx: "16.5", cy: "10.5", r: "1.5" }), _jsx("path", { d: "M14.5 10.5c0-.83.34-1.58.88-2.12" })] })), + crm: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "9", cy: "7", r: "3" }), _jsx("path", { d: "M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2" }), _jsx("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" }), _jsx("path", { d: "M21 21v-2a4 4 0 0 0-3-3.85" })] })), + ocr: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("polyline", { points: "14,2 14,8 20,8" }), _jsx("line", { x1: "4", y1: "13", x2: "20", y2: "13" }), _jsx("line", { x1: "8", y1: "10", x2: "8", y2: "16" })] })), + bi: (_jsxs(_Fragment, { children: [_jsx("line", { x1: "18", y1: "20", x2: "18", y2: "10" }), _jsx("line", { x1: "12", y1: "20", x2: "12", y2: "4" }), _jsx("line", { x1: "6", y1: "20", x2: "6", y2: "14" }), _jsx("line", { x1: "2", y1: "20", x2: "22", y2: "20" }), _jsx("polyline", { points: "2,10 7,7 12,9 17,5 22,8" })] })), + pms: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "2", y: "3", width: "20", height: "18", rx: "2" }), _jsx("line", { x1: "8", y1: "3", x2: "8", y2: "21" }), _jsx("line", { x1: "16", y1: "3", x2: "16", y2: "21" }), _jsx("rect", { x: "3", y: "6", width: "4", height: "3", rx: "1" }), _jsx("rect", { x: "3", y: "12", width: "4", height: "3", rx: "1" }), _jsx("rect", { x: "9", y: "6", width: "6", height: "3", rx: "1" }), _jsx("rect", { x: "17", y: "6", width: "4", height: "3", rx: "1" })] })), + rpa: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "4", y: "6", width: "16", height: "12", rx: "3" }), _jsx("circle", { cx: "9", cy: "11", r: "1.5" }), _jsx("circle", { cx: "15", cy: "11", r: "1.5" }), _jsx("path", { d: "M9 15h6" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "6" }), _jsx("circle", { cx: "12", cy: "2.5", r: "0.5" }), _jsx("line", { x1: "4", y1: "11", x2: "2", y2: "11" }), _jsx("line", { x1: "20", y1: "11", x2: "22", y2: "11" })] })), + groupware: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "5", r: "2" }), _jsx("circle", { cx: "19", cy: "12", r: "2" }), _jsx("circle", { cx: "12", cy: "19", r: "2" }), _jsx("circle", { cx: "5", cy: "12", r: "2" }), _jsx("line", { x1: "12", y1: "7", x2: "17.5", y2: "10.5" }), _jsx("line", { x1: "17.5", y1: "13.5", x2: "12", y2: "17" }), _jsx("line", { x1: "12", y1: "17", x2: "6.5", y2: "13.5" }), _jsx("line", { x1: "6.5", y1: "10.5", x2: "12", y2: "7" })] })), + portal: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "7", y: "2", width: "10", height: "20", rx: "2" }), _jsx("path", { d: "M12 10v4" }), _jsx("circle", { cx: "12", cy: "12", r: "1" }), _jsx("path", { d: "M4 6C2.5 7.5 2 9.5 2 12s.5 4.5 2 6" }), _jsx("path", { d: "M20 6c1.5 1.5 2 3.5 2 6s-.5 4.5-2 6" })] })), + mall: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z" }), _jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("path", { d: "M12 10c-1 0-2 .5-2 2s1 2 2 2 2-.5 2-2-1-2-2-2z" }), _jsx("path", { d: "M12 8v1M10.3 9.3l.7.7M14 10l-.7.7" })] })), + cms: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "9", y1: "9", x2: "9", y2: "21" })] })), + mes: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M3 21h18" }), _jsx("path", { d: "M3 21V11l5-3v3l5-3v3l5-3v10" }), _jsx("rect", { x: "7", y: "14", width: "3", height: "7" }), _jsx("rect", { x: "14", y: "14", width: "3", height: "7" }), _jsx("line", { x1: "18", y1: "5", x2: "18", y2: "8" }), _jsx("path", { d: "M16 5h4" })] })), + hrm: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "9", cy: "7", r: "4" }), _jsx("path", { d: "M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2" }), _jsx("polyline", { points: "16,8 19,5 22,8" }), _jsx("line", { x1: "19", y1: "5", x2: "19", y2: "14" })] })), + esn: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), _jsx("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" }), _jsx("path", { d: "M15 5c1 1.5 1.5 3 1.5 5s-.5 3.5-1.5 5" }), _jsx("path", { d: "M18 3c1.5 2 2.5 4.5 2.5 7s-1 5-2.5 7" })] })), + fa: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "2", y: "14", width: "4", height: "6", rx: "1" }), _jsx("path", { d: "M6 17h3l3-6h5" }), _jsx("path", { d: "M17 11V7a2 2 0 0 0-2-2h-2" }), _jsx("circle", { cx: "13", cy: "4", r: "2" }), _jsx("path", { d: "M20 9l2 2-2 2" }), _jsx("line", { x1: "17", y1: "11", x2: "22", y2: "11" })] })), + // UI icons + copy: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] })), + 'external-link': (_jsxs(_Fragment, { children: [_jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), _jsx("polyline", { points: "15,3 21,3 21,9" }), _jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] })), + mobile: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "5", y: "2", width: "14", height: "20", rx: "2" }), _jsx("line", { x1: "12", y1: "18", x2: "12.01", y2: "18" })] })), + check: _jsx("polyline", { points: "20,6 9,17 4,12" }), + 'chevron-right': _jsx("polyline", { points: "9,18 15,12 9,6" }), +}; +export const SolutionIcon = ({ slug, size = 24, color }) => { + const paths = ICON_PATHS[slug]; + if (!paths) + return null; + return (_jsx("svg", { viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: color || 'currentColor', strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: paths })); +}; +export default SolutionIcon; diff --git a/src/components/SolutionIcon.tsx b/src/components/SolutionIcon.tsx new file mode 100644 index 0000000..785c908 --- /dev/null +++ b/src/components/SolutionIcon.tsx @@ -0,0 +1,193 @@ +import React from 'react'; + +interface SolutionIconProps { + slug: string; + size?: number; + color?: string; +} + +type Paths = React.ReactNode; + +const ICON_PATHS: Record = { + itsm: ( + <> + + + + + ), + erp: ( + <> + + + + + + + ), + crm: ( + <> + + + + + + ), + ocr: ( + <> + + + + + + ), + bi: ( + <> + + + + + + + ), + pms: ( + <> + + + + + + + + + ), + rpa: ( + <> + + + + + + + + + + ), + groupware: ( + <> + + + + + + + + + + ), + portal: ( + <> + + + + + + + ), + mall: ( + <> + + + + + + ), + cms: ( + <> + + + + + + ), + mes: ( + <> + + + + + + + + ), + hrm: ( + <> + + + + + + ), + esn: ( + <> + + + + + + ), + fa: ( + <> + + + + + + + + ), + // UI icons + copy: ( + <> + + + + ), + 'external-link': ( + <> + + + + + ), + mobile: ( + <> + + + + ), + check: , + 'chevron-right': , +}; + +export const SolutionIcon: React.FC = ({ slug, size = 24, color }) => { + const paths = ICON_PATHS[slug]; + if (!paths) return null; + return ( + + ); +}; + +export default SolutionIcon; diff --git a/src/components/StepGuide.js b/src/components/StepGuide.js new file mode 100644 index 0000000..f7bab97 --- /dev/null +++ b/src/components/StepGuide.js @@ -0,0 +1,5 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +export const StepGuide = ({ steps, color }) => { + return (_jsx("ol", { className: "step-guide", children: steps.map((s, i) => (_jsxs("li", { className: "step-guide-item", children: [_jsxs("div", { className: "step-guide-rail", children: [_jsx("span", { className: "step-guide-num", style: { background: color }, children: s.num }), i < steps.length - 1 && _jsx("span", { className: "step-guide-line" })] }), _jsxs("div", { className: "step-guide-body", children: [_jsx("h4", { children: s.title }), _jsx("p", { children: s.desc })] })] }, s.num))) })); +}; +export default StepGuide; diff --git a/src/components/StepGuide.tsx b/src/components/StepGuide.tsx new file mode 100644 index 0000000..39dd539 --- /dev/null +++ b/src/components/StepGuide.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +interface StepGuideProps { + steps: { num: number; title: string; desc: string }[]; + color: string; +} + +export const StepGuide: React.FC = ({ steps, color }) => { + return ( +
    + {steps.map((s, i) => ( +
  1. +
    + + {s.num} + + {i < steps.length - 1 && } +
    +
    +

    {s.title}

    +

    {s.desc}

    +
    +
  2. + ))} +
+ ); +}; + +export default StepGuide; diff --git a/src/data/solutions.js b/src/data/solutions.js new file mode 100644 index 0000000..e7cc114 --- /dev/null +++ b/src/data/solutions.js @@ -0,0 +1,521 @@ +const IP = '101.79.17.164'; +const url = (port) => `http://${IP}:${port}`; +export const SOLUTIONS = [ + { + id: 'itsm', + slug: 'itsm', + name: 'GUARDiA ITSM', + tagline: 'AI 기반 레거시 인프라 자율 운영 플랫폼', + category: '운영관리', + port: 9001, + color: '#6366f1', + status: 'live', + access: { url: url(9001), httpsUrl: 'https://zioinfo.co.kr:8443', id: 'admin', pw: '1111' }, + features: [ + { title: 'SR 자동화', desc: 'SR(서비스요청) 접수·처리·완료 전 과정 자동화' }, + { title: 'CMDB 자산관리', desc: 'CMDB 서버 자산 등록·조회·의존성 관리' }, + { title: 'AI 자연어 명령', desc: '`/배포 서버A 앱B` 자연어 명령으로 운영 자동화' }, + { title: '인시던트·변경관리', desc: '인시던트 대응 및 CAB 승인 변경관리' }, + { title: 'SLA 모니터링', desc: 'SLA 실시간 모니터링 및 위반 알림' }, + { title: '배포 자동화', desc: 'SSH/SFTP 에이전트리스 배포 자동화' }, + { title: '보안 점검', desc: 'CSAP·ISMS 보안 점검 자동화' }, + { title: '메신저 봇 연동', desc: '`/sr`, `/status` 메신저 봇 명령어 연동' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'https://zioinfo.co.kr:8443 접속 → admin / 1111 로그인' }, + { num: 2, title: '대시보드 확인', desc: '오늘 SR 수, 처리율, SLA 현황 확인' }, + { num: 3, title: 'SR 접수', desc: '[새 요청] → 제목 입력 → 자동 분류 확인' }, + { num: 4, title: 'AI 명령', desc: '메신저 탭 → `/sr 서버 장애 발생` 입력' }, + { num: 5, title: '배포', desc: '[배포관리] → [새 배포] → 서버 선택 → 실행' }, + { num: 6, title: 'CMDB', desc: '[자산관리] → 서버 목록 확인' }, + ], + mobileScreens: [ + { name: 'SR 목록', type: 'customer' }, + { name: 'SR 상세', type: 'customer' }, + { name: '빠른 SR 등록', type: 'customer' }, + { name: '승인 요청', type: 'admin' }, + { name: '서버 현황', type: 'admin' }, + { name: 'AI 챗봇', type: 'customer' }, + ], + }, + { + id: 'erp', + slug: 'erp', + name: 'GUARDiA ERP', + tagline: 'AI 기반 스마트 엔터프라이즈 자원관리', + category: '운영관리', + port: 8003, + color: '#10b981', + status: 'live', + access: { url: url(8003), id: 'admin', pw: 'admin123' }, + features: [ + { title: '전표 자동 분류', desc: 'AI 기반 전표 자동 분류 및 계정 매핑' }, + { title: '예산·KPI', desc: '예산 관리 및 KPI 대시보드' }, + { title: '구매 발주', desc: '구매 발주 · 입고 처리 워크플로우' }, + { title: '인사·급여 연동', desc: 'HRM 인사 · 급여 시스템 연동' }, + { title: '영업·매출 분석', desc: '영업 활동 및 매출 분석' }, + { title: 'FinOps', desc: 'FinOps 클라우드 비용 추적' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8003 접속 → admin / admin123 로그인' }, + { num: 2, title: '대시보드', desc: '매출·비용·KPI 현황 확인' }, + { num: 3, title: '재무', desc: '[전표입력] → 항목 입력 → [AI 분류] 클릭 → 계정 자동 입력 확인' }, + { num: 4, title: '예산', desc: '[예산현황] → 부서별 집행률 확인' }, + { num: 5, title: '구매', desc: '[발주서] → [신규] → 품목 입력 → 공급업체 선택 → 승인 요청' }, + ], + mobileScreens: [ + { name: 'KPI 대시보드', type: 'admin' }, + { name: '전표 조회', type: 'admin' }, + { name: '예산 현황', type: 'admin' }, + { name: '구매 승인', type: 'admin' }, + { name: '영업 매출', type: 'admin' }, + ], + }, + { + id: 'crm', + slug: 'crm', + name: 'GUARDiA CRM', + tagline: 'AI 기반 공공기관 고객관계관리', + category: '운영관리', + port: 8004, + color: '#f59e0b', + status: 'live', + access: { url: url(8004), id: 'admin', pw: 'admin123' }, + features: [ + { title: '고객 관리', desc: '고객(기관) 등록 · 히스토리 관리' }, + { title: '영업 파이프라인', desc: '영업기회 파이프라인 단계별 관리' }, + { title: '계약·입찰 추적', desc: '나라장터 G2B 연동 계약 · 입찰 추적' }, + { title: '캠페인 관리', desc: '마케팅 캠페인 기획·실행·분석' }, + { title: 'AI 고객 인사이트', desc: 'AI 기반 고객 행동 인사이트' }, + { title: 'ITSM SR 연동', desc: '고객 문의 → SR 자동 생성' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8004 접속 → admin / admin123 로그인' }, + { num: 2, title: '대시보드', desc: '영업 파이프라인 현황 확인' }, + { num: 3, title: '고객', desc: '[고객등록] → 기관명·담당자·연락처 입력' }, + { num: 4, title: '영업기회', desc: '[새 기회] → 고객 연결 → 단계 설정 (발굴→제안→협상→계약)' }, + { num: 5, title: '계약', desc: '[계약등록] → 나라장터 계약번호 입력 → OCR로 계약서 자동 파싱' }, + ], + mobileScreens: [ + { name: '고객 목록', type: 'admin' }, + { name: '영업기회', type: 'admin' }, + { name: '계약 현황', type: 'admin' }, + { name: '입찰 알림', type: 'admin' }, + { name: 'AI 분석', type: 'admin' }, + ], + }, + { + id: 'ocr', + slug: 'ocr', + name: 'GUARDiA OCR', + tagline: 'AI 기반 지능형 문서 인식 플랫폼', + category: 'AI문서', + port: 8005, + color: '#8b5cf6', + status: 'live', + access: { url: url(8005), id: 'admin', pw: 'admin123' }, + features: [ + { title: '계약서 등록', desc: '계약서 → 나라장터 계약 자동 등록' }, + { title: '납품서 처리', desc: '납품서 → CMDB 서버 자동 등록' }, + { title: '청구서 처리', desc: '청구서 → 세금계산서 처리' }, + { title: '장애보고서 → SR', desc: '장애보고서 이미지 → SR 자동 생성' }, + { title: '회의록 → SR', desc: '회의록 → 액션아이템 SR 생성' }, + { title: '감사보고서', desc: '감사보고서 → CSAP 준수율 업데이트' }, + { title: '브랜드 계약서', desc: '현대백화점 등 브랜드 계약서 → 계약 이력' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8005 접속 → admin / admin123 로그인' }, + { num: 2, title: '문서 업로드', desc: '[문서 업로드] → 문서 이미지/PDF 선택' }, + { num: 3, title: '워크플로우 선택', desc: '워크플로우 선택 → 장애보고서→SR 선택' }, + { num: 4, title: 'OCR 실행', desc: '[OCR 실행] 클릭 → AI 텍스트 추출 확인' }, + { num: 5, title: 'SR 생성', desc: '추출 결과 확인 → [SR 자동 생성] 클릭 → ITSM SR 생성 확인' }, + ], + mobileScreens: [ + { name: '카메라 촬영', type: 'field' }, + { name: '문서 업로드', type: 'field' }, + { name: 'OCR 결과', type: 'customer' }, + { name: '워크플로우', type: 'customer' }, + { name: '처리 이력', type: 'customer' }, + ], + }, + { + id: 'bi', + slug: 'bi', + name: 'GUARDiA BI', + tagline: 'AI 기반 비즈니스 인텔리전스 플랫폼', + category: 'AI문서', + port: 8006, + color: '#06b6d4', + status: 'live', + access: { url: url(8006), id: 'admin', pw: 'admin123' }, + features: [ + { title: '데이터소스 연결', desc: 'PostgreSQL · API · CSV 데이터소스 연결' }, + { title: '대시보드 빌더', desc: '드래그앤드롭 대시보드 빌더' }, + { title: 'KPI 위젯·알림', desc: 'KPI 위젯 및 실시간 알림' }, + { title: 'AI Text-to-SQL', desc: '자연어 → SQL 쿼리 자동 생성' }, + { title: '예측·이상탐지', desc: 'AI 예측 및 이상탐지 분석' }, + { title: 'ITSM 데이터 피드', desc: 'SR·SLA·배포 통계 데이터 피드' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8006 접속 → admin / admin123 로그인' }, + { num: 2, title: '데이터소스', desc: '[추가] → PostgreSQL 선택 → ITSM DB 연결' }, + { num: 3, title: '대시보드', desc: '[새 대시보드] → 위젯 추가 → 막대/라인/KPI 선택' }, + { num: 4, title: 'AI 쿼리', desc: '검색창에 "이번 달 미처리 SR 현황" 입력 → SQL 자동 생성 확인' }, + { num: 5, title: '리포트', desc: '[내보내기] → PDF 저장' }, + ], + mobileScreens: [ + { name: 'KPI 대시보드', type: 'admin' }, + { name: '차트 조회', type: 'admin' }, + { name: 'AI 인사이트', type: 'admin' }, + { name: '리포트', type: 'admin' }, + { name: '알림', type: 'admin' }, + ], + }, + { + id: 'pms', + slug: 'pms', + name: 'GUARDiA PMS', + tagline: 'JIRA 호환 AI 프로젝트 관리 시스템', + category: '프로젝트', + port: 8007, + color: '#f97316', + status: 'live', + access: { url: url(8007), id: 'admin', pw: 'admin123' }, + features: [ + { title: '이슈 추적', desc: '이슈 생성·추적 (JIRA 호환)' }, + { title: 'Scrum·Kanban', desc: 'Scrum · Kanban 보드' }, + { title: 'WBS·마일스톤', desc: 'WBS · 마일스톤 관리' }, + { title: '전자결재', desc: '다단계 결재선 전자결재' }, + { title: 'SI 산출물', desc: 'SI 산출물 관리 (감리/준공)' }, + { title: '요구사항 추적', desc: '요구사항 추적 (RTM)' }, + { title: 'AI 회의록', desc: 'AI 회의록 자동 작성' }, + { title: 'RFP 분석', desc: 'RFP → 요구사항분석서 자동 생성' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8007 접속 → admin / admin123 로그인' }, + { num: 2, title: '프로젝트', desc: '[새 프로젝트] → 이름·유형(Scrum) 선택' }, + { num: 3, title: '백로그', desc: '[이슈 추가] → 제목·담당자·우선순위 입력' }, + { num: 4, title: '보드', desc: 'Kanban 뷰 전환 → 이슈 드래그앤드롭으로 상태 변경' }, + { num: 5, title: 'AI 기능', desc: '[회의록] → 텍스트 붙여넣기 → [액션아이템 추출] 클릭' }, + ], + mobileScreens: [ + { name: '내 이슈', type: 'customer' }, + { name: '보드', type: 'customer' }, + { name: '스프린트', type: 'customer' }, + { name: '승인 요청', type: 'admin' }, + { name: 'WBS 조회', type: 'admin' }, + ], + }, + { + id: 'rpa', + slug: 'rpa', + name: 'GUARDiA RPA', + tagline: 'AI 기반 노코드 업무 자동화 플랫폼', + category: '프로젝트', + port: 8008, + color: '#ec4899', + status: 'live', + access: { url: url(8008), id: 'admin', pw: 'admin123' }, + features: [ + { title: '노코드 워크플로우', desc: '노코드 워크플로우 빌더' }, + { title: '봇 스케줄링', desc: '봇 등록 · 스케줄링' }, + { title: '자연어 생성', desc: '자연어 → 워크플로우 자동 생성' }, + { title: 'ROI 분석', desc: 'ROI 분석 대시보드' }, + { title: 'ITSM 연동', desc: 'ITSM SR 자동 처리 연동' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8008 접속 → admin / admin123 로그인' }, + { num: 2, title: '워크플로우', desc: '[새 워크플로우] → 노드 드래그앤드롭' }, + { num: 3, title: '트리거 설정', desc: '[스케줄] 선택 → 매일 09:00 설정' }, + { num: 4, title: '액션 추가', desc: '[ITSM SR 생성] 노드 추가 → 파라미터 입력' }, + { num: 5, title: 'AI 생성', desc: '입력창에 "매일 아침 서버 상태 체크 후 이상 시 SR 생성" → [생성] 클릭' }, + ], + mobileScreens: [ + { name: '봇 현황', type: 'admin' }, + { name: '워크플로우 목록', type: 'admin' }, + { name: '실행 이력', type: 'admin' }, + { name: 'ROI 현황', type: 'admin' }, + { name: '알림', type: 'admin' }, + ], + }, + { + id: 'groupware', + slug: 'groupware', + name: 'GUARDiA Groupware', + tagline: 'AI 기반 통합 협업 그룹웨어', + category: '협업', + port: 8009, + color: '#14b8a6', + status: 'live', + access: { url: url(8009), id: 'admin', pw: 'admin123' }, + features: [ + { title: '전자결재', desc: '다단계 결재선 · 대리결재' }, + { title: '게시판', desc: '공지 · 자유 · 팀별 게시판' }, + { title: '일정/캘린더', desc: '팀 공유 일정/캘린더' }, + { title: '근태/휴가', desc: '근태/휴가 신청·승인' }, + { title: '자원예약', desc: '회의실 · 장비 자원예약' }, + { title: '주소록·조직도', desc: '주소록 · 조직도 관리' }, + { title: '문서함', desc: '버전관리 문서함' }, + { title: '쪽지', desc: '내부 메시지 쪽지' }, + { title: 'AI 문서 요약', desc: 'AI 문서 자동 요약' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8009 접속 → admin / admin123 로그인' }, + { num: 2, title: '전자결재', desc: '[기안] → 양식 선택 → 내용 작성 → 결재선 설정 → 상신' }, + { num: 3, title: '일정', desc: '[일정등록] → 제목·날짜·참석자 입력 → 팀 공유' }, + { num: 4, title: '근태', desc: '[휴가신청] → 날짜·종류 선택 → 결재 요청' }, + { num: 5, title: '회의실', desc: '[예약] → 날짜·시간·회의실 선택 → 확정' }, + ], + mobileScreens: [ + { name: '결재함', type: 'admin' }, + { name: '공지사항', type: 'customer' }, + { name: '일정', type: 'customer' }, + { name: '근태', type: 'customer' }, + { name: '주소록', type: 'customer' }, + ], + }, + { + id: 'portal', + slug: 'portal', + name: 'GUARDiA Portal', + tagline: 'SSO 단일 로그인 통합 업무 포털', + category: '협업', + port: 8010, + color: '#7c3aed', + status: 'live', + access: { url: url(8010), id: 'admin', pw: 'admin123' }, + features: [ + { title: 'SSO 단일 로그인', desc: 'GUARDiA 전 시스템 SSO 바로가기' }, + { title: '위젯 대시보드', desc: '역할별 위젯 대시보드' }, + { title: '즐겨찾기', desc: '즐겨찾기 시스템' }, + { title: '통합 공지', desc: '통합 공지사항' }, + { title: '알림 센터', desc: '전 시스템 통합 알림 센터' }, + { title: '통합 검색', desc: '전 시스템 통합 검색' }, + { title: '마이페이지', desc: '프로필 · 설정 마이페이지' }, + { title: 'AI 공지 요약', desc: 'AI 공지 자동 요약' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8010 접속 → admin / admin123 로그인' }, + { num: 2, title: '포털 메인', desc: '9개 시스템 바로가기 카드 확인' }, + { num: 3, title: '위젯', desc: '[위젯 추가] → SR현황·KPI·일정 선택 → 대시보드 배치' }, + { num: 4, title: '통합검색', desc: '검색창에 "장애 처리" 입력 → ITSM·KB·문서 통합 결과 확인' }, + { num: 5, title: '알림센터', desc: '전 시스템 알림 통합 확인' }, + ], + mobileScreens: [ + { name: '포털 홈', type: 'customer' }, + { name: '알림', type: 'customer' }, + { name: '바로가기', type: 'customer' }, + { name: '통합검색', type: 'customer' }, + { name: '마이페이지', type: 'customer' }, + ], + }, + { + id: 'mall', + slug: 'mall', + name: 'GUARDiA Mall', + tagline: '미국 다지점 꽃집 옴니채널 e-커머스', + category: '커머스', + port: 8011, + color: '#f43f5e', + status: 'live', + access: { url: url(8011), id: 'admin', pw: 'admin123' }, + features: [ + { title: 'ZIP 배송권역', desc: '다매장 ZIP 배송권역 관리' }, + { title: '타임슬롯', desc: '타임슬롯 스케줄링 (당일배송)' }, + { title: '구독 서비스', desc: '정기 구독 서비스' }, + { title: '게이트웨이 어댑터', desc: 'Stripe · Twilio mock 게이트웨이 어댑터' }, + { title: 'AI 추천·예측', desc: 'AI 상품 추천 · 수요예측' }, + { title: '매장간 재고 이양', desc: '매장간 재고 이양' }, + { title: '관리자 앱', desc: '매장 매니저용 관리자 앱' }, + { title: 'i18n 한/영', desc: '한국어/영어 다국어 지원' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8011 접속 → admin / admin123 로그인' }, + { num: 2, title: '상품 확인', desc: '상품 목록 확인 (Montvale Florist 실제 상품)' }, + { num: 3, title: '주문', desc: '상품 선택 → ZIP 코드 입력 → 배송 슬롯 선택 → 장바구니 추가' }, + { num: 4, title: '결제', desc: '카드 정보 (mock: 4242-4242-4242-4242) → 주문 완료' }, + { num: 5, title: '관리자', desc: '[주문관리] → 주문 확인 → 상태 변경 (준비중→배송중→완료)' }, + ], + mobileScreens: [ + { name: '상품 목록', type: 'customer' }, + { name: '상품 상세', type: 'customer' }, + { name: '장바구니', type: 'customer' }, + { name: '결제', type: 'customer' }, + { name: '주문 칸반', type: 'admin' }, + { name: '재고 ON/OFF', type: 'admin' }, + ], + }, + { + id: 'cms', + slug: 'cms', + name: 'GUARDiA CMS', + tagline: 'AI 기반 통합 콘텐츠 관리 시스템', + category: '커머스', + port: 8012, + color: '#0ea5e9', + status: 'live', + access: { url: url(8012), id: 'admin', pw: 'admin123' }, + features: [ + { title: '헤드리스 CMS', desc: '페이지 · 포스트 · 블록 헤드리스 CMS' }, + { title: '게시 워크플로우', desc: '버전관리 · 게시 워크플로우 (draft→published)' }, + { title: '미디어 라이브러리', desc: '미디어 라이브러리 관리' }, + { title: '배너·프로모션', desc: '배너 · 프로모션 관리' }, + { title: 'AI SEO 제안', desc: 'AI 기반 SEO 자동 제안' }, + { title: '다국어·테마', desc: '다국어 · 테마 관리' }, + { title: 'UGC 모더레이션', desc: 'UGC 콘텐츠 모더레이션' }, + { title: '폼 빌더', desc: '드래그앤드롭 폼 빌더' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8012 접속 → admin / admin123 로그인' }, + { num: 2, title: '페이지', desc: '[새 페이지] → 제목 입력 → 블록 에디터에서 콘텐츠 추가' }, + { num: 3, title: '미디어', desc: '[업로드] → 이미지 선택 → AI 자동 태그 확인' }, + { num: 4, title: '워크플로우', desc: '페이지 상태를 draft → review → published 변경' }, + { num: 5, title: 'SEO', desc: '페이지 선택 → [AI SEO 제안] → 메타태그 자동 입력 확인' }, + ], + mobileScreens: [ + { name: '콘텐츠 목록', type: 'admin' }, + { name: '콘텐츠 승인', type: 'admin' }, + { name: '미디어 촬영업로드', type: 'field' }, + { name: 'UGC 모더레이션', type: 'admin' }, + { name: '성과', type: 'admin' }, + ], + }, + { + id: 'mes', + slug: 'mes', + name: 'GUARDiA MES', + tagline: 'AI 기반 제조실행시스템 (WMS+MES+QMS)', + category: '제조', + port: 8013, + color: '#84cc16', + status: 'live', + access: { url: url(8013), id: 'admin', pw: 'admin123' }, + features: [ + { title: '작업지시·실적', desc: '작업지시 · 생산실적 등록' }, + { title: 'BOM·라우팅', desc: 'BOM · 공정/라우팅 관리' }, + { title: '설비 OEE', desc: '설비 OEE 모니터링' }, + { title: 'LOT 추적', desc: 'LOT 추적' }, + { title: '창고관리', desc: '입고·출고·재고·피킹 창고관리' }, + { title: '검사', desc: '수입·공정·출하 검사' }, + { title: 'SPC 관리도', desc: 'SPC 관리도 (Cp/Cpk)' }, + { title: 'CAPA', desc: 'CAPA (시정조치)' }, + { title: 'AI 불량분석', desc: 'AI 불량원인 분석' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8013 접속 → admin / admin123 로그인' }, + { num: 2, title: '작업지시', desc: '[신규] → 품목·수량·공정 입력 → 발행' }, + { num: 3, title: '생산실적', desc: '작업지시 선택 → 생산량·불량량 입력 → 등록' }, + { num: 4, title: '검사', desc: '[공정검사] → LOT 번호 입력 → 검사항목 체크 → 합격/불합격' }, + { num: 5, title: 'SPC', desc: '관리도 차트 확인 → Cp/Cpk 수치 확인 → 이상 점 알림' }, + ], + mobileScreens: [ + { name: '작업지시 조회', type: 'field' }, + { name: '실적입력', type: 'field' }, + { name: '바코드스캔', type: 'field' }, + { name: '검사입력', type: 'field' }, + { name: '설비점검', type: 'field' }, + ], + }, + { + id: 'hrm', + slug: 'hrm', + name: 'GUARDiA HRM', + tagline: 'AI 기반 인사관리 플랫폼', + category: '운영관리', + port: 8014, + color: '#a855f7', + status: 'live', + access: { url: url(8014), id: 'admin', pw: 'admin123' }, + features: [ + { title: '사원 관리', desc: '사원 등록 · 이력관리' }, + { title: '조직도·직급', desc: '조직도 · 직급체계' }, + { title: '급여·명세서', desc: '급여 계산 · 명세서' }, + { title: '근태·휴가', desc: '근태 · 휴가 관리' }, + { title: '성과 평가', desc: '성과 평가 · KPI' }, + { title: '채용 관리', desc: '채용 공고 · 지원자 관리' }, + { title: '교육 관리', desc: '교육 이수 관리' }, + { title: 'AI 인사 분석', desc: 'AI 인사 분석' }, + { title: 'Groupware 연동', desc: 'Groupware 연동' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8014 접속 → admin / admin123 로그인' }, + { num: 2, title: '사원', desc: '[신규 등록] → 기본정보·부서·직급 입력 → 저장' }, + { num: 3, title: '급여', desc: '사원 선택 → 월급여 입력 → [명세서 생성] → PDF 확인' }, + { num: 4, title: '성과', desc: '[평가 등록] → KPI 항목 입력 → 점수 부여' }, + { num: 5, title: 'AI', desc: '[인사 인사이트] → 이탈 위험도·성과 트렌드 확인' }, + ], + mobileScreens: [ + { name: '사원증', type: 'customer' }, + { name: '근태 체크인', type: 'customer' }, + { name: '급여 명세서', type: 'customer' }, + { name: '휴가신청', type: 'customer' }, + { name: '성과현황', type: 'customer' }, + ], + }, + { + id: 'esn', + slug: 'esn', + name: 'GUARDiA ESN', + tagline: 'ESL 전자가격표 통합 관리 플랫폼', + category: '커머스', + port: 8015, + color: '#f59e0b', + status: 'live', + access: { url: url(8015), id: 'admin', pw: 'admin123' }, + features: [ + { title: '멀티테넌트', desc: 'LGInnotek · LGIT · Emart · ZIOINFO 멀티테넌트' }, + { title: 'ESL 기기 관리', desc: 'ESL 기기 등록 · 상태 모니터링' }, + { title: 'POS 자동 변환', desc: 'POS 데이터 → ESL 가격 자동 변환' }, + { title: '템플릿 관리', desc: '크기별 레이아웃 템플릿 관리' }, + { title: 'HCore 관제', desc: 'HCore 게이트웨이/허브 관제' }, + { title: '알람·장애 이력', desc: '알람 · 장애 이력 관리' }, + { title: '펌웨어 업데이트', desc: '펌웨어 업데이트 관리' }, + { title: 'AI 알람 분석', desc: 'Ollama AI 알람 원인 분석' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8015 접속 → admin / admin123 로그인' }, + { num: 2, title: '테넌트 선택', desc: '드롭다운에서 LGINNOTEK 선택' }, + { num: 3, title: 'ESL 기기', desc: '기기 목록 확인 → 상태(온라인/오프라인) 확인' }, + { num: 4, title: 'POS 변환', desc: '[POS 데이터 업로드] → CSV 파일 선택 → 변환 실행' }, + { num: 5, title: '알람', desc: '알람 목록 → [AI 원인분석] 클릭 → Ollama 분석 결과 확인' }, + ], + mobileScreens: [ + { name: 'ESL 기기 목록', type: 'field' }, + { name: '알람', type: 'field' }, + { name: 'HCore 상태', type: 'field' }, + { name: '펌웨어', type: 'admin' }, + { name: '작업 이력', type: 'field' }, + ], + }, + { + id: 'fa', + slug: 'fa', + name: 'GUARDiA FA', + tagline: 'AI 기반 스마트 팩토리 자동화 플랫폼 (준비중)', + category: '제조', + port: 8017, + color: '#64748b', + status: 'soon', + access: { url: url(8017), id: '', pw: '' }, + features: [ + { title: 'e-Paper 지시서', desc: 'e-Paper 전자 공정 지시서' }, + { title: 'QR 작업 추적', desc: 'QR 기반 작업 추적' }, + { title: 'MES 연동', desc: 'MES 연동 실시간 현황' }, + { title: '로봇·PLC 인터페이스', desc: '로봇 · PLC 인터페이스' }, + { title: 'AI 공정 최적화', desc: 'AI 공정 최적화' }, + ], + steps: [ + { num: 1, title: '배포 예정', desc: 'GUARDiA FA(공장 자동화)는 현재 준비 중입니다.' }, + ], + mobileScreens: [], + }, +]; +export const CATEGORIES = [ + { name: '운영관리', slugs: ['itsm', 'erp', 'crm', 'hrm'] }, + { name: 'AI문서', slugs: ['ocr', 'bi'] }, + { name: '프로젝트', slugs: ['pms', 'rpa'] }, + { name: '협업', slugs: ['groupware', 'portal'] }, + { name: '커머스', slugs: ['mall', 'cms', 'esn'] }, + { name: '제조', slugs: ['mes', 'fa'] }, +]; +export const getSolution = (slug) => SOLUTIONS.find((s) => s.slug === slug); diff --git a/src/data/solutions.ts b/src/data/solutions.ts new file mode 100644 index 0000000..bac231b --- /dev/null +++ b/src/data/solutions.ts @@ -0,0 +1,545 @@ +export interface Solution { + id: string; + slug: string; + name: string; + tagline: string; + category: string; + port: number; + color: string; + status: 'live' | 'soon' | 'dev'; + access: { + url: string; + httpsUrl?: string; + id: string; + pw: string; + }; + features: { title: string; desc: string }[]; + steps: { num: number; title: string; desc: string }[]; + mobileScreens: { name: string; type: 'customer' | 'admin' | 'field' }[]; +} + +const IP = '101.79.17.164'; +const url = (port: number) => `http://${IP}:${port}`; + +export const SOLUTIONS: Solution[] = [ + { + id: 'itsm', + slug: 'itsm', + name: 'GUARDiA ITSM', + tagline: 'AI 기반 레거시 인프라 자율 운영 플랫폼', + category: '운영관리', + port: 9001, + color: '#6366f1', + status: 'live', + access: { url: url(9001), httpsUrl: 'https://zioinfo.co.kr:8443', id: 'admin', pw: '1111' }, + features: [ + { title: 'SR 자동화', desc: 'SR(서비스요청) 접수·처리·완료 전 과정 자동화' }, + { title: 'CMDB 자산관리', desc: 'CMDB 서버 자산 등록·조회·의존성 관리' }, + { title: 'AI 자연어 명령', desc: '`/배포 서버A 앱B` 자연어 명령으로 운영 자동화' }, + { title: '인시던트·변경관리', desc: '인시던트 대응 및 CAB 승인 변경관리' }, + { title: 'SLA 모니터링', desc: 'SLA 실시간 모니터링 및 위반 알림' }, + { title: '배포 자동화', desc: 'SSH/SFTP 에이전트리스 배포 자동화' }, + { title: '보안 점검', desc: 'CSAP·ISMS 보안 점검 자동화' }, + { title: '메신저 봇 연동', desc: '`/sr`, `/status` 메신저 봇 명령어 연동' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'https://zioinfo.co.kr:8443 접속 → admin / 1111 로그인' }, + { num: 2, title: '대시보드 확인', desc: '오늘 SR 수, 처리율, SLA 현황 확인' }, + { num: 3, title: 'SR 접수', desc: '[새 요청] → 제목 입력 → 자동 분류 확인' }, + { num: 4, title: 'AI 명령', desc: '메신저 탭 → `/sr 서버 장애 발생` 입력' }, + { num: 5, title: '배포', desc: '[배포관리] → [새 배포] → 서버 선택 → 실행' }, + { num: 6, title: 'CMDB', desc: '[자산관리] → 서버 목록 확인' }, + ], + mobileScreens: [ + { name: 'SR 목록', type: 'customer' }, + { name: 'SR 상세', type: 'customer' }, + { name: '빠른 SR 등록', type: 'customer' }, + { name: '승인 요청', type: 'admin' }, + { name: '서버 현황', type: 'admin' }, + { name: 'AI 챗봇', type: 'customer' }, + ], + }, + { + id: 'erp', + slug: 'erp', + name: 'GUARDiA ERP', + tagline: 'AI 기반 스마트 엔터프라이즈 자원관리', + category: '운영관리', + port: 8003, + color: '#10b981', + status: 'live', + access: { url: url(8003), id: 'admin', pw: 'admin123' }, + features: [ + { title: '전표 자동 분류', desc: 'AI 기반 전표 자동 분류 및 계정 매핑' }, + { title: '예산·KPI', desc: '예산 관리 및 KPI 대시보드' }, + { title: '구매 발주', desc: '구매 발주 · 입고 처리 워크플로우' }, + { title: '인사·급여 연동', desc: 'HRM 인사 · 급여 시스템 연동' }, + { title: '영업·매출 분석', desc: '영업 활동 및 매출 분석' }, + { title: 'FinOps', desc: 'FinOps 클라우드 비용 추적' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8003 접속 → admin / admin123 로그인' }, + { num: 2, title: '대시보드', desc: '매출·비용·KPI 현황 확인' }, + { num: 3, title: '재무', desc: '[전표입력] → 항목 입력 → [AI 분류] 클릭 → 계정 자동 입력 확인' }, + { num: 4, title: '예산', desc: '[예산현황] → 부서별 집행률 확인' }, + { num: 5, title: '구매', desc: '[발주서] → [신규] → 품목 입력 → 공급업체 선택 → 승인 요청' }, + ], + mobileScreens: [ + { name: 'KPI 대시보드', type: 'admin' }, + { name: '전표 조회', type: 'admin' }, + { name: '예산 현황', type: 'admin' }, + { name: '구매 승인', type: 'admin' }, + { name: '영업 매출', type: 'admin' }, + ], + }, + { + id: 'crm', + slug: 'crm', + name: 'GUARDiA CRM', + tagline: 'AI 기반 공공기관 고객관계관리', + category: '운영관리', + port: 8004, + color: '#f59e0b', + status: 'live', + access: { url: url(8004), id: 'admin', pw: 'admin123' }, + features: [ + { title: '고객 관리', desc: '고객(기관) 등록 · 히스토리 관리' }, + { title: '영업 파이프라인', desc: '영업기회 파이프라인 단계별 관리' }, + { title: '계약·입찰 추적', desc: '나라장터 G2B 연동 계약 · 입찰 추적' }, + { title: '캠페인 관리', desc: '마케팅 캠페인 기획·실행·분석' }, + { title: 'AI 고객 인사이트', desc: 'AI 기반 고객 행동 인사이트' }, + { title: 'ITSM SR 연동', desc: '고객 문의 → SR 자동 생성' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8004 접속 → admin / admin123 로그인' }, + { num: 2, title: '대시보드', desc: '영업 파이프라인 현황 확인' }, + { num: 3, title: '고객', desc: '[고객등록] → 기관명·담당자·연락처 입력' }, + { num: 4, title: '영업기회', desc: '[새 기회] → 고객 연결 → 단계 설정 (발굴→제안→협상→계약)' }, + { num: 5, title: '계약', desc: '[계약등록] → 나라장터 계약번호 입력 → OCR로 계약서 자동 파싱' }, + ], + mobileScreens: [ + { name: '고객 목록', type: 'admin' }, + { name: '영업기회', type: 'admin' }, + { name: '계약 현황', type: 'admin' }, + { name: '입찰 알림', type: 'admin' }, + { name: 'AI 분석', type: 'admin' }, + ], + }, + { + id: 'ocr', + slug: 'ocr', + name: 'GUARDiA OCR', + tagline: 'AI 기반 지능형 문서 인식 플랫폼', + category: 'AI문서', + port: 8005, + color: '#8b5cf6', + status: 'live', + access: { url: url(8005), id: 'admin', pw: 'admin123' }, + features: [ + { title: '계약서 등록', desc: '계약서 → 나라장터 계약 자동 등록' }, + { title: '납품서 처리', desc: '납품서 → CMDB 서버 자동 등록' }, + { title: '청구서 처리', desc: '청구서 → 세금계산서 처리' }, + { title: '장애보고서 → SR', desc: '장애보고서 이미지 → SR 자동 생성' }, + { title: '회의록 → SR', desc: '회의록 → 액션아이템 SR 생성' }, + { title: '감사보고서', desc: '감사보고서 → CSAP 준수율 업데이트' }, + { title: '브랜드 계약서', desc: '현대백화점 등 브랜드 계약서 → 계약 이력' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8005 접속 → admin / admin123 로그인' }, + { num: 2, title: '문서 업로드', desc: '[문서 업로드] → 문서 이미지/PDF 선택' }, + { num: 3, title: '워크플로우 선택', desc: '워크플로우 선택 → 장애보고서→SR 선택' }, + { num: 4, title: 'OCR 실행', desc: '[OCR 실행] 클릭 → AI 텍스트 추출 확인' }, + { num: 5, title: 'SR 생성', desc: '추출 결과 확인 → [SR 자동 생성] 클릭 → ITSM SR 생성 확인' }, + ], + mobileScreens: [ + { name: '카메라 촬영', type: 'field' }, + { name: '문서 업로드', type: 'field' }, + { name: 'OCR 결과', type: 'customer' }, + { name: '워크플로우', type: 'customer' }, + { name: '처리 이력', type: 'customer' }, + ], + }, + { + id: 'bi', + slug: 'bi', + name: 'GUARDiA BI', + tagline: 'AI 기반 비즈니스 인텔리전스 플랫폼', + category: 'AI문서', + port: 8006, + color: '#06b6d4', + status: 'live', + access: { url: url(8006), id: 'admin', pw: 'admin123' }, + features: [ + { title: '데이터소스 연결', desc: 'PostgreSQL · API · CSV 데이터소스 연결' }, + { title: '대시보드 빌더', desc: '드래그앤드롭 대시보드 빌더' }, + { title: 'KPI 위젯·알림', desc: 'KPI 위젯 및 실시간 알림' }, + { title: 'AI Text-to-SQL', desc: '자연어 → SQL 쿼리 자동 생성' }, + { title: '예측·이상탐지', desc: 'AI 예측 및 이상탐지 분석' }, + { title: 'ITSM 데이터 피드', desc: 'SR·SLA·배포 통계 데이터 피드' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8006 접속 → admin / admin123 로그인' }, + { num: 2, title: '데이터소스', desc: '[추가] → PostgreSQL 선택 → ITSM DB 연결' }, + { num: 3, title: '대시보드', desc: '[새 대시보드] → 위젯 추가 → 막대/라인/KPI 선택' }, + { num: 4, title: 'AI 쿼리', desc: '검색창에 "이번 달 미처리 SR 현황" 입력 → SQL 자동 생성 확인' }, + { num: 5, title: '리포트', desc: '[내보내기] → PDF 저장' }, + ], + mobileScreens: [ + { name: 'KPI 대시보드', type: 'admin' }, + { name: '차트 조회', type: 'admin' }, + { name: 'AI 인사이트', type: 'admin' }, + { name: '리포트', type: 'admin' }, + { name: '알림', type: 'admin' }, + ], + }, + { + id: 'pms', + slug: 'pms', + name: 'GUARDiA PMS', + tagline: 'JIRA 호환 AI 프로젝트 관리 시스템', + category: '프로젝트', + port: 8007, + color: '#f97316', + status: 'live', + access: { url: url(8007), id: 'admin', pw: 'admin123' }, + features: [ + { title: '이슈 추적', desc: '이슈 생성·추적 (JIRA 호환)' }, + { title: 'Scrum·Kanban', desc: 'Scrum · Kanban 보드' }, + { title: 'WBS·마일스톤', desc: 'WBS · 마일스톤 관리' }, + { title: '전자결재', desc: '다단계 결재선 전자결재' }, + { title: 'SI 산출물', desc: 'SI 산출물 관리 (감리/준공)' }, + { title: '요구사항 추적', desc: '요구사항 추적 (RTM)' }, + { title: 'AI 회의록', desc: 'AI 회의록 자동 작성' }, + { title: 'RFP 분석', desc: 'RFP → 요구사항분석서 자동 생성' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8007 접속 → admin / admin123 로그인' }, + { num: 2, title: '프로젝트', desc: '[새 프로젝트] → 이름·유형(Scrum) 선택' }, + { num: 3, title: '백로그', desc: '[이슈 추가] → 제목·담당자·우선순위 입력' }, + { num: 4, title: '보드', desc: 'Kanban 뷰 전환 → 이슈 드래그앤드롭으로 상태 변경' }, + { num: 5, title: 'AI 기능', desc: '[회의록] → 텍스트 붙여넣기 → [액션아이템 추출] 클릭' }, + ], + mobileScreens: [ + { name: '내 이슈', type: 'customer' }, + { name: '보드', type: 'customer' }, + { name: '스프린트', type: 'customer' }, + { name: '승인 요청', type: 'admin' }, + { name: 'WBS 조회', type: 'admin' }, + ], + }, + { + id: 'rpa', + slug: 'rpa', + name: 'GUARDiA RPA', + tagline: 'AI 기반 노코드 업무 자동화 플랫폼', + category: '프로젝트', + port: 8008, + color: '#ec4899', + status: 'live', + access: { url: url(8008), id: 'admin', pw: 'admin123' }, + features: [ + { title: '노코드 워크플로우', desc: '노코드 워크플로우 빌더' }, + { title: '봇 스케줄링', desc: '봇 등록 · 스케줄링' }, + { title: '자연어 생성', desc: '자연어 → 워크플로우 자동 생성' }, + { title: 'ROI 분석', desc: 'ROI 분석 대시보드' }, + { title: 'ITSM 연동', desc: 'ITSM SR 자동 처리 연동' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8008 접속 → admin / admin123 로그인' }, + { num: 2, title: '워크플로우', desc: '[새 워크플로우] → 노드 드래그앤드롭' }, + { num: 3, title: '트리거 설정', desc: '[스케줄] 선택 → 매일 09:00 설정' }, + { num: 4, title: '액션 추가', desc: '[ITSM SR 생성] 노드 추가 → 파라미터 입력' }, + { num: 5, title: 'AI 생성', desc: '입력창에 "매일 아침 서버 상태 체크 후 이상 시 SR 생성" → [생성] 클릭' }, + ], + mobileScreens: [ + { name: '봇 현황', type: 'admin' }, + { name: '워크플로우 목록', type: 'admin' }, + { name: '실행 이력', type: 'admin' }, + { name: 'ROI 현황', type: 'admin' }, + { name: '알림', type: 'admin' }, + ], + }, + { + id: 'groupware', + slug: 'groupware', + name: 'GUARDiA Groupware', + tagline: 'AI 기반 통합 협업 그룹웨어', + category: '협업', + port: 8009, + color: '#14b8a6', + status: 'live', + access: { url: url(8009), id: 'admin', pw: 'admin123' }, + features: [ + { title: '전자결재', desc: '다단계 결재선 · 대리결재' }, + { title: '게시판', desc: '공지 · 자유 · 팀별 게시판' }, + { title: '일정/캘린더', desc: '팀 공유 일정/캘린더' }, + { title: '근태/휴가', desc: '근태/휴가 신청·승인' }, + { title: '자원예약', desc: '회의실 · 장비 자원예약' }, + { title: '주소록·조직도', desc: '주소록 · 조직도 관리' }, + { title: '문서함', desc: '버전관리 문서함' }, + { title: '쪽지', desc: '내부 메시지 쪽지' }, + { title: 'AI 문서 요약', desc: 'AI 문서 자동 요약' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8009 접속 → admin / admin123 로그인' }, + { num: 2, title: '전자결재', desc: '[기안] → 양식 선택 → 내용 작성 → 결재선 설정 → 상신' }, + { num: 3, title: '일정', desc: '[일정등록] → 제목·날짜·참석자 입력 → 팀 공유' }, + { num: 4, title: '근태', desc: '[휴가신청] → 날짜·종류 선택 → 결재 요청' }, + { num: 5, title: '회의실', desc: '[예약] → 날짜·시간·회의실 선택 → 확정' }, + ], + mobileScreens: [ + { name: '결재함', type: 'admin' }, + { name: '공지사항', type: 'customer' }, + { name: '일정', type: 'customer' }, + { name: '근태', type: 'customer' }, + { name: '주소록', type: 'customer' }, + ], + }, + { + id: 'portal', + slug: 'portal', + name: 'GUARDiA Portal', + tagline: 'SSO 단일 로그인 통합 업무 포털', + category: '협업', + port: 8010, + color: '#7c3aed', + status: 'live', + access: { url: url(8010), id: 'admin', pw: 'admin123' }, + features: [ + { title: 'SSO 단일 로그인', desc: 'GUARDiA 전 시스템 SSO 바로가기' }, + { title: '위젯 대시보드', desc: '역할별 위젯 대시보드' }, + { title: '즐겨찾기', desc: '즐겨찾기 시스템' }, + { title: '통합 공지', desc: '통합 공지사항' }, + { title: '알림 센터', desc: '전 시스템 통합 알림 센터' }, + { title: '통합 검색', desc: '전 시스템 통합 검색' }, + { title: '마이페이지', desc: '프로필 · 설정 마이페이지' }, + { title: 'AI 공지 요약', desc: 'AI 공지 자동 요약' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8010 접속 → admin / admin123 로그인' }, + { num: 2, title: '포털 메인', desc: '9개 시스템 바로가기 카드 확인' }, + { num: 3, title: '위젯', desc: '[위젯 추가] → SR현황·KPI·일정 선택 → 대시보드 배치' }, + { num: 4, title: '통합검색', desc: '검색창에 "장애 처리" 입력 → ITSM·KB·문서 통합 결과 확인' }, + { num: 5, title: '알림센터', desc: '전 시스템 알림 통합 확인' }, + ], + mobileScreens: [ + { name: '포털 홈', type: 'customer' }, + { name: '알림', type: 'customer' }, + { name: '바로가기', type: 'customer' }, + { name: '통합검색', type: 'customer' }, + { name: '마이페이지', type: 'customer' }, + ], + }, + { + id: 'mall', + slug: 'mall', + name: 'GUARDiA Mall', + tagline: '미국 다지점 꽃집 옴니채널 e-커머스', + category: '커머스', + port: 8011, + color: '#f43f5e', + status: 'live', + access: { url: url(8011), id: 'admin', pw: 'admin123' }, + features: [ + { title: 'ZIP 배송권역', desc: '다매장 ZIP 배송권역 관리' }, + { title: '타임슬롯', desc: '타임슬롯 스케줄링 (당일배송)' }, + { title: '구독 서비스', desc: '정기 구독 서비스' }, + { title: '게이트웨이 어댑터', desc: 'Stripe · Twilio mock 게이트웨이 어댑터' }, + { title: 'AI 추천·예측', desc: 'AI 상품 추천 · 수요예측' }, + { title: '매장간 재고 이양', desc: '매장간 재고 이양' }, + { title: '관리자 앱', desc: '매장 매니저용 관리자 앱' }, + { title: 'i18n 한/영', desc: '한국어/영어 다국어 지원' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8011 접속 → admin / admin123 로그인' }, + { num: 2, title: '상품 확인', desc: '상품 목록 확인 (Montvale Florist 실제 상품)' }, + { num: 3, title: '주문', desc: '상품 선택 → ZIP 코드 입력 → 배송 슬롯 선택 → 장바구니 추가' }, + { num: 4, title: '결제', desc: '카드 정보 (mock: 4242-4242-4242-4242) → 주문 완료' }, + { num: 5, title: '관리자', desc: '[주문관리] → 주문 확인 → 상태 변경 (준비중→배송중→완료)' }, + ], + mobileScreens: [ + { name: '상품 목록', type: 'customer' }, + { name: '상품 상세', type: 'customer' }, + { name: '장바구니', type: 'customer' }, + { name: '결제', type: 'customer' }, + { name: '주문 칸반', type: 'admin' }, + { name: '재고 ON/OFF', type: 'admin' }, + ], + }, + { + id: 'cms', + slug: 'cms', + name: 'GUARDiA CMS', + tagline: 'AI 기반 통합 콘텐츠 관리 시스템', + category: '커머스', + port: 8012, + color: '#0ea5e9', + status: 'live', + access: { url: url(8012), id: 'admin', pw: 'admin123' }, + features: [ + { title: '헤드리스 CMS', desc: '페이지 · 포스트 · 블록 헤드리스 CMS' }, + { title: '게시 워크플로우', desc: '버전관리 · 게시 워크플로우 (draft→published)' }, + { title: '미디어 라이브러리', desc: '미디어 라이브러리 관리' }, + { title: '배너·프로모션', desc: '배너 · 프로모션 관리' }, + { title: 'AI SEO 제안', desc: 'AI 기반 SEO 자동 제안' }, + { title: '다국어·테마', desc: '다국어 · 테마 관리' }, + { title: 'UGC 모더레이션', desc: 'UGC 콘텐츠 모더레이션' }, + { title: '폼 빌더', desc: '드래그앤드롭 폼 빌더' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8012 접속 → admin / admin123 로그인' }, + { num: 2, title: '페이지', desc: '[새 페이지] → 제목 입력 → 블록 에디터에서 콘텐츠 추가' }, + { num: 3, title: '미디어', desc: '[업로드] → 이미지 선택 → AI 자동 태그 확인' }, + { num: 4, title: '워크플로우', desc: '페이지 상태를 draft → review → published 변경' }, + { num: 5, title: 'SEO', desc: '페이지 선택 → [AI SEO 제안] → 메타태그 자동 입력 확인' }, + ], + mobileScreens: [ + { name: '콘텐츠 목록', type: 'admin' }, + { name: '콘텐츠 승인', type: 'admin' }, + { name: '미디어 촬영업로드', type: 'field' }, + { name: 'UGC 모더레이션', type: 'admin' }, + { name: '성과', type: 'admin' }, + ], + }, + { + id: 'mes', + slug: 'mes', + name: 'GUARDiA MES', + tagline: 'AI 기반 제조실행시스템 (WMS+MES+QMS)', + category: '제조', + port: 8013, + color: '#84cc16', + status: 'live', + access: { url: url(8013), id: 'admin', pw: 'admin123' }, + features: [ + { title: '작업지시·실적', desc: '작업지시 · 생산실적 등록' }, + { title: 'BOM·라우팅', desc: 'BOM · 공정/라우팅 관리' }, + { title: '설비 OEE', desc: '설비 OEE 모니터링' }, + { title: 'LOT 추적', desc: 'LOT 추적' }, + { title: '창고관리', desc: '입고·출고·재고·피킹 창고관리' }, + { title: '검사', desc: '수입·공정·출하 검사' }, + { title: 'SPC 관리도', desc: 'SPC 관리도 (Cp/Cpk)' }, + { title: 'CAPA', desc: 'CAPA (시정조치)' }, + { title: 'AI 불량분석', desc: 'AI 불량원인 분석' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8013 접속 → admin / admin123 로그인' }, + { num: 2, title: '작업지시', desc: '[신규] → 품목·수량·공정 입력 → 발행' }, + { num: 3, title: '생산실적', desc: '작업지시 선택 → 생산량·불량량 입력 → 등록' }, + { num: 4, title: '검사', desc: '[공정검사] → LOT 번호 입력 → 검사항목 체크 → 합격/불합격' }, + { num: 5, title: 'SPC', desc: '관리도 차트 확인 → Cp/Cpk 수치 확인 → 이상 점 알림' }, + ], + mobileScreens: [ + { name: '작업지시 조회', type: 'field' }, + { name: '실적입력', type: 'field' }, + { name: '바코드스캔', type: 'field' }, + { name: '검사입력', type: 'field' }, + { name: '설비점검', type: 'field' }, + ], + }, + { + id: 'hrm', + slug: 'hrm', + name: 'GUARDiA HRM', + tagline: 'AI 기반 인사관리 플랫폼', + category: '운영관리', + port: 8014, + color: '#a855f7', + status: 'live', + access: { url: url(8014), id: 'admin', pw: 'admin123' }, + features: [ + { title: '사원 관리', desc: '사원 등록 · 이력관리' }, + { title: '조직도·직급', desc: '조직도 · 직급체계' }, + { title: '급여·명세서', desc: '급여 계산 · 명세서' }, + { title: '근태·휴가', desc: '근태 · 휴가 관리' }, + { title: '성과 평가', desc: '성과 평가 · KPI' }, + { title: '채용 관리', desc: '채용 공고 · 지원자 관리' }, + { title: '교육 관리', desc: '교육 이수 관리' }, + { title: 'AI 인사 분석', desc: 'AI 인사 분석' }, + { title: 'Groupware 연동', desc: 'Groupware 연동' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8014 접속 → admin / admin123 로그인' }, + { num: 2, title: '사원', desc: '[신규 등록] → 기본정보·부서·직급 입력 → 저장' }, + { num: 3, title: '급여', desc: '사원 선택 → 월급여 입력 → [명세서 생성] → PDF 확인' }, + { num: 4, title: '성과', desc: '[평가 등록] → KPI 항목 입력 → 점수 부여' }, + { num: 5, title: 'AI', desc: '[인사 인사이트] → 이탈 위험도·성과 트렌드 확인' }, + ], + mobileScreens: [ + { name: '사원증', type: 'customer' }, + { name: '근태 체크인', type: 'customer' }, + { name: '급여 명세서', type: 'customer' }, + { name: '휴가신청', type: 'customer' }, + { name: '성과현황', type: 'customer' }, + ], + }, + { + id: 'esn', + slug: 'esn', + name: 'GUARDiA ESN', + tagline: 'ESL 전자가격표 통합 관리 플랫폼', + category: '커머스', + port: 8015, + color: '#f59e0b', + status: 'live', + access: { url: url(8015), id: 'admin', pw: 'admin123' }, + features: [ + { title: '멀티테넌트', desc: 'LGInnotek · LGIT · Emart · ZIOINFO 멀티테넌트' }, + { title: 'ESL 기기 관리', desc: 'ESL 기기 등록 · 상태 모니터링' }, + { title: 'POS 자동 변환', desc: 'POS 데이터 → ESL 가격 자동 변환' }, + { title: '템플릿 관리', desc: '크기별 레이아웃 템플릿 관리' }, + { title: 'HCore 관제', desc: 'HCore 게이트웨이/허브 관제' }, + { title: '알람·장애 이력', desc: '알람 · 장애 이력 관리' }, + { title: '펌웨어 업데이트', desc: '펌웨어 업데이트 관리' }, + { title: 'AI 알람 분석', desc: 'Ollama AI 알람 원인 분석' }, + ], + steps: [ + { num: 1, title: '접속·로그인', desc: 'http://101.79.17.164:8015 접속 → admin / admin123 로그인' }, + { num: 2, title: '테넌트 선택', desc: '드롭다운에서 LGINNOTEK 선택' }, + { num: 3, title: 'ESL 기기', desc: '기기 목록 확인 → 상태(온라인/오프라인) 확인' }, + { num: 4, title: 'POS 변환', desc: '[POS 데이터 업로드] → CSV 파일 선택 → 변환 실행' }, + { num: 5, title: '알람', desc: '알람 목록 → [AI 원인분석] 클릭 → Ollama 분석 결과 확인' }, + ], + mobileScreens: [ + { name: 'ESL 기기 목록', type: 'field' }, + { name: '알람', type: 'field' }, + { name: 'HCore 상태', type: 'field' }, + { name: '펌웨어', type: 'admin' }, + { name: '작업 이력', type: 'field' }, + ], + }, + { + id: 'fa', + slug: 'fa', + name: 'GUARDiA FA', + tagline: 'AI 기반 스마트 팩토리 자동화 플랫폼 (준비중)', + category: '제조', + port: 8017, + color: '#64748b', + status: 'soon', + access: { url: url(8017), id: '', pw: '' }, + features: [ + { title: 'e-Paper 지시서', desc: 'e-Paper 전자 공정 지시서' }, + { title: 'QR 작업 추적', desc: 'QR 기반 작업 추적' }, + { title: 'MES 연동', desc: 'MES 연동 실시간 현황' }, + { title: '로봇·PLC 인터페이스', desc: '로봇 · PLC 인터페이스' }, + { title: 'AI 공정 최적화', desc: 'AI 공정 최적화' }, + ], + steps: [ + { num: 1, title: '배포 예정', desc: 'GUARDiA FA(공장 자동화)는 현재 준비 중입니다.' }, + ], + mobileScreens: [], + }, +]; + +export const CATEGORIES: { name: string; slugs: string[] }[] = [ + { name: '운영관리', slugs: ['itsm', 'erp', 'crm', 'hrm'] }, + { name: 'AI문서', slugs: ['ocr', 'bi'] }, + { name: '프로젝트', slugs: ['pms', 'rpa'] }, + { name: '협업', slugs: ['groupware', 'portal'] }, + { name: '커머스', slugs: ['mall', 'cms', 'esn'] }, + { name: '제조', slugs: ['mes', 'fa'] }, +]; + +export const getSolution = (slug: string): Solution | undefined => + SOLUTIONS.find((s) => s.slug === slug); diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..f941a20 --- /dev/null +++ b/src/main.js @@ -0,0 +1,9 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import App from './App'; +import './styles/tokens.css'; +import './styles/global.css'; +import './styles/app.css'; +ReactDOM.createRoot(document.getElementById('root')).render(_jsx(React.StrictMode, { children: _jsx(BrowserRouter, { children: _jsx(App, {}) }) })); diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..6f22a1a --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import App from './App'; +import './styles/tokens.css'; +import './styles/global.css'; +import './styles/app.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + +); diff --git a/src/pages/CatalogPage.js b/src/pages/CatalogPage.js new file mode 100644 index 0000000..c099d6d --- /dev/null +++ b/src/pages/CatalogPage.js @@ -0,0 +1,14 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useNavigate } from 'react-router-dom'; +import { CATEGORIES, getSolution } from '../data/solutions'; +import { SolutionCard } from '../components/SolutionCard'; +export const CatalogPage = () => { + const navigate = useNavigate(); + return (_jsx("main", { className: "section", children: _jsxs("div", { className: "container", children: [_jsx("h1", { className: "section-title", children: "\uC804\uCCB4 \uC194\uB8E8\uC158" }), _jsx("p", { className: "section-sub", children: "\uCE74\uD14C\uACE0\uB9AC\uBCC4 GUARDiA \uC194\uB8E8\uC158 \u2014 \uCD1D 15\uAC1C" }), CATEGORIES.map((cat) => (_jsxs("section", { className: "catalog-cat", children: [_jsxs("h2", { className: "catalog-cat-title", children: [cat.name, _jsx("span", { className: "catalog-cat-count", children: cat.slugs.length })] }), _jsx("div", { className: "card-grid", children: cat.slugs.map((slug) => { + const sol = getSolution(slug); + if (!sol) + return null; + return (_jsx(SolutionCard, { solution: sol, onClick: () => navigate(`/solution/${slug}`) }, slug)); + }) })] }, cat.name)))] }) })); +}; +export default CatalogPage; diff --git a/src/pages/CatalogPage.tsx b/src/pages/CatalogPage.tsx new file mode 100644 index 0000000..9464f01 --- /dev/null +++ b/src/pages/CatalogPage.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import { CATEGORIES, getSolution } from '../data/solutions'; +import { SolutionCard } from '../components/SolutionCard'; + +export const CatalogPage: React.FC = () => { + const navigate = useNavigate(); + + return ( +
+
+

전체 솔루션

+

카테고리별 GUARDiA 솔루션 — 총 15개

+ + {CATEGORIES.map((cat) => ( +
+

+ {cat.name} + {cat.slugs.length} +

+
+ {cat.slugs.map((slug) => { + const sol = getSolution(slug); + if (!sol) return null; + return ( + navigate(`/solution/${slug}`)} + /> + ); + })} +
+
+ ))} +
+
+ ); +}; + +export default CatalogPage; diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js new file mode 100644 index 0000000..731e41d --- /dev/null +++ b/src/pages/HomePage.js @@ -0,0 +1,10 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useNavigate } from 'react-router-dom'; +import { SOLUTIONS } from '../data/solutions'; +import { SolutionCard } from '../components/SolutionCard'; +const liveCount = SOLUTIONS.filter((s) => s.status === 'live').length; +export const HomePage = () => { + const navigate = useNavigate(); + return (_jsxs("main", { children: [_jsx("section", { className: "hero", children: _jsxs("div", { className: "container hero-inner", children: [_jsx("span", { className: "hero-eyebrow", children: "\uC9C0\uC624\uC815\uBCF4\uAE30\uC220 \u00B7 GUARDiA Platform" }), _jsxs("h1", { className: "hero-title", children: ["GUARDiA ", _jsx("span", { className: "hero-grad", children: "\uC194\uB8E8\uC158 \uD3EC\uD138" })] }), _jsx("p", { className: "hero-sub", children: "15\uAC1C \uC194\uB8E8\uC158 \uC2DC\uC5F0 \uAC00\uC774\uB4DC \u00B7 \uC811\uC18D \uC815\uBCF4 \u00B7 \uBAA8\uBC14\uC77C \uC571" }), _jsxs("div", { className: "hero-actions", children: [_jsx("button", { className: "btn-primary", onClick: () => navigate('/catalog'), children: "\uC804\uCCB4 \uC194\uB8E8\uC158 \uBCF4\uAE30" }), _jsx("a", { className: "btn-ghost", href: "http://101.79.17.164:9001/app", target: "_blank", rel: "noreferrer", children: "\uBAA8\uBC14\uC77C \uC571 \uB2E4\uC6B4\uB85C\uB4DC" })] })] }) }), _jsx("section", { className: "stats-strip", children: _jsxs("div", { className: "container stats-inner", children: [_jsxs("div", { className: "stat", children: [_jsx("span", { className: "stat-value", children: "15\uAC1C" }), _jsx("span", { className: "stat-label", children: "\uC194\uB8E8\uC158" })] }), _jsxs("div", { className: "stat", children: [_jsxs("span", { className: "stat-value", children: [liveCount, "\uAC1C"] }), _jsx("span", { className: "stat-label", children: "LIVE \uC6B4\uC601 \uC911" })] }), _jsxs("div", { className: "stat", children: [_jsx("span", { className: "stat-value", children: "\uC628\uD504\uB808\uBBF8\uC2A4" }), _jsx("span", { className: "stat-label", children: "AI (Ollama)" })] })] }) }), _jsx("section", { className: "section", children: _jsxs("div", { className: "container", children: [_jsx("h2", { className: "section-title", children: "\uC194\uB8E8\uC158 \uCE74\uD0C8\uB85C\uADF8" }), _jsx("p", { className: "section-sub", children: "\uCE74\uB4DC\uB97C \uD074\uB9AD\uD558\uBA74 \uC811\uC18D \uC815\uBCF4\uC640 \uB2E8\uACC4\uBCC4 \uC2DC\uC5F0 \uAC00\uC774\uB4DC\uB97C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4." }), _jsx("div", { className: "card-grid", children: SOLUTIONS.map((s) => (_jsx(SolutionCard, { solution: s, onClick: () => navigate(`/solution/${s.slug}`) }, s.id))) })] }) }), _jsx("footer", { className: "home-footer", children: _jsxs("div", { className: "container", children: [_jsxs("p", { children: ["\uC11C\uBC84 IP: ", _jsx("code", { children: "101.79.17.164" })] }), _jsxs("p", { children: ["\uBAA8\uBC14\uC77C \uC571 \uB2E4\uC6B4\uB85C\uB4DC:", ' ', _jsx("a", { href: "http://101.79.17.164:9001/app", target: "_blank", rel: "noreferrer", children: "http://101.79.17.164:9001/app" })] }), _jsx("p", { className: "home-footer-copy", children: "\u00A9 2026 \uC9C0\uC624\uC815\uBCF4\uAE30\uC220 GUARDiA Platform" })] }) })] })); +}; +export default HomePage; diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx new file mode 100644 index 0000000..9b9f51b --- /dev/null +++ b/src/pages/HomePage.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import { SOLUTIONS } from '../data/solutions'; +import { SolutionCard } from '../components/SolutionCard'; + +const liveCount = SOLUTIONS.filter((s) => s.status === 'live').length; + +export const HomePage: React.FC = () => { + const navigate = useNavigate(); + + return ( +
+ {/* Hero */} +
+
+ 지오정보기술 · GUARDiA Platform +

+ GUARDiA 솔루션 포털 +

+

+ 15개 솔루션 시연 가이드 · 접속 정보 · 모바일 앱 +

+
+ + + 모바일 앱 다운로드 + +
+
+
+ + {/* Stats */} +
+
+
+ 15개 + 솔루션 +
+
+ {liveCount}개 + LIVE 운영 중 +
+
+ 온프레미스 + AI (Ollama) +
+
+
+ + {/* Grid */} +
+
+

솔루션 카탈로그

+

카드를 클릭하면 접속 정보와 단계별 시연 가이드를 확인할 수 있습니다.

+
+ {SOLUTIONS.map((s) => ( + navigate(`/solution/${s.slug}`)} /> + ))} +
+
+
+ + {/* Footer info */} + +
+ ); +}; + +export default HomePage; diff --git a/src/pages/SolutionDetailPage.js b/src/pages/SolutionDetailPage.js new file mode 100644 index 0000000..0787f9f --- /dev/null +++ b/src/pages/SolutionDetailPage.js @@ -0,0 +1,19 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useParams, Link } from 'react-router-dom'; +import { getSolution } from '../data/solutions'; +import { SolutionIcon } from '../components/SolutionIcon'; +import { LoginBox } from '../components/LoginBox'; +import { FeatureGrid } from '../components/FeatureGrid'; +import { StepGuide } from '../components/StepGuide'; +import { MobileScreenList } from '../components/MobileScreenList'; +export const SolutionDetailPage = () => { + const { slug } = useParams(); + const solution = slug ? getSolution(slug) : undefined; + if (!solution) { + return (_jsx("main", { className: "section", children: _jsxs("div", { className: "container", children: [_jsx("h1", { className: "section-title", children: "\uC194\uB8E8\uC158\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" }), _jsx(Link, { to: "/catalog", className: "btn-primary", style: { marginTop: 16, display: 'inline-block' }, children: "\uC804\uCCB4 \uC194\uB8E8\uC158\uC73C\uB85C" })] }) })); + } + const { name, tagline, color, status, category, access, features, steps, mobileScreens } = solution; + const soon = status === 'soon'; + return (_jsxs("main", { className: "detail", children: [_jsx("section", { className: "detail-header", style: { background: `radial-gradient(900px 360px at 18% -20%, ${color}33, transparent 70%)` }, children: _jsxs("div", { className: "container", children: [_jsx(Link, { to: "/catalog", className: "detail-back", children: "\u2190 \uC804\uCCB4 \uC194\uB8E8\uC158" }), _jsxs("div", { className: "detail-header-row", children: [_jsx("span", { className: "detail-icon", style: { color, background: `${color}1a`, borderColor: `${color}40` }, children: _jsx(SolutionIcon, { slug: solution.slug, size: 44, color: color }) }), _jsxs("div", { children: [_jsxs("div", { className: "detail-title-row", children: [_jsx("h1", { className: "detail-title", children: name }), status === 'live' ? (_jsx("span", { className: "badge badge-live", children: "LIVE" })) : (_jsx("span", { className: "badge badge-soon", children: "SOON" }))] }), _jsx("p", { className: "detail-tagline", children: tagline }), _jsx("span", { className: "detail-cat", children: category })] })] })] }) }), _jsxs("div", { className: "container detail-body", children: [_jsx("section", { className: "detail-section", children: _jsx(LoginBox, { access: access, color: color, soon: soon }) }), _jsxs("section", { className: "detail-section", children: [_jsx("h2", { className: "section-title", children: "\uC8FC\uC694 \uAE30\uB2A5" }), _jsxs("p", { className: "section-sub", children: [name, "\uC758 \uD575\uC2EC \uAE30\uB2A5\uC785\uB2C8\uB2E4."] }), _jsx(FeatureGrid, { features: features, color: color })] }), _jsxs("section", { className: "detail-section", children: [_jsx("h2", { className: "section-title", children: "\uB2E8\uACC4\uBCC4 \uC2DC\uC5F0 \uAC00\uC774\uB4DC" }), _jsx("p", { className: "section-sub", children: "\uC544\uB798 \uC21C\uC11C\uB300\uB85C \uB530\uB77C \uD558\uBA74 \uC8FC\uC694 \uAE30\uB2A5\uC744 \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4." }), _jsx(StepGuide, { steps: steps, color: color })] }), _jsxs("section", { className: "detail-section", children: [_jsx("h2", { className: "section-title", children: "\uBAA8\uBC14\uC77C \uC571 \uD654\uBA74" }), _jsx("p", { className: "section-sub", children: "\uD604\uC7A5\u00B7\uAD00\uB9AC\uC790\u00B7\uC0AC\uC6A9\uC790\uC6A9 \uBAA8\uBC14\uC77C \uD654\uBA74 \uAD6C\uC131\uC785\uB2C8\uB2E4." }), _jsx(MobileScreenList, { screens: mobileScreens, color: color })] })] })] })); +}; +export default SolutionDetailPage; diff --git a/src/pages/SolutionDetailPage.tsx b/src/pages/SolutionDetailPage.tsx new file mode 100644 index 0000000..57179a8 --- /dev/null +++ b/src/pages/SolutionDetailPage.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { useParams, Link } from 'react-router-dom'; +import { getSolution } from '../data/solutions'; +import { SolutionIcon } from '../components/SolutionIcon'; +import { LoginBox } from '../components/LoginBox'; +import { FeatureGrid } from '../components/FeatureGrid'; +import { StepGuide } from '../components/StepGuide'; +import { MobileScreenList } from '../components/MobileScreenList'; + +export const SolutionDetailPage: React.FC = () => { + const { slug } = useParams<{ slug: string }>(); + const solution = slug ? getSolution(slug) : undefined; + + if (!solution) { + return ( +
+
+

솔루션을 찾을 수 없습니다

+ + 전체 솔루션으로 + +
+
+ ); + } + + const { name, tagline, color, status, category, access, features, steps, mobileScreens } = solution; + const soon = status === 'soon'; + + return ( +
+ {/* Header */} +
+
+ ← 전체 솔루션 +
+ + + +
+
+

{name}

+ {status === 'live' ? ( + LIVE + ) : ( + SOON + )} +
+

{tagline}

+ {category} +
+
+
+
+ +
+ {/* Login */} +
+ +
+ + {/* Features */} +
+

주요 기능

+

{name}의 핵심 기능입니다.

+ +
+ + {/* Steps */} +
+

단계별 시연 가이드

+

아래 순서대로 따라 하면 주요 기능을 확인할 수 있습니다.

+ +
+ + {/* Mobile */} +
+

모바일 앱 화면

+

현장·관리자·사용자용 모바일 화면 구성입니다.

+ +
+
+
+ ); +}; + +export default SolutionDetailPage; diff --git a/src/styles/app.css b/src/styles/app.css new file mode 100644 index 0000000..b6bde65 --- /dev/null +++ b/src/styles/app.css @@ -0,0 +1,217 @@ +/* ============ Navbar ============ */ +.navbar { + position: sticky; top: 0; z-index: 50; + background: rgba(6, 11, 24, 0.78); + backdrop-filter: blur(14px); + border-bottom: 1px solid var(--border); +} +.navbar-inner { display: flex; align-items: center; justify-content: space-between; height: 64px; } +.navbar-logo { display: flex; align-items: center; gap: 10px; } +.navbar-logo-mark { + width: 32px; height: 32px; border-radius: 9px; + background: var(--accent-grad); + display: grid; place-items: center; + font-weight: 800; font-size: 18px; color: #fff; +} +.navbar-logo-text { font-weight: 700; font-size: 16px; letter-spacing: -0.2px; } +.navbar-nav { display: flex; align-items: center; gap: 20px; } +.navbar-link { font-size: 14px; color: var(--text-secondary); transition: color var(--transition); } +.navbar-link:hover { color: var(--text-primary); } +.navbar-cta { + padding: 8px 16px; border-radius: var(--radius-sm); + background: var(--accent-grad); color: #fff; font-size: 13px; font-weight: 600; + transition: opacity var(--transition); +} +.navbar-cta:hover { opacity: 0.88; } + +/* ============ Hero ============ */ +.hero { + position: relative; overflow: hidden; + padding: 110px 0 90px; + background: + radial-gradient(800px 380px at 50% -10%, rgba(99,102,241,0.25), transparent 70%), + radial-gradient(600px 300px at 85% 10%, rgba(139,92,246,0.18), transparent 70%); +} +.hero-inner { text-align: center; } +.hero-eyebrow { + display: inline-block; font-size: 13px; letter-spacing: 1px; text-transform: uppercase; + color: var(--accent-2); font-weight: 600; margin-bottom: 18px; +} +.hero-title { font-size: 56px; font-weight: 800; line-height: 1.1; letter-spacing: -1.5px; } +.hero-grad { + background: var(--accent-grad); + -webkit-background-clip: text; background-clip: text; color: transparent; +} +.hero-sub { margin-top: 18px; font-size: 18px; color: var(--text-secondary); } +.hero-actions { margin-top: 36px; display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; } + +.btn-primary { + padding: 13px 26px; border: none; cursor: pointer; + border-radius: var(--radius-sm); background: var(--accent-grad); + color: #fff; font-size: 15px; font-weight: 600; + transition: transform var(--transition), box-shadow var(--transition); +} +.btn-primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-glow); } +.btn-ghost { + padding: 13px 26px; cursor: pointer; + border-radius: var(--radius-sm); + border: 1px solid var(--border); background: var(--bg-card); + color: var(--text-primary); font-size: 15px; font-weight: 600; + transition: background var(--transition); +} +.btn-ghost:hover { background: var(--bg-card-hover); } + +/* ============ Stats ============ */ +.stats-strip { border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); background: var(--bg-surface); } +.stats-inner { display: flex; justify-content: center; gap: 80px; padding: 36px 0; flex-wrap: wrap; } +.stat { text-align: center; } +.stat-value { + display: block; font-size: 30px; font-weight: 800; + background: var(--accent-grad); -webkit-background-clip: text; background-clip: text; color: transparent; +} +.stat-label { font-size: 13px; color: var(--text-muted); } + +/* ============ Card Grid ============ */ +.card-grid { + display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; +} +@media (max-width: 980px) { .card-grid { grid-template-columns: repeat(2, 1fr); } } +@media (max-width: 640px) { .card-grid { grid-template-columns: 1fr; } } + +.solution-card { + text-align: left; cursor: pointer; width: 100%; + display: flex; flex-direction: column; gap: 12px; + padding: 22px; border-radius: var(--radius-card); + background: var(--bg-card); border: 1px solid var(--border); + color: inherit; font-family: inherit; + transition: transform var(--transition), border-color var(--transition), box-shadow var(--transition); +} +.solution-card:hover { + transform: translateY(-4px); + border-color: var(--card-color); + box-shadow: 0 0 36px color-mix(in srgb, var(--card-color) 22%, transparent); +} +.solution-card-top { display: flex; align-items: center; justify-content: space-between; } +.solution-card-icon { + width: 52px; height: 52px; border-radius: 14px; + display: grid; place-items: center; border: 1px solid; +} +.solution-card-name { font-size: 18px; font-weight: 700; } +.solution-card-tagline { font-size: 13.5px; color: var(--text-muted); line-height: 1.5; flex: 1; } +.solution-card-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; } +.solution-card-port { font-family: var(--font-mono); font-size: 13px; color: var(--text-muted); } +.solution-card-more { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; } + +/* ============ Catalog ============ */ +.catalog-cat { margin-bottom: 48px; } +.catalog-cat-title { + display: flex; align-items: center; gap: 10px; + font-size: 20px; font-weight: 700; margin-bottom: 20px; +} +.catalog-cat-count { + font-size: 12px; font-weight: 700; color: var(--accent-2); + background: rgba(139,92,246,0.15); border: 1px solid rgba(139,92,246,0.3); + border-radius: 20px; padding: 2px 10px; +} + +/* ============ Detail ============ */ +.detail-header { padding: 36px 0 44px; border-bottom: 1px solid var(--border); } +.detail-back { display: inline-block; font-size: 14px; color: var(--text-muted); margin-bottom: 24px; transition: color var(--transition); } +.detail-back:hover { color: var(--text-primary); } +.detail-header-row { display: flex; align-items: center; gap: 22px; } +.detail-icon { width: 80px; height: 80px; border-radius: 20px; display: grid; place-items: center; border: 1px solid; flex-shrink: 0; } +.detail-title-row { display: flex; align-items: center; gap: 12px; } +.detail-title { font-size: 34px; font-weight: 800; letter-spacing: -0.8px; } +.detail-tagline { margin-top: 6px; font-size: 16px; color: var(--text-secondary); } +.detail-cat { + display: inline-block; margin-top: 12px; font-size: 12px; font-weight: 600; + color: var(--text-muted); border: 1px solid var(--border); border-radius: 20px; padding: 3px 12px; +} +.detail-body { padding: 48px 0 90px; } +.detail-section { margin-bottom: 56px; } + +/* ============ Login Box ============ */ +.login-box { + background: rgba(0, 0, 0, 0.4); + border: 1px solid var(--border); + border-radius: var(--radius-card); + padding: 24px; + max-width: 560px; +} +.login-box-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; } +.login-box-title { font-size: 14px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-muted); } +.login-field { + display: flex; align-items: center; gap: 12px; + padding: 12px 14px; border-radius: var(--radius-sm); + background: rgba(255,255,255,0.03); margin-bottom: 10px; +} +.login-field-label { width: 52px; font-size: 12px; font-weight: 700; color: var(--text-muted); flex-shrink: 0; } +.login-field-value { flex: 1; font-family: var(--font-mono); font-size: 16px; color: var(--text-primary); letter-spacing: 0.5px; } +.login-url { + flex: 1; display: inline-flex; align-items: center; gap: 6px; + font-family: var(--font-mono); font-size: 14px; color: #60a5fa; word-break: break-all; +} +.login-url:hover { text-decoration: underline; } +.login-copy-btn { + display: inline-flex; align-items: center; gap: 4px; + background: none; border: none; cursor: pointer; padding: 4px 6px; border-radius: 6px; + transition: background var(--transition); +} +.login-copy-btn:hover { background: rgba(255,255,255,0.08); } +.login-copied { font-size: 12px; font-weight: 700; color: #10b981; } +.login-soon-text { font-size: 14px; color: var(--text-muted); line-height: 1.6; } + +/* ============ Feature Grid ============ */ +.feature-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; } +@media (max-width: 640px) { .feature-grid { grid-template-columns: 1fr; } } +.feature-card { + display: flex; gap: 12px; align-items: flex-start; + padding: 16px 18px; border-radius: var(--radius-md); + background: var(--bg-card); border: 1px solid var(--border); +} +.feature-check { flex-shrink: 0; margin-top: 2px; display: inline-flex; } +.feature-title { font-size: 15px; font-weight: 600; margin-bottom: 3px; } +.feature-desc { font-size: 13.5px; color: var(--text-muted); line-height: 1.5; } + +/* ============ Step Guide ============ */ +.step-guide { list-style: none; } +.step-guide-item { display: grid; grid-template-columns: 44px 1fr; gap: 18px; } +.step-guide-rail { display: flex; flex-direction: column; align-items: center; } +.step-guide-num { + width: 40px; height: 40px; border-radius: 50%; + display: grid; place-items: center; font-size: 15px; font-weight: 700; color: #fff; flex-shrink: 0; +} +.step-guide-line { flex: 1; width: 2px; background: var(--border); margin: 6px 0; min-height: 16px; } +.step-guide-body { padding-bottom: 24px; } +.step-guide-body h4 { font-size: 16px; font-weight: 600; margin-bottom: 5px; } +.step-guide-body p { font-size: 14px; color: var(--text-secondary); line-height: 1.6; } + +/* ============ Mobile Screens ============ */ +.mobile-screen-list { display: flex; gap: 24px; flex-wrap: wrap; } +.mobile-empty { font-size: 14px; color: var(--text-muted); } +.phone-card { display: flex; flex-direction: column; align-items: center; gap: 12px; } +.phone-frame { width: 150px; height: 300px; border-radius: 26px; } +.phone-notch { width: 64px; height: 18px; } +.phone-screen-inner { + width: 100%; height: 100%; display: flex; flex-direction: column; + align-items: center; justify-content: center; gap: 14px; padding: 16px; +} +.phone-icon { display: inline-flex; } +.phone-screen-name { font-size: 13px; font-weight: 600; color: var(--text-secondary); text-align: center; } +.phone-label { display: flex; flex-direction: column; align-items: center; gap: 6px; } +.phone-name { font-size: 13px; font-weight: 600; } +.phone-type { font-size: 11px; font-weight: 600; border: 1px solid; border-radius: 20px; padding: 1px 8px; } + +/* ============ Home Footer ============ */ +.home-footer { border-top: 1px solid var(--border); padding: 40px 0; background: var(--bg-surface); } +.home-footer p { font-size: 14px; color: var(--text-secondary); margin-bottom: 6px; } +.home-footer code { font-family: var(--font-mono); color: var(--accent-2); } +.home-footer a { color: #60a5fa; } +.home-footer a:hover { text-decoration: underline; } +.home-footer-copy { margin-top: 16px; font-size: 13px; color: var(--text-muted); } + +@media (max-width: 640px) { + .hero-title { font-size: 38px; } + .stats-inner { gap: 40px; } + .detail-header-row { flex-direction: column; align-items: flex-start; } +} diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..3cb6d23 --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,82 @@ +@import './tokens.css'; + +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { scroll-behavior: smooth; } + +body { + font-family: var(--font-sans); + background: var(--bg-base); + color: var(--text-primary); + line-height: 1.6; + -webkit-font-smoothing: antialiased; +} + +a { color: inherit; text-decoration: none; } + +img { max-width: 100%; display: block; } + +::-webkit-scrollbar { width: 6px; } +::-webkit-scrollbar-track { background: var(--bg-base); } +::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } + +/* 유틸 */ +.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; } +.badge { + display: inline-flex; align-items: center; gap: 6px; + padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 600; + letter-spacing: 0.5px; text-transform: uppercase; +} +.badge-live { background: rgba(16,185,129,0.15); color: #10b981; border: 1px solid rgba(16,185,129,0.3); } +.badge-soon { background: rgba(251,191,36,0.15); color: #fbbf24; border: 1px solid rgba(251,191,36,0.3); } +.badge-mobile { background: rgba(99,102,241,0.15); color: #818cf8; border: 1px solid rgba(99,102,241,0.3); } + +/* 공통 섹션 */ +.section { padding: 80px 0; } +.section-title { font-size: 28px; font-weight: 700; margin-bottom: 8px; } +.section-sub { color: var(--text-muted); font-size: 15px; margin-bottom: 40px; } + +/* 코드 블록 */ +.code-block { + background: rgba(0,0,0,0.5); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 16px 20px; + font-family: var(--font-mono); + font-size: 13px; + color: #a5f3fc; + overflow-x: auto; + line-height: 1.7; +} +.code-block .comment { color: #475569; } +.code-block .cmd { color: #34d399; } +.code-block .url { color: #f472b6; } + +/* 스텝 */ +.step-list { list-style: none; display: flex; flex-direction: column; gap: 16px; } +.step-item { + display: grid; grid-template-columns: 40px 1fr; gap: 16px; align-items: start; +} +.step-num { + width: 40px; height: 40px; border-radius: 50%; + background: var(--accent-grad); display: flex; align-items: center; justify-content: center; + font-size: 14px; font-weight: 700; flex-shrink: 0; +} +.step-body h4 { font-size: 15px; font-weight: 600; margin-bottom: 4px; } +.step-body p { font-size: 14px; color: var(--text-muted); } + +/* 모바일 프레임 */ +.phone-frame { + width: 240px; height: 480px; + border-radius: 32px; + border: 2px solid var(--border); + background: #0f172a; + overflow: hidden; + position: relative; + box-shadow: 0 20px 60px rgba(0,0,0,0.6); +} +.phone-notch { + position: absolute; top: 0; left: 50%; transform: translateX(-50%); + width: 100px; height: 24px; background: #000; border-radius: 0 0 16px 16px; z-index: 2; +} +.phone-screen { width: 100%; height: 100%; overflow: hidden; } diff --git a/src/styles/tokens.css b/src/styles/tokens.css new file mode 100644 index 0000000..3ab4b77 --- /dev/null +++ b/src/styles/tokens.css @@ -0,0 +1,29 @@ +:root { + --bg-base: #060b18; + --bg-surface: #0d1525; + --bg-card: rgba(255, 255, 255, 0.04); + --bg-card-hover: rgba(255, 255, 255, 0.08); + --border: rgba(255, 255, 255, 0.10); + --border-accent: rgba(99, 102, 241, 0.40); + + --accent-1: #6366f1; + --accent-2: #8b5cf6; + --accent-grad: linear-gradient(135deg, #6366f1, #8b5cf6); + + --text-primary: #f1f5f9; + --text-secondary:#cbd5e1; + --text-muted: #64748b; + + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 20px; + --radius-card:16px; + + --shadow-card: 0 4px 32px rgba(0, 0, 0, 0.5); + --shadow-glow: 0 0 40px rgba(99, 102, 241, 0.15); + + --font-sans: 'Pretendard', 'Apple SD Gothic Neo', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; + + --transition: 0.2s ease; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1538675 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..b634892 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + build: { outDir: 'dist' }, + server: { port: 3020 } +})