[CLAUDE] Scaffold: khoi tao SOLUTION_ERP Phase 0

- .NET 10 Clean Architecture: Domain/Application/Infrastructure/Api (4 project)
- 2 React + Vite + TS app: fe-admin (:8082), fe-user (:8080) voi proxy /api
- Node engines >=20, .nvmrc = 20 cho CI (bai hoc NamGroup)
- SQL Server 2022 qua docker-compose (dev)
- Parse 8 FORM -> docs/forms-spec.md (catalog + ma HD format RG-001)
- Parse QUY_TRINH -> docs/workflow-contract.md (9 phase state machine + role matrix)
- docs: CLAUDE.md, STATUS.md, PROJECT-MAP.md, migration-todos.md (roadmap 5 phase)
- .claude/skills: 3 placeholder (contract-workflow, form-engine, permission-matrix)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-21 10:37:34 +07:00
commit 25dad7f36f
69 changed files with 3616 additions and 0 deletions

55
.claude/skills/README.md Normal file
View File

@ -0,0 +1,55 @@
# Skill Library — SOLUTION_ERP
Skill này là tài liệu chuyên biệt để Claude (và developer khác) dùng khi cần deep-dive 1 domain area. Claude tự động invoke qua Skill tool dựa trên semantic matching với `description` trong từng `SKILL.md`.
## Skills hiện có
| Skill | Mục đích | Trigger ví dụ | Trạng thái |
|---|---|---|---|
| `contract-workflow` | State machine 9 phase, role × phase guard, SLA timer, auto-approve | "approve contract", "chuyển phase", "auto-approve quá hạn" | 📝 Placeholder (Phase 3) |
| `form-engine` | Render template docx/xlsx, parse 8 form, field mapping, PO generator | "export contract as word", "điền form", "render template" | 📝 Placeholder (Phase 2) |
| `permission-matrix` | Role × MenuKey × CRUD, seed, reset password, 3-layer resolution | "permission denied", "gán role", "menu không hiện" | 📝 Placeholder (Phase 1) |
## Format chuẩn 1 skill
Mỗi skill là 1 folder với ít nhất `SKILL.md` + optional `examples/` + `references/`:
```
.claude/skills/<skill-name>/
├── SKILL.md ← Entry point: description, when-to-use, workflow
├── examples/ ← Code snippets mẫu
│ └── *.cs | *.tsx
└── references/ ← Link đến file code thật, docs
```
**Frontmatter `SKILL.md`:**
```markdown
---
name: contract-workflow
description: State machine 9 phase cho hợp đồng — guard rule, SLA, auto-approve
when-to-use:
- "approve contract"
- "state machine bug"
- "SLA expired"
---
# Contract Workflow Skill
## Context
...
## Workflow
...
## Code pointers
- `src/Backend/SolutionErp.Domain/Contracts/ContractPhase.cs`
- `src/Backend/SolutionErp.Application/Contracts/Commands/TransitionContractCommand.cs`
```
## Tạo skill mới — checklist
1. Tạo folder `.claude/skills/<kebab-case-name>/`
2. Viết `SKILL.md` với frontmatter + sections: Context / Workflow / Code pointers / Common pitfalls
3. Add row vào bảng "Skills hiện có" phía trên
4. Commit `[CLAUDE] Skill: add <name>`

View File

@ -0,0 +1,34 @@
---
name: contract-workflow
description: State machine 9 phase cho hợp đồng TP/NCC/Tổ đội — guard rule, SLA auto-approve, role × phase matrix. Dùng khi debug transition, approve HĐ, xử lý HĐ quá hạn.
when-to-use:
- "transition contract"
- "chuyển phase hợp đồng"
- "HĐ quá hạn auto-approve"
- "role không duyệt được"
- "reject contract về draft"
---
# Contract Workflow Skill
> **Phase 3 deliverable.** Hiện tại skill này là PLACEHOLDER — sẽ được expand khi implement Phase 3.
## Context
Xem đầy đủ ở [`docs/workflow-contract.md`](../../../docs/workflow-contract.md):
- 9 state: `DangChon``DangSoanThao``DangGopY``DangDamPhan``DangInKy``DangKiemTraCCM``DangTrinhKy``DangDongDau``DaPhatHanh` (+ `TuChoi`)
- SLA mỗi phase: Draft 7d, GópÝ 7d, ĐàmPhán 7d, InKý 1d, CCMCheck 3d, BOD 1d
- Role × Phase matrix (Drafter, TBP/TPB, PD/PM, PRO/EQU/FIN/ACT, CCM, BOD/NĐUQ, HRA)
## Code pointers (sẽ có sau Phase 3)
- `src/Backend/SolutionErp.Domain/Contracts/ContractPhase.cs` (enum)
- `src/Backend/SolutionErp.Domain/Contracts/Contract.cs` (aggregate root)
- `src/Backend/SolutionErp.Application/Contracts/Services/IContractWorkflowService.cs`
- `src/Backend/SolutionErp.Infrastructure/HostedServices/SlaExpiryJob.cs`
## Common pitfalls (dự kiến — update khi build)
- Không check bypass flag khi HĐ với Chủ đầu tư → sẽ reject oan ở CCM phase
- Gen mã HĐ trước khi BOD approve → có thể waste số thứ tự nếu reject sau đó
- Auto-approve chạy trong transaction dài → lock table → timeout

View File

@ -0,0 +1,34 @@
---
name: form-engine
description: Template engine render 8 form hợp đồng ra .docx/.xlsx giống 100% mẫu gốc. Gen mã HĐ theo RG-001. Dùng khi export HĐ, upload template mới, debug render lỗi format.
when-to-use:
- "export contract to word"
- "render template docx"
- "xuất đơn đặt hàng excel"
- "gen mã hợp đồng"
- "upload template mới"
---
# Form Engine Skill
> **Phase 2 deliverable.** Hiện tại skill này là PLACEHOLDER.
## Context
Xem đầy đủ ở [`docs/forms-spec.md`](../../../docs/forms-spec.md):
- 8 form (6 .docx/.doc + 1 .xlsx + 1 .docx quy định)
- Mã HĐ format theo SOL-CCM-RG-001: `{Project}/{Type}/SOL&{Partner}/{Seq}` và biến thể
- 3 file `.doc` cần convert qua Word COM / LibreOffice headless trước khi parse
## Tech stack dự kiến
- **.docx render:** DocumentFormat.OpenXml (free, verbose) hoặc Aspose.Words (phí, dễ)
- **.xlsx render:** EPPlus (free non-commercial) hoặc ClosedXML (free)
- **PDF preview:** wkhtmltopdf hoặc LibreOffice `--convert-to pdf`
## Code pointers (sẽ có sau Phase 2)
- `src/Backend/SolutionErp.Application/Forms/Services/IFormRenderer.cs`
- `src/Backend/SolutionErp.Infrastructure/Forms/DocxRenderer.cs`
- `src/Backend/SolutionErp.Infrastructure/Forms/XlsxRenderer.cs`
- `src/Backend/SolutionErp.Infrastructure/Services/ContractCodeGenerator.cs`

View File

@ -0,0 +1,41 @@
---
name: permission-matrix
description: Hệ thống phân quyền Role × MenuKey × CRUD. Sidebar gating, permission guard, seed default, reset password. Dùng khi debug access denied, gán role, menu không hiện.
when-to-use:
- "permission denied"
- "access denied"
- "menu không hiện"
- "gán role cho user"
- "reset password"
- "seed permission"
---
# Permission Matrix Skill
> **Phase 1 deliverable.** Hiện tại skill này là PLACEHOLDER.
## Context
Pattern copy từ **NamGroup** skill `permission-system` nhưng đơn giản hóa:
- 1 User có N Role
- 1 Role có ma trận (MenuKey, CRUD flags) — `Permission` table
- Không có per-user override (giữ đơn giản cho Phase 1)
- Menu tree flat 2 cấp, hardcode `MenuKey`
## Tech
- BE: `[Authorize(Policy = "Menu.Read")]` attribute
- FE: `<PermissionGuard menuKey="Contracts" action="Update">` + `usePermission().can("Contracts", "Update")`
- Resolution: API `/api/menus/me` trả về tree + permissions đã resolved theo user's roles
## Code pointers (sẽ có sau Phase 1)
- `src/Backend/SolutionErp.Domain/Identity/Permission.cs`
- `src/Backend/SolutionErp.Application/Permissions/Queries/GetMyMenuTreeQuery.cs`
- `fe-admin/src/components/PermissionGuard.tsx`
- `fe-admin/src/hooks/usePermission.ts`
## Common pitfalls (dự kiến)
- Quên refresh token sau khi admin update permission → user phải logout/login mới thấy
- MenuKey hardcode dễ typo → tập trung vào file `src/lib/menuKeys.ts` (FE) + `MenuKeys.cs` (BE const)

79
.gitignore vendored Normal file
View File

@ -0,0 +1,79 @@
# =============================================================
# .NET
# =============================================================
bin/
obj/
out/
*.user
*.suo
*.ide/
*.VisualState.xml
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
.vs/
project.lock.json
project.fragment.lock.json
artifacts/
# EF Core migrations scratch
src/**/Migrations/*.Designer.cs.bak
# =============================================================
# Node / React / Vite
# =============================================================
node_modules/
dist/
dist-ssr/
.vite/
*.local
coverage/
.eslintcache
# =============================================================
# Logs
# =============================================================
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# =============================================================
# Editor / OS
# =============================================================
.idea/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
Thumbs.db
desktop.ini
.DS_Store
# =============================================================
# Secrets / env
# =============================================================
appsettings.*.Local.json
appsettings.Production.json
*.env
.env.local
.env.*.local
src/Backend/SolutionErp.Api/.env
# =============================================================
# Build artifacts / uploads
# =============================================================
src/Backend/SolutionErp.Api/wwwroot/uploads/
src/Backend/SolutionErp.Api/wwwroot/exports/
# =============================================================
# Claude / skills (keep tracked — these ARE source of truth)
# =============================================================
# .claude/ ← DO NOT ignore, skills are committed
!.claude/
!.claude/**

80
CLAUDE.md Normal file
View File

@ -0,0 +1,80 @@
# CLAUDE.md — AI Agent Context
> **Full content:** [`docs/CLAUDE.md`](docs/CLAUDE.md)
---
**SOLUTION_ERP** — Hệ thống Quản lý Hợp đồng Nhà cung cấp / Thầu phụ / Tổ đội cho Công ty TNHH Xây dựng Solutions.
Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Server + IIS**.
## 🚀 BẮT ĐẦU SESSION — 4 file đọc trước tiên
```
1. docs/STATUS.md ← Snapshot HIỆN TẠI (phase nào, việc gì đang làm)
2. docs/PROJECT-MAP.md ← Bản đồ toàn cảnh (nên đọc đầu dự án)
3. docs/changelog/migration-todos.md ← Atomic tasks theo từng phase
4. docs/workflow-contract.md ← ⭐ State machine 9 phase — ảnh hưởng mọi thiết kế
```
## ⚡ Quick Rules
### Backend — `.NET 10` Clean Architecture
- Solution: `SolutionErp.slnx` ở root, projects ở `src/Backend/`
- **4 layer:** `Api → Application ← Domain` + `Infrastructure → Application`
- Pattern: **CQRS + MediatR**, **FluentValidation**, **AutoMapper**
- Repository qua `IApplicationDbContext` interface (Application layer)
- Auth: **JWT Bearer + ASP.NET Identity**
- DB: **SQL Server** (LocalDB dev / SQL Server prod), **EF Core 10 Code-First migrations**
- Error handling: `GlobalExceptionMiddleware` map exception → HTTP status
- Commit scope tech stack: `Api` · `App` · `Domain` · `Infra`
### Frontend — 2 app React 18 + Vite + TS + shadcn/ui + TanStack Query
- `fe-admin/` (port **8082**) · `fe-user/` (port **8080**)
- Vite proxy `/api → http://localhost:5443` (SolutionErp.Api)
- **Named export**, không default export (trừ `App`)
- shadcn/ui copy-paste, **duplicate giữa 2 app là CÓ CHỦ ĐÍCH** (mỗi app UX riêng)
- Auth context: `solution-erp-admin-token` / `solution-erp-user-token``localStorage`
- TanStack Query cho data fetching
- **Node pin `>=20`** trong `engines`; CI pin `20.x` qua `.nvmrc` (bài học NamGroup — KHÔNG dùng Node latest trên CI)
- UI **100% tiếng Việt**
### Database conventions
- Schema: `dbo` (single schema)
- Table: **PascalCase tiếng Anh** (Contracts, Suppliers, Projects, ContractApprovals, ...)
- PK: `Id` (Guid), FK: `{Entity}Id`
- Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`)
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
### Commit convention
```
[CLAUDE] <scope>: <imperative message>
```
**Scope:** `Contract` · `Form` · `Workflow` · `Supplier` · `Auth` · `Admin` · `Api` · `App` · `Domain` · `Infra` · `FE-Admin` · `FE-User` · `Docs` · `CICD` · `Scripts`
## 📖 Tài liệu quan trọng
| File | Nội dung |
|---|---|
| [`docs/STATUS.md`](docs/STATUS.md) | **🔥 Current state** — đọc đầu tiên |
| [`docs/PROJECT-MAP.md`](docs/PROJECT-MAP.md) | Bản đồ tổng quan |
| [`docs/changelog/migration-todos.md`](docs/changelog/migration-todos.md) | Roadmap 5 phase + atomic tasks |
| [`docs/CLAUDE.md`](docs/CLAUDE.md) | Full context — tech stack, architecture |
| [`docs/workflow-contract.md`](docs/workflow-contract.md) | State machine 9 phase + role matrix |
| [`docs/forms-spec.md`](docs/forms-spec.md) | Catalog 8 form + quy định mã HĐ |
## ⚠️ Kết thúc session
1. Update `docs/STATUS.md` (`In Progress``Recently Done`)
2. Tick checklist tương ứng trong `docs/changelog/migration-todos.md`
3. Tạo session log `docs/changelog/sessions/YYYY-MM-DD-HHMM-{topic}.md` nếu đáng ghi
4. Commit `[CLAUDE] <scope>: <message>`
5. ⚠️ **Update `SolutionErp.slnx`** nếu có `.cs/.csproj` mới
> Bỏ qua nếu chỉ trả lời câu hỏi, không sửa file nào.

68
README.md Normal file
View File

@ -0,0 +1,68 @@
# SOLUTION_ERP
Hệ thống quản lý Hợp đồng Nhà cung cấp / Thầu phụ / Tổ đội cho **Công ty TNHH Xây dựng Solutions**.
> 📘 AI context: [`CLAUDE.md`](CLAUDE.md) (pointer) → [`docs/CLAUDE.md`](docs/CLAUDE.md) (full)
## Quick start (dev)
**Yêu cầu:** .NET 10 SDK, Node 20+, SQL Server (local hoặc qua Docker).
```powershell
# 1. DB — chạy SQL Server qua Docker (nếu chưa có local)
docker compose up -d
# 2. Backend — migration + run Api (port 5443)
dotnet ef database update --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api
dotnet run --project src/Backend/SolutionErp.Api
# 3. Frontend admin (port 8082) — terminal mới
cd fe-admin
npm install
npm run dev
# 4. Frontend user (port 8080) — terminal mới
cd fe-user
npm install
npm run dev
```
Admin mặc định (sẽ seed sau Phase 1): `admin@solutionerp.local` / `Admin@123456`
## Architecture
```
fe-admin (:8082) fe-user (:8080)
│ │
└────── /api proxy ──────┘
SolutionErp.Api (:5443)
┌──────────────┼──────────────┐
▼ ▼ ▼
Application Domain Infrastructure ── SQL Server
```
## Tech stack
- **Backend:** .NET 10 + Clean Architecture + CQRS (MediatR) + FluentValidation + AutoMapper + EF Core + ASP.NET Identity + JWT
- **Frontend:** React 18 + Vite + TypeScript + Tailwind + shadcn/ui + TanStack Query
- **DB:** SQL Server 2022
- **Deploy:** Windows Server + IIS
## Roadmap
| Phase | Tuần | Focus |
|---|---|---|
| 0 Draft | T1 | Scaffold, parse FORM + QUY_TRINH |
| 1 Alpha Core | T2-4 | Auth, Permission, CRUD master |
| 2 Form Engine | T5-6 | Render template docx/xlsx |
| 3 Workflow | T7-9 | State machine 9 phase |
| 4 Report + Polish | T10-11 | Dashboard + Excel export |
| 5 Production | T12-13 | CI/CD IIS, UAT, go-live |
Chi tiết ở [`docs/changelog/migration-todos.md`](docs/changelog/migration-todos.md).
## License
Proprietary — Công ty TNHH Xây dựng Solutions.

9
SolutionErp.slnx Normal file
View File

@ -0,0 +1,9 @@
<Solution>
<Folder Name="/src/" />
<Folder Name="/src/Backend/">
<Project Path="src/Backend/SolutionErp.Api/SolutionErp.Api.csproj" />
<Project Path="src/Backend/SolutionErp.Application/SolutionErp.Application.csproj" />
<Project Path="src/Backend/SolutionErp.Domain/SolutionErp.Domain.csproj" />
<Project Path="src/Backend/SolutionErp.Infrastructure/SolutionErp.Infrastructure.csproj" />
</Folder>
</Solution>

16
docker-compose.yml Normal file
View File

@ -0,0 +1,16 @@
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: solution-erp-sql
environment:
- ACCEPT_EULA=Y
- MSSQL_SA_PASSWORD=SolutionErp@2026
- MSSQL_PID=Developer
ports:
- "1433:1433"
volumes:
- sqldata:/var/opt/mssql
restart: unless-stopped
volumes:
sqldata:

139
docs/CLAUDE.md Normal file
View File

@ -0,0 +1,139 @@
# CLAUDE.md — Full Context (SOLUTION_ERP)
## 1. Giới thiệu
**SOLUTION_ERP** là hệ thống quản lý Hợp đồng Nhà cung cấp / Thầu phụ / Tổ đội / Dịch vụ cho **Công ty TNHH Xây dựng Solutions**, thay thế quy trình giấy / Word+Excel hiện tại.
**Scope chính:**
1. Số hóa 8 form hợp đồng (template engine điền field → export .docx/.xlsx)
2. Workflow 9 phase trình ký (state machine + role guard + SLA auto-approve)
3. Tự gen mã HĐ theo Quy định SOL-CCM-RG-001
4. Dashboard báo cáo HĐ theo NCC, dự án, trạng thái, giá trị
## 2. Kiến trúc tổng thể
```
┌────────────────┐ ┌────────────────┐
│ fe-admin │ │ fe-user │ React 18 + Vite + TS + shadcn/ui
│ :8082 │ │ :8080 │ TanStack Query, Tailwind
└────────┬───────┘ └────────┬───────┘
│ │
│ Vite proxy /api → :5443
▼ ▼
┌──────────────────────────────────────┐
│ SolutionErp.Api :5443 │ ASP.NET Core 10 + JWT
│ Controllers / Middleware / Swagger │
└──────────────┬───────────────────────┘
┌────────▼─────────┐
│ SolutionErp. │ CQRS + MediatR
│ Application │ FluentValidation + AutoMapper
└────────┬─────────┘
┌────────▼─────────┐ ← SolutionErp.Infrastructure
│ SolutionErp. │ (EF Core, Identity, Repos, External services)
│ Domain │
└──────────────────┘
┌────────────────┐
│ SQL Server │ dbo schema, GUID PK
└────────────────┘
```
## 3. Project layout
```
SOLUTION_ERP/
├── src/Backend/
│ ├── SolutionErp.Domain/ Entities, ValueObjects, Enums, DomainEvents
│ ├── SolutionErp.Application/ CQRS handlers, DTOs, Validators, Mappings, Interfaces
│ ├── SolutionErp.Infrastructure/ EF DbContext, Identity, Repositories, External
│ └── SolutionErp.Api/ Controllers, Middleware, Program.cs, appsettings
├── fe-admin/ (admin UI — CRUD master data, approve, dashboards)
│ └── src/
├── fe-user/ (user UI — soạn thảo HĐ, comment, upload)
│ └── src/
├── docs/
│ ├── CLAUDE.md (file này)
│ ├── STATUS.md
│ ├── PROJECT-MAP.md
│ ├── forms-spec.md ⭐ 8 form catalog + RG-001 code format
│ ├── workflow-contract.md ⭐ 9 phase state machine + role matrix
│ ├── guides/ setup, cicd, code-rules...
│ ├── database/ schema docs
│ └── changelog/
│ ├── migration-todos.md Roadmap 5 phase
│ └── sessions/ YYYY-MM-DD session logs
├── .claude/skills/ Skill library (contract-workflow, form-engine, ...)
├── scripts/ parse_forms.py, parse_workflow.py, seed, deploy
├── SolutionErp.slnx Solution file
├── global.json .NET 10 SDK pin
├── docker-compose.yml SQL Server cho dev
└── CLAUDE.md Pointer → docs/CLAUDE.md
```
## 4. Tech stack chi tiết
| Layer | Tech | Version |
|---|---|---|
| .NET SDK | .NET | 10.0.104 |
| ORM | EF Core | 10 |
| DB | SQL Server | 2019+ (LocalDB dev) |
| API | ASP.NET Core Web API | 10 |
| Mediator | MediatR | latest |
| Validation | FluentValidation | latest |
| Mapping | AutoMapper | latest |
| Auth | JWT Bearer + ASP.NET Identity | 10 |
| Logging | Serilog | latest |
| OpenAPI | Swashbuckle | latest |
| FE Build | Vite | 8 |
| FE Lib | React | 19 (auto-scaffolded) |
| FE Lang | TypeScript | 6 |
| FE Styling | Tailwind CSS + shadcn/ui | Phase 1 install |
| FE Data | TanStack Query | Phase 1 install |
| Node (local) | >= 20 | local có thể Node 22 |
| Node (CI) | **pin 20.x** | bài học từ NamGroup |
## 5. Reference projects
- **NamGroup** (`D:\Dropbox\CONG_VIEC\NAMGROUP\SOURCECODE_CÔNG_TY\NAMGROUP\`) — template 2 FE + IIS deploy + permission matrix
- **DH_Y_DUOC** (`D:\Dropbox\CONG_VIEC\DAI_Y_DUOC\DH_Y_DUOC_SOURCECODE\DH_Y_DUOC\`) — template backend hiện đại (Clean Arch + CQRS + EF migrations)
## 6. Roadmap 5 phase (summary)
| Phase | Tuần | Focus | Exit criteria |
|---|---|---|---|
| **0 Draft** | T1 | Scaffold, parse FORM + QUY_TRINH | Build pass, docs đủ |
| **1 Alpha Core** | T2-4 | Auth + Permission + CRUD Supplier/Project/Contract | Admin tạo HĐ draft + gán role |
| **2 Form Engine** | T5-6 | Template engine 8 form, export .docx/.xlsx | Export 1 HĐ giống mẫu 100% |
| **3 Workflow** | T7-9 | State machine 9 phase + SLA + notify | HĐ đi hết quy trình → có mã số |
| **4 Report + Polish** | T10-11 | Dashboard, Excel export, UX pass | UAT 5-10 HĐ thật |
| **5 Production** | T12-13 | CI/CD IIS, security, runbook, UAT | Go-live |
Chi tiết atomic tasks ở [`changelog/migration-todos.md`](changelog/migration-todos.md).
## 7. Quy ước code
- **Ngôn ngữ UI:** 100% tiếng Việt. Code + comment + tên DB table: **English**.
- **Tên file:** PascalCase cho `.cs`, kebab-case cho TS/TSX (trừ React component = PascalCase).
- **Null safety:** `.NET` bật nullable reference types. TS `strict: true`.
- **Async/await** cho mọi I/O.
- **Error handling BE:** `GlobalExceptionMiddleware` → map exception → ProblemDetails. KHÔNG try-catch ở controller.
- **Error handling FE:** error boundary + toast. Dùng `useErrorHandler` hook.
- **Logging:** Serilog structured. Không `Console.WriteLine`.
## 8. Security baseline
- HTTPS everywhere (IIS redirect HTTP → HTTPS)
- JWT expiry 1h, refresh token 7d
- Password hash: Identity default (PBKDF2)
- CORS: whitelist chỉ 2 FE origin
- Rate limit: 100 req/min/IP cho endpoint auth
- SQL injection: EF Core parameterized (không raw SQL trừ khi cần)
- Audit log: mọi write → `AuditLog` table
## 9. Liên hệ
- **Dev:** pqhuy1987@gmail.com (solo)
- **Domain expert:** TBD (sẽ update sau UAT đầu tiên)

154
docs/PROJECT-MAP.md Normal file
View File

@ -0,0 +1,154 @@
# PROJECT-MAP — Bản đồ toàn cảnh
> Đọc file này nếu cần deep context (~15 phút). Nếu chỉ cần snapshot → đọc [`STATUS.md`](STATUS.md).
## Module map
```
┌─────────────────────────────────────────────────────────────────┐
│ SOLUTION_ERP │
└─────────────────────────────────────────────────────────────────┘
╔════════════════╗ ╔════════════════╗ ╔════════════════╗
║ IDENTITY ║ ║ DANH MỤC ║ ║ QUẢN LÝ HĐ ║
║ (Phase 1) ║ ║ (Phase 1) ║ ║ (Phase 1-3) ║
╠════════════════╣ ╠════════════════╣ ╠════════════════╣
║ User ║ ║ Supplier ║ ║ Contract ║
║ Role ║ ║ Project ║ ║ ContractType ║
║ Permission ║ ║ Department ║ ║ ContractForm ║
║ MenuKey ║ ║ ContractClause ║ ║ Approval ║
║ AuditLog ║ ║ (điều kiện ║ ║ Comment ║
║ ║ ║ chung - 002.04)║ ║ Attachment ║
╚════════════════╝ ╚════════════════╝ ║ AuditTrail ║
╚════════════════╝
╔════════════════╗ ╔════════════════╗ ╔════════════════╗
║ FORM ENGINE ║ ║ WORKFLOW ║ ║ BÁO CÁO ║
║ (Phase 2) ║ ║ (Phase 3) ║ ║ (Phase 4) ║
╠════════════════╣ ╠════════════════╣ ╠════════════════╣
║ Template ║ ║ StateMachine ║ ║ Dashboard ║
║ Renderer ║ ║ Transition ║ ║ ExcelExport ║
║ (DOCX/XLSX) ║ ║ SlaTimer ║ ║ FilterQuery ║
║ Field mapping ║ ║ Notification ║ ║ ║
║ PO gen (F.07) ║ ║ CodeGenerator ║ ║ ║
║ ║ ║ (RG-001) ║ ║ ║
╚════════════════╝ ╚════════════════╝ ╚════════════════╝
```
## Domain entities chính (dự kiến)
```
User ────< Role ────< Permission (Role × MenuKey × CRUD)
User ────< AuditLog
Supplier (NCC)
Project (Dự án)
Department (Phòng ban)
Contract
├── Type: HĐTP | HĐGK | NCC | HĐDV | HĐ Mua bán | ...
├── Phase (9 state — xem workflow-contract.md)
├── Supplier, Project, Drafter
├── MaHopDong (gen theo RG-001)
├── Approvals[] (audit ai ký phase nào)
├── Comments[] (thread góp ý Phase 3 của workflow)
├── Attachments[] (scan bản gốc, file export)
└── TemplateData (JSON — field đã điền khi render form)
ContractTemplate (ánh xạ Type → File mẫu FO-002.xx)
ContractClause (điều khoản chung FO-002.04 — rich text)
PurchaseOrder (có thể đính với Contract hoặc standalone)
```
## API namespace dự kiến
```
/api/auth — login, refresh, logout, register (admin gate)
/api/users — CRUD user, assign role, reset password
/api/roles — CRUD role, permission matrix
/api/menus — menu tree + permission resolution
/api/suppliers — CRUD NCC
/api/projects — CRUD dự án
/api/departments — CRUD phòng ban
/api/contracts — CRUD + query by phase/project/supplier
/api/contracts/{id}/transitions — state machine action
/api/contracts/{id}/comments — thread góp ý
/api/contracts/{id}/attachments — upload/download
/api/forms — template catalog
/api/forms/{id}/render — render template → docx/xlsx (Phase 2)
/api/reports/dashboard — KPI tổng hợp
/api/reports/export — Excel download
```
## FE screens dự kiến
### fe-admin (:8082) — cho Admin + Role quản lý
- `/login`
- `/dashboard` — KPI system
- `/master/users` + `/master/roles` + `/master/permissions`
- `/master/suppliers` + `/master/projects` + `/master/departments`
- `/master/contract-templates` + `/master/contract-clauses`
- `/contracts` — danh sách toàn bộ, filter phase/dự án
- `/contracts/{id}` — detail + approval panel + audit log
- `/reports` + `/system/audit-log`
### fe-user (:8080) — cho Drafter, TBP, PD/PM, BOD, CCM, ...
- `/login`
- `/inbox` — HĐ đang chờ tôi xử lý (filter theo role × phase)
- `/contracts/new` — chọn template + điền form + submit
- `/contracts/{id}` — detail, comment, approve/reject
- `/my-contracts` — HĐ tôi đã tạo/tham gia
## Flow chính (Phase 3 — trình ký HĐ end-to-end)
```
Drafter (QS/NV.PB)
├─ [POST /api/contracts] tạo draft + chọn template
├─ [POST /api/forms/{id}/render] fill + preview
├─ [POST /api/contracts/{id}/transitions] DangSoanThao → DangGopY
PD/PM/PRO/CCM/FIN/ACT (song song)
└─ [POST /api/contracts/{id}/comments] góp ý
Drafter
├─ [PATCH /api/contracts/{id}] revise
├─ [POST /api/contracts/{id}/transitions] DangGopY → DangDamPhan → DangInKy
NTP/NCC/TĐ (external — Drafter update thay)
└─ upload scan có chữ ký đối tác
Drafter
└─ [POST /api/contracts/{id}/transitions] → DangKiemTraCCM
CCM
└─ [POST /api/contracts/{id}/transitions] → DangTrinhKy
BOD/NĐUQ
└─ [POST /api/contracts/{id}/transitions] → DangDongDau (GEN mã HĐ ở đây!)
HRA
└─ [POST /api/contracts/{id}/transitions] → DaPhatHanh (upload scan có dấu)
```
## External dependencies
- **SQL Server** — chính thức, dev có thể LocalDB hoặc Docker (`docker-compose.yml`)
- **IIS** — deploy target (Windows Server 2019+)
- **Gitea** — git remote (URL chờ user cấp)
- **Aspose.Words / DocumentFormat.OpenXml** — render .docx (Phase 2, quyết định khi đó)
- **EPPlus hoặc ClosedXML** — render .xlsx (Phase 2)
## Non-goals (KHÔNG làm)
- ❌ Python AI service (user đã quyết gác vô thời hạn)
- ❌ Mobile app
- ❌ Multi-tenant (1 instance / 1 công ty)
- ❌ Tích hợp e-signature (Phase 6+ nếu có)
- ❌ Tích hợp SAP/Bravo ERP (Phase 6+ nếu có)
- ❌ Public API / external webhooks

55
docs/STATUS.md Normal file
View File

@ -0,0 +1,55 @@
# STATUS — Snapshot hiện tại
> **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`.
**Last updated:** 2026-04-21
## 📍 Phase hiện tại: **Phase 0 — Draft Scaffold** (in progress)
## 🔥 In Progress
| Ai | Task | Bắt đầu |
|---|---|---|
| Claude | Hoàn tất Phase 0 — docs + git init | 2026-04-21 |
## ✅ Recently Done (newest on top)
| Ngày | Ai | Task | Commit |
|---|---|---|---|
| 2026-04-21 | Claude | Parse QUY_TRINH → `workflow-contract.md` (9 phase state machine + role matrix) | — |
| 2026-04-21 | Claude | Parse 8 FORM → `forms-spec.md` (catalog + RG-001 code format) | — |
| 2026-04-21 | Claude | Scaffold 2 React + Vite apps (fe-admin :8082, fe-user :8080) + proxy config + Node engines pin | — |
| 2026-04-21 | Claude | Scaffold .NET 10 solution + 4 project (Domain/Application/Infrastructure/Api) + references + packages | — |
| 2026-04-21 | Claude | Tạo cấu trúc thư mục `SOLUTION_ERP/` + MEMORY.md | — |
## 🎯 Next up (Phase 0 còn lại)
- [ ] Tạo file root: `.gitignore`, `README.md`, `global.json`, `docker-compose.yml`
- [ ] Tạo 3 skill folder placeholders (`contract-workflow`, `form-engine`, `permission-matrix`)
- [ ] `git init` + commit đầu tiên `[CLAUDE] Scaffold: khởi tạo SOLUTION_ERP Phase 0`
- [ ] Set Gitea remote (khi user cấp URL)
## 📋 Phase 1 — Alpha Core (sắp tới)
Xem chi tiết ở [`changelog/migration-todos.md`](changelog/migration-todos.md) section **Phase 1**.
Summary:
1. Install Tailwind + shadcn/ui cho 2 FE + setup `BrandingProvider`
2. Tạo `BaseEntity`, `AuditableEntity`, `ApplicationDbContext`, migration đầu tiên
3. ASP.NET Identity + JWT endpoint (login, refresh, logout, seed admin)
4. Permission Matrix (Role × MenuKey × CRUD) — copy pattern từ NamGroup skill
5. CRUD Supplier + Project + Contract (draft only, chưa workflow)
6. FE admin: login page + layout + permission guard + 3 CRUD page
7. FE user: login + layout + list HĐ của tôi
## 🚨 Blockers / risks
- **Gitea remote** chưa có URL — chờ user cấp để push code
- **3 file `.doc`** (FO-002.02, .03, .06) không parse được bằng python-docx — cần Word COM hoặc LibreOffice ở Phase 2
- **Node 22 local vs 20 CI** — phải test CI sớm (Phase 5) để tránh surprise
## 📊 Thông số
- LOC: ~0 (scaffold chỉ)
- Build time: ~10s (.NET)
- Test coverage: 0% (chưa có test)

View File

@ -0,0 +1,164 @@
# Migration To-dos — Atomic Roadmap
> Mỗi item là 1 task atomic (~2-8h work). Tick `[x]` khi xong. Link session log nếu có.
## Phase 0 — Draft Scaffold (T1)
- [x] Tạo cấu trúc thư mục `SOLUTION_ERP/`
- [x] Scaffold .NET 10 solution `SolutionErp.slnx`
- [x] Scaffold 4 project: `SolutionErp.{Domain, Application, Infrastructure, Api}`
- [x] Wire Clean Arch references (Api → App/Infra, Infra → App, App → Domain)
- [x] Install NuGet base: MediatR, FluentValidation, AutoMapper, EF Core SqlServer, Identity, JWT, Swagger, Serilog
- [x] Scaffold 2 React + Vite apps `fe-admin` + `fe-user` với TS template
- [x] Config vite.config.ts: port, strictPort, proxy `/api`, alias `@`
- [x] Pin Node `>=20` trong package.json + `.nvmrc` cho CI
- [x] Parse 8 form → `docs/forms-spec.md`
- [x] Parse quy trình → `docs/workflow-contract.md`
- [x] Viết `docs/{CLAUDE,STATUS,PROJECT-MAP}.md`
- [ ] Viết `.gitignore`, `README.md`, `global.json`, `docker-compose.yml`
- [ ] Tạo placeholder skill folders: `contract-workflow`, `form-engine`, `permission-matrix`
- [ ] `git init` + commit đầu
- [ ] Push Gitea remote (chờ URL từ user)
## Phase 1 — Alpha Core (T2-4)
### Backend foundation
- [ ] `Domain/BaseEntity.cs` (Id, CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)
- [ ] `Domain/AuditableEntity : BaseEntity` (IsDeleted, DeletedAt, DeletedBy)
- [ ] `Domain/ValueObjects/ContractCode.cs` (wrap string theo format RG-001)
- [ ] `Domain/Enums/ContractType.cs`, `ContractPhase.cs`, `ApprovalDecision.cs`
- [ ] `Application/Common/IApplicationDbContext.cs` interface
- [ ] `Application/Common/IDateTime.cs`, `ICurrentUser.cs`
- [ ] `Application/DependencyInjection.cs` — register MediatR, FluentValidation, AutoMapper
- [ ] `Infrastructure/Persistence/ApplicationDbContext.cs` : `IdentityDbContext<User, Role, Guid>`, `IApplicationDbContext`
- [ ] Configurations per entity qua `IEntityTypeConfiguration<T>`
- [ ] `Infrastructure/DependencyInjection.cs` — register DbContext, Identity, services
- [ ] `Api/Program.cs` setup: services, Serilog, auth, Swagger, CORS, middleware
- [ ] `Api/Middleware/GlobalExceptionMiddleware.cs`
### Auth + Identity
- [ ] `Domain/Entities/User : IdentityUser<Guid>`, `Role : IdentityRole<Guid>`
- [ ] Migration 1: `Init` (Identity tables)
- [ ] `Application/Auth/Commands/LoginCommand` + handler + validator
- [ ] `Application/Auth/Commands/RefreshTokenCommand`
- [ ] `Api/Controllers/AuthController` (login, refresh, logout, me)
- [ ] JWT config: issuer, audience, key, expiry 1h + refresh 7d
- [ ] Seed admin: `admin@solutionerp.local` / `Admin@123456`
- [ ] Test login → get token → call `/me` OK
### Permission Matrix
- [ ] `Domain/Entities/MenuItem` (Key, Label, ParentKey, Order, Icon)
- [ ] `Domain/Entities/Permission` (RoleId, MenuKey, CanRead, CanCreate, CanUpdate, CanDelete)
- [ ] Seed default menu tree (based on FE screens list)
- [ ] `Application/Permissions/Queries/GetMyMenuTree` — resolve per-user
- [ ] `Api/Controllers/MenusController` + `RolesController` + `PermissionsController`
- [ ] Admin UI: Permission Matrix grid (role × menu × CRUD checkbox)
### CRUD master data
- [ ] `Domain/Entities/Supplier` (Code, Name, TaxCode, Phone, Email, Address, Type: NCC/NTP/TĐ/ĐVDV)
- [ ] `Domain/Entities/Project` (Code, Name, StartDate, EndDate, Manager)
- [ ] `Domain/Entities/Department` (Code, Name, Manager)
- [ ] CQRS + Controller + Migration cho 3 entity
- [ ] FE admin 3 trang CRUD (list, create, edit, delete confirm)
- [ ] Pagination, search, sort server-side
### Contract draft (chưa workflow — chỉ CRUD)
- [ ] `Domain/Entities/Contract` (skeleton: Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData)
- [ ] API create/update/list/delete draft
- [ ] FE admin: list contracts + filter
- [ ] FE user: "HĐ của tôi" list
### FE setup
- [ ] Install Tailwind CSS cho 2 app + config content paths
- [ ] Install shadcn/ui CLI, init 2 app
- [ ] Install: `@tanstack/react-query`, `react-router-dom`, `axios`, `lucide-react`, `sonner`
- [ ] `src/lib/api.ts` — axios instance + interceptor JWT
- [ ] `src/contexts/AuthContext.tsx` — token từ localStorage
- [ ] `src/components/PermissionGuard.tsx` + `usePermission()` hook
- [ ] Layout shell: sidebar + header + content
- [ ] Route với protected route + role guard
- [ ] Toast notifications (sonner)
### Exit criteria Phase 1
- [ ] Admin login → tạo NCC/Project → tạo role "Nhân viên CCM" → gán permission menu "Contracts.Read"
- [ ] User CCM login → thấy menu Contracts, không thấy menu Admin
- [ ] Tạo Contract draft → list hiển thị, không bị 403 sai
## Phase 2 — Form Engine (T5-6)
- [ ] Khảo sát: OpenXml vs Aspose.Words — chọn 1 (Aspose có license phí; OpenXml free nhưng verbose)
- [ ] Convert 3 file `.doc``.docx` (COM automation PowerShell hoặc LibreOffice headless)
- [ ] Parse chi tiết field của 5 template HĐ — mỗi form thành JSON spec
- [ ] `Domain/Entities/ContractTemplate` (Id, FormCode, Name, TemplateFile path, FieldSpec JSON)
- [ ] `Application/Forms/Services/IFormRenderer` — input: template + data dict → output: byte[] (.docx)
- [ ] Implement `DocxRenderer` (OpenXml-based replace placeholder)
- [ ] Implement `XlsxRenderer` cho FO-002.07 (dùng EPPlus/ClosedXML)
- [ ] `Api/Controllers/FormsController` — GET /templates, POST /render
- [ ] FE user: form builder — chọn template → dynamic form → preview → export
- [ ] FE admin: upload template mới, edit field mapping
- [ ] Lưu `ContractClause` (FO-002.04) dạng rich text, admin edit qua TipTap/TinyMCE
- [ ] Import/export template (để backup)
- [ ] Test: 1 HĐ Giao khoán filled → export .docx mở bằng Word y hệt mẫu
## Phase 3 — Workflow State Machine (T7-9)
- [ ] `Domain/Entities/ContractApproval` + `ContractComment` + `ContractAttachment`
- [ ] `Domain/Entities/Contract` update: thêm `Phase`, `SlaDeadline`, `BypassProcurementAndCCM`
- [ ] `Domain/Services/IContractWorkflowService.TransitionAsync(...)` — state guard + role guard + side effects
- [ ] `Infrastructure/Services/ContractCodeGenerator` (implement RG-001) với locking cho seq
- [ ] `Infrastructure/HostedServices/SlaExpiryJob` — check mỗi 15min, auto-approve quá hạn
- [ ] `Infrastructure/Services/NotificationService` — email (MailKit) + in-app (SignalR optional)
- [ ] MediatR behavior: `AuditBehavior` — log mọi command
- [ ] API: `POST /api/contracts/{id}/transitions` body: `{targetPhase, comment}`
- [ ] FE user Inbox: list "HĐ chờ tôi xử lý" (query by current phase + user role)
- [ ] FE Contract detail page: timeline 9 phase, approval panel, comment thread
- [ ] Upload attachment (scan có chữ ký đối tác)
- [ ] Notification UI: badge count, dropdown, click → detail
- [ ] E2E test: happy path end-to-end 1 HĐ qua 9 phase
- [ ] E2E test: reject → quay về DangSoanThao
- [ ] E2E test: SLA expired → auto-approve + log
## Phase 4 — Reporting + Polish (T10-11)
- [ ] Dashboard admin: số HĐ theo phase, top NCC, top dự án, tổng giá trị theo tháng
- [ ] Excel export theo bộ lọc (dùng EPPlus)
- [ ] Report: HĐ quá hạn SLA bao nhiêu lần theo phase/role
- [ ] UX polish: skeleton loader, empty state, error boundary có recovery button
- [ ] Accessibility: keyboard nav, focus trap modal, aria labels
- [ ] Dark mode (optional, nếu rảnh)
- [ ] Performance: index DB cho query hot (SupplierId, ProjectId, Phase)
- [ ] Tài liệu user guide: quy trình tạo HĐ + duyệt
- [ ] UAT với 5-10 HĐ dữ liệu thật từ bộ phận Cung ứng
## Phase 5 — Production (T12-13)
- [ ] `docs/guides/cicd.md` — CI/CD runbook
- [ ] Gitea Actions workflow `.gitea/workflows/deploy.yml` — build .NET + 2 FE, deploy IIS qua SSH/WinRM
- [ ] Pin Node 20.x trong workflow, test CI sớm (không để surprise cuối dự án)
- [ ] `scripts/deploy-iis.ps1` — stop app pool, xcopy, start app pool
- [ ] Windows Server setup: IIS + URL Rewrite + ARR (reverse proxy FE → IIS)
- [ ] SQL Server prod: backup plan daily + weekly full
- [ ] HTTPS certificate (Let's Encrypt qua win-acme hoặc mua cert)
- [ ] `appsettings.Production.json` + user secrets
- [ ] Security audit: owasp top 10 check
- [ ] Rate limiting middleware (AspNetCoreRateLimit hoặc built-in)
- [ ] Health check endpoint `/health` cho IIS probe
- [ ] Error tracking: Serilog → file rolling daily, retention 30 ngày
- [ ] Runbook: restart app, rollback migration, restore backup
- [ ] UAT production 1 tuần với 2-3 user thật
- [ ] Go-live checklist: backup, rollback plan, on-call contact
## Post-launch (Phase 6+ — future)
- [ ] E-signature integration (VNPT CA hoặc FPT CA)
- [ ] Tích hợp Bravo / SAP ERP import NCC
- [ ] Mobile app (React Native?) cho BOD duyệt ngoài giờ
- [ ] AI: gợi ý điền form dựa HĐ cũ, OCR scan HĐ đối tác
- [ ] Multi-tenant nếu có công ty thứ 2

657
docs/forms-spec-raw.md Normal file

File diff suppressed because one or more lines are too long

108
docs/forms-spec.md Normal file
View File

@ -0,0 +1,108 @@
# Forms Specification — SOLUTION_ERP
> **Nguồn:** `D:\Dropbox\CONG_VIEC\SOLUTION\FORM\` (8 file)
> **Raw dump:** [`forms-spec-raw.md`](forms-spec-raw.md) — text gốc extract từ docx/xlsx
> **Phase 2 deliverable:** parse từng form → template engine (điền field tự động → export .docx/.xlsx giống 100% mẫu)
## Danh mục 8 form
| Mã form | Tên | Loại file | Mục đích | Trạng thái parse |
|---|---|---|---|---|
| **SOL-CCM-FO-002.01** | Bảng kiểm tra hợp đồng | `.docx` | Checklist 4 bộ phận (Đề xuất / Cung ứng / CCM / Giám đốc) review HĐ trước ký | ✅ Parsed — table 40×13 |
| **SOL-CCM-FO-002.02** | Hợp đồng trọn gói (nhân công + vật tư) | `.doc` | Template HĐ thầu phụ trọn gói cung cấp cả NC + VT | ⚠️ Cần convert `.doc``.docx` (COM/LibreOffice) ở Phase 2 |
| **SOL-CCM-FO-002.03** | Hợp đồng trọn gói (nhân công + thiết bị) | `.doc` | Template HĐ thầu phụ trọn gói cung cấp NC + TB | ⚠️ Cần convert `.doc``.docx` ở Phase 2 |
| **SOL-CCM-FO-002.04** | Điều kiện chung hợp đồng trọn gói | `.docx` | Phần điều khoản chung (5+ điều: am hiểu TL, nội dung CV, tiến độ, xử lý tranh chấp, sửa đổi HĐ) | ✅ Parsed — narrative text |
| **SOL-CCM-FO-002.05** | Hợp đồng Giao khoán | `.docx` | Template HĐ giao khoán NC/Tổ đội — 10 table (thông tin, giá trị, hạng mục, nghiệm thu, thanh toán) | ✅ Parsed — 10 tables |
| **SOL-CCM-FO-002.06** | Hợp đồng mua bán | `.doc` | Template HĐ mua bán (NCC cấp vật tư, thiết bị) | ⚠️ Cần convert `.doc``.docx` ở Phase 2 |
| **SOL-CCM-FO-002.07** | Đơn đặt hàng (PO) | `.xlsx` | 3 sheet: `(F.01)_SS GIA` (so sánh giá NCC), `PO` (đơn đặt hàng chính), `Po01 NLT (sua)` (PO nguyên liệu) | ✅ Parsed — 3 sheets |
| **SOL-CCM-RG-001** | Quy định mã số hợp đồng | `.docx` | Regex + convention đánh mã HĐ và PO (bảng ví dụ) | ✅ Parsed |
---
## Detail — các form đã parse được
### SOL-CCM-FO-002.01 — Bảng kiểm tra hợp đồng
**Nhóm field thông tin HĐ (phần trên bảng):**
- `phong_ban_du_an` (Phòng ban / Dự án) — string
- `loai_hop_dong` — enum: `ChuDauTu` | `NCC` | `ThauPhu` | `ToDoi` | `ThietBiThue` | `Khac`
- `ten_doi_tac` — string
- `noi_dung_hop_dong` — text
**Nhóm field checklist theo bộ phận (mỗi row = 1 bộ phận × nội dung × ý kiến):**
| Bộ phận | Nội dung kiểm tra (check) | Field dữ liệu |
|---|---|---|
| PHÒNG BAN/DỰ ÁN (Đề xuất) | Soạn thảo (✓), Nội dung HĐ | `de_xuat_soan_thao: bool`, `de_xuat_noi_dung_ok: bool`, `de_xuat_ho_ten`, `de_xuat_ngay`, `de_xuat_y_kien: text` |
| PHÒNG CUNG ỨNG | Điều khoản HĐ, Điều khoản t/toán, Rủi ro pháp lý HĐ | `cung_ung_dieu_khoan: bool`, `cung_ung_thanh_toan: bool`, `cung_ung_rui_ro: bool`, `cung_ung_ho_ten`, `cung_ung_ngay`, `cung_ung_y_kien: text` |
| PHÒNG CCM | G/trị HĐ so với NS, Rủi ro pháp lý HĐ | `ccm_gia_tri_so_ns: bool`, `ccm_rui_ro: bool`, `ccm_ho_ten`, `ccm_ngay`, `ccm_y_kien: text` |
| GIÁM ĐỐC (hoặc ủy quyền) | Ký kết, phát hành | `gd_ky_ket: bool`, `gd_ho_ten`, `gd_ngay`, `gd_y_kien: text` |
**Business rule (từ ghi chú cuối bảng):**
- HĐ với Chủ đầu tư → Đơn vị đề xuất có thể **bypass** Cung ứng + CCM, chuyển thẳng Giám đốc.
- Các loại khác → PHẢI qua Cung ứng + CCM trước.
- Mỗi bộ phận **01 ngày** xử lý. Quá 1 ngày không phản hồi → **auto approve** ("xem như đã thông qua").
- Nếu cần kéo dài → phải ghi chú vào "Ý kiến" + thông báo cho Đề xuất.
**Mapping sang workflow state:** file này chính là thể hiện UI của workflow bên `workflow-contract.md`.
### SOL-CCM-FO-002.04 — Điều kiện chung
Pure narrative template — 5+ điều khoản cố định (AM HIỂU TÀI LIỆU, NỘI DUNG CV, TIẾN ĐỘ, XỬ LÝ TRANH CHẤP, SỬA ĐỔI HĐ). Không có field động — dùng làm **appendix cố định đính kèm** cho HĐ trọn gói.
**Data model:** lưu dạng `ContractClauseTemplate` với `Content: text (markdown)`, version theo đợt cập nhật. Cho phép admin edit qua rich text editor.
### SOL-CCM-FO-002.05 — Hợp đồng Giao khoán
10 bảng. Các nhóm field chính (suy từ số row/col):
- Thông tin 2 bên (Bên A + Bên B) — 4 row × 2 col
- Giá trị HĐ + hạng mục công việc — 8 row × 8 col (chi tiết hạng mục, đơn giá, khối lượng, thành tiền)
- Nghiệm thu + thanh toán — 6 row × 4 col
- Bảng tiến độ — 9 row × 11 col
- Ký tên 2 bên — 1 row × 2 col
**Phase 2:** parse từng table chi tiết sau khi có cấu trúc DB ổn định.
### SOL-CCM-FO-002.07 — Đơn đặt hàng (xlsx)
| Sheet | Rows × Cols | Mục đích |
|---|---|---|
| `(F.01)_SS GIA` | 84 × 36 | So sánh giá nhiều NCC cho cùng mặt hàng (chọn NCC tốt nhất) |
| `PO` | 80 × 17 | Đơn đặt hàng chính — template chuẩn |
| `Po01 NLT (sua)` | 74 × 12 | Variant PO cho NLT (nguyên liệu?) |
**Phase 2:** implement xuất file Excel đúng format — dùng **EPPlus** hoặc **ClosedXML** thay vì OpenXml thủ công (giữ được style, formula, merged cells).
### SOL-CCM-RG-001 — Quy định mã số hợp đồng ⭐ (critical — dùng ở Phase 3)
**Contract code format:**
| Loại HĐ | Format | Ví dụ |
|---|---|---|
| HĐ Thầu phụ (TP) | `{ProjectCode}/HĐTP/SOL&{PartnerAbbr}/{Seq}` | `FLOCK 01/HĐTP/SOL&HD/01` |
| HĐ Giao khoán / Tổ đội / Nhân công cơ hữu | `{ProjectCode}/HĐGK/SOL&{PartnerAbbr}/{Seq}` | `FLOCK 01/HĐGK/SOL&PVL/01` |
| HĐ NCC (cấp riêng từng dự án) | `{ProjectCode}/NCC/SOL&{PartnerAbbr}/{Seq}` | `FLOCK 01/NCC/SOL&CC1/01` |
| HĐ nguyên tắc NCC | `{Year}/NCC/SOL&{PartnerAbbr}/{Seq}` | `2026/NCC/SOL&AKATI/01` |
| HĐ Dịch vụ (cấp riêng) | `{ProjectCode}/HĐDV/SOL&{PartnerAbbr}/{Seq}` | `FLOCK 01/HĐDV/SOL&KG/01` |
| HĐ nguyên tắc Dịch vụ | `{Year}/HĐDV/SOL&{PartnerAbbr}/{Seq}` | `2026/HĐDV/SOL&TTL/01` |
**PO code format:**
| Loại PO | Format | Ví dụ |
|---|---|---|
| PO vật tư, thiết bị (theo HĐ Nguyên tắc) | `{ProjectCode}/{PartnerAbbr}/PO {Seq}` | `FLOCK 01/TRUNGDUNG/PO 01` |
| PO với TP/NCC/ĐVDV (không theo HĐ Nguyên tắc) | `{ProjectCode}/PO/SOL&{PartnerAbbr}/{Seq}` | `FLOCK 01/PO/SOL&PVL/01` |
**Seq** bắt đầu từ `01`, tăng dần theo dự án + loại HĐ + đối tác.
**Domain service Phase 3:** `IContractCodeGenerator.Generate(project, type, partner) → string` — transactional để tránh race condition khi gen Seq.
---
## TODO Phase 2 (form engine)
- [ ] Convert 3 file `.doc` (FO-002.02, 002.03, 002.06) sang `.docx` qua COM automation hoặc LibreOffice headless
- [ ] Parse chi tiết field của 5 file template HĐ (002.02, .03, .04, .05, .06) — mỗi form thành 1 spec JSON
- [ ] Xây template engine: `Template + data → output .docx/.xlsx` (dùng **DocumentFormat.OpenXml** hoặc **Aspose.Words** + **EPPlus**)
- [ ] UI form builder cho admin: gán template ↔ loại HĐ ↔ field layout
- [ ] Preview PDF trước khi export final

156
docs/workflow-contract.md Normal file
View File

@ -0,0 +1,156 @@
# Workflow — Quy trình trình ký Hợp đồng TP/NCC/Tổ đội
> **Nguồn:** `QUY_TRINH/QT TRINH KY HOP DONG TP-NCC.docx`
> **Raw dump:** [`workflow-raw.md`](workflow-raw.md)
> **Phase 3 deliverable:** Implement state machine + role guard + SLA timer + notification
## 1. Phạm vi
Áp dụng cho Hợp đồng/Phụ lục HĐ ký với: **Thầu phụ (NTP)** · **Nhà cung cấp (NCC)** · **Tổ đội (TĐ)**. Tham chiếu ISO 9001.
## 2. Glossary (viết tắt)
| Mã | Nghĩa | Role trong system |
|---|---|---|
| **BOD** | Ban Giám đốc | `Director` |
| **NĐUQ** | Người được ủy quyền | `AuthorizedSigner` |
| **CCM** | Phòng Kiểm soát Chi phí | `CostControl` |
| **PRO** | Phòng Cung ứng | `Procurement` |
| **FIN** | Phòng Tài chính | `Finance` |
| **ACT** | Phòng Kế toán | `Accounting` |
| **EQU** | Phòng Thiết bị | `Equipment` |
| **HRA** | Nhân sự - Hành chính (đóng dấu) | `HR_Admin` |
| **PB** | Phòng ban | `Department` |
| **BCH CT** | Ban chỉ huy công trường | `SiteCommand` |
| **PD** | Giám đốc Thi công | `ProjectDirector` |
| **PM** | Giám đốc Dự án | `ProjectManager` |
| **TPB** | Trưởng Phòng ban | `DeptManager` |
| **TBP** | Trưởng Bộ phận | `SectionLeader` |
| **QS/NV.PB** | QS công trường / Nhân viên PB | `Drafter` (người soạn) |
| **NTP/NCC/TĐ** | Đối tác ký HĐ | Partner (external — không login) |
## 3. State Machine (9 phase)
```mermaid
stateDiagram-v2
[*] --> DangChon: Tạo mới
DangChon --> DangSoanThao: Chọn NCC xong (chuyển Drafter)
DangSoanThao --> DangGopY: Gửi email góp ý (7d)
DangGopY --> DangDamPhan: Nhận xong comment (7d)
DangDamPhan --> DangInKy: Thỏa thuận xong (7d)
DangInKy --> DangKiemTraCCM: Đã ký nháy QS/PD/TPB, chuyển CCM (1d)
DangKiemTraCCM --> DangTrinhKy: CCM ký nháy xong (3d)
DangTrinhKy --> DangDongDau: BOD/NĐUQ ký duyệt (1d)
DangDongDau --> DaPhatHanh: HRA đóng dấu xong
DaPhatHanh --> [*]: PB scan + gửi bản gốc + lưu Filing
DangSoanThao --> TuChoi: Cancel
DangGopY --> DangSoanThao: Revise
DangKiemTraCCM --> DangSoanThao: CCM reject
DangTrinhKy --> DangSoanThao: BOD reject
TuChoi --> [*]
```
## 4. Chi tiết từng phase
| # | Phase (state) | Role thực hiện | Input | Output | SLA | Form liên quan |
|---|---|---|---|---|---|---|
| 1 | `DangChon` — Lựa chọn NTP/NCC | `PB` / `BCH CT` | Yêu cầu công việc | Chốt đối tác | — | (theo Quy trình Cung ứng SOL-PRO-SP-001) |
| 2 | `DangSoanThao` — Soạn thảo HĐ | `Drafter` (QS/NV.PB) + `TBP`/`TPB` check | Template HĐ | Dự thảo HĐ | **7 ngày** | FO-002.02/.03/.04/.05/.06 (chọn loại phù hợp) |
| 3 | `DangGopY` — Góp ý nội dung | `PD`/`PM`/`PRO`/`CCM`/`FIN`/`ACT`/`EQU` | Dự thảo | Comment (email → hệ thống) | **7 ngày** | (đính kèm comment thread) |
| 4 | `DangDamPhan` — Đàm phán | `Drafter` + `TBP`/`TPB`/`PD`/`PM` | Comment | Bản cuối thỏa thuận | **7 ngày** | — |
| 5 | `DangInKy` — In + đối tác ký | `Drafter` + **NTP/NCC/TĐ** (external) + `PD`/`PM`/`TPB` ký nháy | Bản cuối | 2 mặt, có chữ ký đối tác + ký nháy nội bộ | **1 ngày** | FO-002.01 (cover approval) |
| 6 | `DangKiemTraCCM` — CCM kiểm tra | `CCM` (`TP.CCM` ký nháy) | HĐ đã ký nháy | HĐ CCM approved | **3 ngày** | FO-002.01 |
| 7 | `DangTrinhKy` — BOD ký duyệt | `BOD` hoặc `NĐUQ` | HĐ CCM approved | HĐ ký duyệt | **1 ngày** | FO-002.01 |
| 8 | `DangDongDau` — Đóng dấu | `HRA`/`ISO` | HĐ đã ký | HĐ có dấu | — | — |
| 9 | `DaPhatHanh` — Phát hành + lưu trữ | `PB`/`BCH CT` + `CCM` | HĐ có dấu | Scan + bản gốc gửi NCC + lưu Filing System | — | — |
**Tổng SLA:** ~19 ngày (phase 2-7) cho 1 HĐ hoàn chỉnh.
## 5. Role × Phase Matrix (quyền xem/thao tác)
Ký hiệu: `R` = read, `W` = write/update draft, `A` = approve (chuyển phase tiếp), `-` = không có quyền.
| Role \ Phase | 1.Chọn | 2.Soạn | 3.GópÝ | 4.ĐàmPhán | 5.InKý | 6.CCMCheck | 7.BODKý | 8.ĐóngDấu | 9.PhátHành |
|---|---|---|---|---|---|---|---|---|---|
| **Drafter** (QS/NV.PB) | R | W,A | R | W,A | W,A | R | R | R | W |
| **TBP/TPB** | R | R,A | R,A | R,A | R,A | R | R | R | R |
| **PD/PM** | R | R | W,A | R,A | R,A | R | R | R | R |
| **PRO/EQU/FIN/ACT** | A | R | W,A | R | - | - | - | - | R |
| **CCM** | - | R | W,A | R | R | W,A | R | R | W |
| **BOD/NĐUQ** | - | - | - | - | - | - | A | - | R |
| **HRA** | - | - | - | - | - | - | - | W,A | R |
| **Admin** (system) | R | R | R | R | R | R | R | R | R (+ override) |
**Guard rule:**
- Đặc biệt: HĐ với **Chủ đầu tư** → có thể **bypass** phase 3 + 6 (không cần PRO/CCM góp ý + kiểm tra). Cờ `BypassProcurementAndCCM: bool` ở entity HĐ.
- Quá SLA mỗi phase không action → **auto-approve** (chuyển phase tiếp). Gửi notification warning khi còn 2h.
- Nếu cần kéo dài → bộ phận kiểm tra phải comment vào "Ý kiến" trước khi SLA hết.
## 6. Notification triggers
| Event | Người nhận | Kênh |
|---|---|---|
| Chuyển `DangSoanThao``DangGopY` | Tất cả role góp ý (PD/PM/PRO/CCM/FIN/ACT) | email + in-app |
| Chuyển `DangKiemTraCCM` | `CCM` | email + in-app |
| Chuyển `DangTrinhKy` | `BOD` + `NĐUQ` | email + in-app (high priority) |
| Quá SLA 80% thời gian | Role đang giữ phase | in-app warning |
| Quá SLA → auto-approve | Drafter + role giữ phase | email + in-app (log audit) |
| Reject (quay về `DangSoanThao`) | Drafter | email + in-app |
## 7. Data model implication (cho Phase 3)
```csharp
// Domain
public enum ContractPhase {
DangChon = 1,
DangSoanThao,
DangGopY,
DangDamPhan,
DangInKy,
DangKiemTraCCM,
DangTrinhKy,
DangDongDau,
DaPhatHanh,
TuChoi
}
public class Contract : AuditableEntity {
public Guid Id { get; set; }
public string MaHopDong { get; set; } // tự gen theo RG-001
public ContractType Type { get; set; } // HDTP, HDGK, NCC, HDDV...
public ContractPhase Phase { get; set; }
public Guid SupplierId { get; set; }
public Guid ProjectId { get; set; }
public decimal GiaTri { get; set; }
public bool BypassProcurementAndCCM { get; set; }
public DateTime? SlaDeadline { get; set; } // khi nào phase hiện tại hết hạn
// ...
public List<ContractComment> Comments { get; set; } // thread góp ý phase 3
public List<ContractApproval> Approvals { get; set; } // ai ký phase nào, lúc nào
public List<ContractAttachment> Attachments { get; set; } // scan bản gốc, file export
}
public class ContractApproval {
public Guid ContractId { get; set; }
public ContractPhase Phase { get; set; }
public Guid ApproverUserId { get; set; }
public DateTime? ApprovedAt { get; set; }
public ApprovalDecision Decision { get; set; } // Approve | Reject | AutoApprove
public string? Comment { get; set; }
}
```
**Service chính:**
- `IContractWorkflowService.TransitionAsync(contractId, targetPhase, userId, comment)` — check guard + update state + tạo approval + notify
- `IContractCodeGenerator.GenerateAsync(projectId, type, supplierId)` — dùng SEMAPHORE/transaction tránh race condition
- `ISlaExpiryJob` — hosted service chạy mỗi 15 phút, auto-approve các HĐ quá hạn
## 8. Business rules summary
1. **Một role chỉ có 1 phase active tại 1 thời điểm** cho 1 HĐ.
2. **Auto-approve nếu quá SLA** — nhưng phải log `Decision=AutoApprove` rõ ràng trong `ContractApproval`.
3. **Reject → quay về `DangSoanThao`** — Drafter nhận lại, toàn bộ approval trước đó bị invalidate (kept as history).
4. **Không cho xóa HĐ** đã qua phase 5 (`DangInKy`) — chỉ soft delete.
5. **Mã HĐ** gen theo `forms-spec.md § RG-001` — chỉ gen khi transition sang phase 5.
6. **Audit log đầy đủ** — mọi transition đều ghi `AuditLog(entityId, action, oldPhase, newPhase, userId, timestamp, diff)`.

63
docs/workflow-raw.md Normal file
View File

@ -0,0 +1,63 @@
MỤC ĐÍCH/PURPOSE:
Nhằm thống nhất các bước thực hiện trình ký Hợp đồng Thầu phụ/Nhà cung cấp/Tổ đội.
To unify the steps in the process of signing Subcontractor/Supplier/Team Contract.
Đảm bảo sự tuân thủ và vận hành Hệ thống một cách đồng nhất trên tất cả các Phòng Ban/Dự án của Công ty TNHH Xây dựng Solutions.
Ensure compliance and consistent of system operation through all Departments and Projects of Solutions Construction Co., Ltd.
PHẠM VI VÀ ĐỐI TƯỢNG ÁP DỤNG/SCOPE AND OBJECTS OF APPLICATION:
Áp dụng đối với các hợp đồng/phụ lục hợp đồng Thầu phụ/Nhà cung cấp/Tổ đội tại các Phòng ban/Dự án của Công ty TNHH Xây dựng Solutions như sau:
This procedure shall apply to Subcontractor/Supplier/Team contracts and contract appendices at the Departments and Projects of Solutions Construction Co., Ltd., as follows:
TÀI LIỆU THAM KHẢO/REFERENCES:
Tiêu chuẩn ISO 9001/ISO 9001 standard.
ĐỊNH NGHĨA, TỪ VIẾT TẮT/DEFINITIONS, ABBREVIATIONS:
SOL : Công ty TNHH Xây dựng Solutions/Solutions Construction Co., Ltd
BOD : Ban Giám đốc/Board of Directors
NĐUQ : Người được ủy quyền/Authorized person
CCM : Phòng Kiểm soát Chi phí/Cost Control Management Department
PRO : Phòng Cung ứng/Procurement Department
FIN : Phòng Tài chính/Financial Department
ACT : Phòng Kế toán/Accounting Department
EQU : Phòng Thiết bị/Equipment Department
HRA : Phòng Nhân sự - Hành chính/HRA Department
PB : Phòngban/Department
TP : Trưởng Phòng ban/Department Manager
TBP : Trưởng Bộ phận/Section Leader
NV : Nhân viên/Officer
BCH CT : Ban chỉ huy công trường/Site Management Team
PD : Giám đốc Thi công/Project Director
PM : Giám đốc Dự án/Project Manager
QS : QS công trường/QS
HĐ : Hợp đồng/Contract
HĐTG : Hợp đồng Trọn gói/Lump sum Contract
HĐNC : Hợp đồng Nhân công/Workmen Contract
NTP : Nhà thầu phụ/Subcontractor
NCC : Nhà cung cấp/Supplier
TĐ : Tổ đội/Team
QUY TRÌNH/PROCEDURE:
Lưu đồ/Flow chart
Diễn giải/Description
--- TABLE 1 (12r x 5c) ---
Bước | Step || Quy trình | Procedure || Trách nhiệm | Responsibilities || Biểu mẫu | Forms || Thời gian | Time
|| || || ||
|| || PB/BCH CT || SOL-PRO-SP-001 Quy trình Cung ứng | SOL-PRO-SP-001 Procurement Procedure ||
|| || QS/NV.PB/ | TBP/TPB || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code || 07 ngày | 07 days
|| || PD/PM/PRO/ | CCM/FIN/ACT || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code || 07 ngày | 07 days
|| || QS/NV.PB/ | TBP/TPB/ PD/PM || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code || 07 ngày | 07 days
|| || NTP/NCC/TĐ || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code || 07 ngày | 07 days
|| || CCM || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code || 03 ngày | 03 days
|| || BOD/ | NĐUQ || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code || 01 ngày | 01 days
|| || HRA || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code ||
|| || PB/BCH CT/ CCM || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code ||
|| || || SOL-CCM-FO-002.01.v01 | Chấp thuận Hợp đồng Contract approval cover | SOL-CCM-FO-002.02.v01 | Hợp đồng trọn gói - Nhân công, Vật tư | Lump sum contract - Labor and Material | SOL-CCM-FO-002.03.v01 | Hợp đồng trọn gói - Nhân công, Thiết bị | Lump sum contract - Labor and Equipment | SOL-CCM-FO-002.04.v01 | Hợp đồng trọn gói Điều kiện chung | Lump sum contract General conditions | SOL-CCM-FO-002.05.v01 | Hợp đồng giao khoán | Workmen contract | SOL-CCM-FO-002.06.v01 | Hợp đồng mua bán | Purchase contract | SOL-CCM-FO-002.07.v01 | Đơn đặt hàng | Purchase order | SOL-CCM-RG-001 | Quy định mã số hợp đồng và đơn hàng | Regulation on contract and purchasing order code ||
--- TABLE 2 (10r x 3c) ---
BƯỚC | STEP || TRÁCH NHIỆM | RESPONSIBILITIES || NỘI DUNG CHI TIẾT | DETAIL
Lựa chọn NTP/NCC | Select NTP/NCC || PB/BCH CT || Thống nhất chọn NTP/NCC/ĐTC theo quy định. | Select NTP/NCC/ĐTC in accordance with regulations. | Tham khảo Quy trình Cung ứng để so sánh và lựa chọn NTP/NCC/TĐC đúng quy định. | Refer to the Procurement Procedure to compare and select NTP/NCC/ĐTC in accordance with regulations.
Soạn thảo hợp đồng | Draft the contract || QS/NV.PB/ | TBP/TPB || QS/NV.PB soạn dự thảo HĐ. | QS/NV.PB drafts Contract. | TBP/TPB kiểm tra các nội dung phù hợp với gói thầu và biểu mẫu HĐ. | TBP/TPB checks contents in compliance with the packages and Forms of Contract. | QS/NV.PB gởi dự thảo HĐ cho các Phòng ban để lấy ý kiến qua Email. | QS/NV.PB sends draft contract to relevant Departments for obtaining comments via email.
Góp ý nội dung | Feedback content || PD/PM/PRO/ | CCM/FIN/ACT || PM/PD góp ý về phạm vi công việc, tiến độ, chất lượng, biện pháp thi công... (đối với HĐ của BCH CT). | PD/PM comments on scope of words, construction schedule, quality, construction methods,… (for BCH CTs contract). | EQU góp ý (thuê/mua thiết bị). | EQU comments on hiring and purchasing equipment. | PRO góp ý (vật tư/thầu phụ trọn gói). | PRO comments on material and lumpsum Subcontractor. | CCM góp ý về các điều khoản liên quan đến chi phí. | CCM comments on provisions relating to cost. | ACT/FIN góp ý về điều kiện thanh toán, bảo lãnh, bảo hành. | ACT/FIN Comments on term of payments, guarantees, project warranty.
Đàm phán, thương thảo hợp đồng | Negotiate the contract terms || QS/NV.PB/ | TBP/TPB/PD/PM || PB/BCH CT đàm phán các điều khoản về chất lượng, giao hàng, giá trị HĐ, điều kiện thanh toán, tạm ứng, ... | PB/BCH CT negotiates provisions on quality, product delivery, contract value, term of payment, advance payment, etc. | Các Phòng chức năng tham gia thương thảo nếu có yêu cầu. | Departments participates in negotiation if required.
In hợp đồng | Print the contract || QS/NV.PB || NTP/NCC/TĐ in hợp đồng 2 mặt, ký, đóng dấu Hợp đồng và gửi về cho PB/BCH CT. | NTP/NCC/TĐ prints double-sided contract, signs and seals on the contract and sends to PB/BCH CT. | Cập nhật số Hợp đồng theo quy định. | Update the contract number. | PD/PM/TPB kiểm tra các điều khoản hợp đồng và ký nháy trên từng HĐ đúng theo các vị trí quy định. | PD/PM/TPB checks provisions contract and initializes on each contract. | PD/PM/TPB/TBP ký duyệt và cho ý kiến (nếu có) lên tờ cover chấp thuận HĐ (SOL-CCM-FO-002.01). | PD/PM/TPB signs and gives comments (if any) on the cover of Contract approval cover (SOL-CCM-FO-002.01). | QS/NV.PB chuyển HĐ cho CCM. | QS/NV.PB handover the contract to CCM Dept.
Kiểm tra | Review and verify || CCM || CCM kiểm tra, TP.CCM ký nháy trên từng hợp đồng; ký duyệt và cho ý kiến (nếu có) lên Chấp thuận HĐ (SOL-CCM-FO-002.01.v01). | CCM checks and initializes on each contract; signs and gives comments (if any) on the Contract approval cover (SOL-CCM-FO-002.01.v01).
Duyệt | Approve || BOD/ | NĐUQ || Xem xét và ký duyệt Hợp đồng. | Reviewing and signing the Contract.
Đóng dấu hợp đồng | Seal the contract || HRA/ISO || PB/BCH CT chuyển hợp đồng đã được ký duyệt đến HRA/ISO để đóng dấu theo quy định. | PB/BCH CT transfrom the approved contract to HRA for seal the contract in accordance with regulations.
Phát hành hợp đồng. Lưu trữ hồ sơ | Issue and archive the contract || PB/BCH CT/ | CCM || PB/BCH CT scan và gởi 01 bản gốc cho CCM. | PB/BCH CT scan and send the original contract to CCM | PB/BCH CT thông báo và chuyển bản gốc hợp đồng đã ký (theo đúng số lượng) đến NTP/NCC. | Notifying and sending originals of the signed contract (in accordance with quantity) to NTP/NCC. | Lưu trữ bản gốc hợp đồng. | Archive the original contract. | Lưu trữ trên Server theo Filing System (Hợp đồng bản cứng chính và bản scan). | Keep the contract on Filing System (hard copy and soft copy).

24
fe-admin/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

1
fe-admin/.nvmrc Normal file
View File

@ -0,0 +1 @@
20

73
fe-admin/README.md Normal file
View File

@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

23
fe-admin/eslint.config.js Normal file
View File

@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

13
fe-admin/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>fe-admin</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

33
fe-admin/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "fe-admin",
"private": true,
"version": "0.0.0",
"type": "module",
"engines": {
"node": ">=20"
},
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.5",
"react-dom": "^19.2.5"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.5.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.2",
"vite": "^8.0.9"
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

24
fe-admin/public/icons.svg Normal file
View File

@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

184
fe-admin/src/App.css Normal file
View File

@ -0,0 +1,184 @@
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}

121
fe-admin/src/App.tsx Normal file
View File

@ -0,0 +1,121 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
</div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div>
</section>
<div className="ticks"></div>
<section id="spacer"></section>
</>
)
}
export default App

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

111
fe-admin/src/index.css Normal file
View File

@ -0,0 +1,111 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
#root {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
body {
margin: 0;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}

10
fe-admin/src/main.tsx Normal file
View File

@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023", "DOM"],
"module": "esnext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

7
fe-admin/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"module": "esnext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

24
fe-admin/vite.config.ts Normal file
View File

@ -0,0 +1,24 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'node:path'
// Admin UI — port 8082, proxy /api → SolutionErp.Api (http://localhost:5443)
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 8082,
strictPort: true,
proxy: {
'/api': {
target: 'http://localhost:5443',
changeOrigin: true,
secure: false,
},
},
},
})

24
fe-user/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

1
fe-user/.nvmrc Normal file
View File

@ -0,0 +1 @@
20

73
fe-user/README.md Normal file
View File

@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

23
fe-user/eslint.config.js Normal file
View File

@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

13
fe-user/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>fe-user</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

33
fe-user/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "fe-user",
"private": true,
"version": "0.0.0",
"type": "module",
"engines": {
"node": ">=20"
},
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.2.5",
"react-dom": "^19.2.5"
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.5.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.2",
"vite": "^8.0.9"
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

24
fe-user/public/icons.svg Normal file
View File

@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

184
fe-user/src/App.css Normal file
View File

@ -0,0 +1,184 @@
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}

121
fe-user/src/App.tsx Normal file
View File

@ -0,0 +1,121 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
</div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div>
</section>
<div className="ticks"></div>
<section id="spacer"></section>
</>
)
}
export default App

BIN
fe-user/src/assets/hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

111
fe-user/src/index.css Normal file
View File

@ -0,0 +1,111 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
#root {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
body {
margin: 0;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}

10
fe-user/src/main.tsx Normal file
View File

@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

25
fe-user/tsconfig.app.json Normal file
View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023", "DOM"],
"module": "esnext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

7
fe-user/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"module": "esnext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

24
fe-user/vite.config.ts Normal file
View File

@ -0,0 +1,24 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'node:path'
// User UI — port 8080, proxy /api → SolutionErp.Api (http://localhost:5443)
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 8080,
strictPort: true,
proxy: {
'/api': {
target: 'http://localhost:5443',
changeOrigin: true,
secure: false,
},
},
},
})

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"version": "10.0.104",
"rollForward": "latestFeature"
}
}

100
scripts/parse_forms.py Normal file
View File

@ -0,0 +1,100 @@
"""Extract text + structure from 8 FORM files (.docx, .doc, .xlsx).
Outputs: docs/forms-spec-raw.md — dump text + tables for manual field spec extraction.
Usage: python scripts/parse_forms.py
"""
from __future__ import annotations
import os
import sys
import zipfile
from pathlib import Path
from xml.etree import ElementTree as ET
try:
import docx
from openpyxl import load_workbook
except ImportError:
sys.stderr.write("pip install python-docx openpyxl\n")
sys.exit(1)
FORM_DIR = Path("D:/Dropbox/CONG_VIEC/SOLUTION/FORM")
OUT = Path(__file__).parent.parent / "docs" / "forms-spec-raw.md"
W_NS = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
def extract_doc_via_zip(path: Path) -> str:
"""Fallback for .doc (binary) or .docx — unzip and read word/document.xml."""
try:
with zipfile.ZipFile(path) as z:
with z.open("word/document.xml") as f:
tree = ET.parse(f)
root = tree.getroot()
texts: list[str] = []
for para in root.iter(f"{{{W_NS['w']}}}p"):
line = "".join(t.text or "" for t in para.iter(f"{{{W_NS['w']}}}t"))
if line.strip():
texts.append(line)
return "\n".join(texts)
except Exception as e:
return f"[ERROR unzip: {e}]"
def extract_docx(path: Path) -> str:
try:
d = docx.Document(str(path))
parts: list[str] = []
for p in d.paragraphs:
if p.text.strip():
parts.append(p.text)
# Tables
for i, tbl in enumerate(d.tables):
parts.append(f"\n--- TABLE {i+1} ({len(tbl.rows)} rows x {len(tbl.columns)} cols) ---")
for row in tbl.rows:
cells = [c.text.strip().replace("\n", " | ") for c in row.cells]
parts.append(" || ".join(cells))
return "\n".join(parts)
except Exception:
return extract_doc_via_zip(path)
def extract_xlsx(path: Path) -> str:
wb = load_workbook(str(path), data_only=True)
parts: list[str] = []
for sh_name in wb.sheetnames:
ws = wb[sh_name]
parts.append(f"\n--- SHEET: {sh_name} ({ws.max_row} rows x {ws.max_column} cols) ---")
for row in ws.iter_rows(values_only=True):
vals = [str(v).strip() if v is not None else "" for v in row]
if any(v for v in vals):
parts.append(" | ".join(vals))
return "\n".join(parts)
def main() -> None:
files = sorted(FORM_DIR.iterdir())
OUT.parent.mkdir(parents=True, exist_ok=True)
with OUT.open("w", encoding="utf-8") as out:
out.write("# Forms — Raw Text Dump\n\n")
out.write(f"Source: `{FORM_DIR}` — {len(files)} files\n\n")
for f in files:
out.write(f"\n---\n\n## {f.name}\n\n")
ext = f.suffix.lower()
if ext == ".xlsx":
text = extract_xlsx(f)
elif ext in (".docx", ".doc"):
text = extract_docx(f)
else:
text = "[unsupported]"
out.write("```\n")
out.write(text[:20000])
if len(text) > 20000:
out.write(f"\n\n[... truncated {len(text)-20000} chars]")
out.write("\n```\n")
print(f"OK -> {OUT}")
if __name__ == "__main__":
main()

21
scripts/parse_workflow.py Normal file
View File

@ -0,0 +1,21 @@
"""Extract QUY_TRINH docx text for manual workflow modeling."""
from pathlib import Path
import docx
SRC = Path("D:/Dropbox/CONG_VIEC/SOLUTION/QUY_TRINH/QT TRINH KY HOP DONG TP-NCC.docx")
OUT = Path(__file__).parent.parent / "docs" / "workflow-raw.md"
d = docx.Document(str(SRC))
parts = []
for p in d.paragraphs:
if p.text.strip():
parts.append(p.text)
for i, tbl in enumerate(d.tables):
parts.append(f"\n--- TABLE {i+1} ({len(tbl.rows)}r x {len(tbl.columns)}c) ---")
for row in tbl.rows:
cells = [c.text.strip().replace("\n", " | ") for c in row.cells]
parts.append(" || ".join(cells))
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text("\n".join(parts), encoding="utf-8")
print(f"OK -> {OUT} ({OUT.stat().st_size} bytes)")

View File

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc;
namespace SolutionErp.Api.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries =
[
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
];
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}

View File

@ -0,0 +1,23 @@
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5235",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7241;http://localhost:5235",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.6" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.4" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SolutionErp.Application\SolutionErp.Application.csproj" />
<ProjectReference Include="..\SolutionErp.Infrastructure\SolutionErp.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
@SolutionErp.Api_HostAddress = http://localhost:5235
GET {{SolutionErp.Api_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,12 @@
namespace SolutionErp.Api;
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\SolutionErp.Domain\SolutionErp.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
<PackageReference Include="MediatR" Version="14.1.0" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\SolutionErp.Application\SolutionErp.Application.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>