Manual ICO Web¶
ICO Web — Project Migration Document
Note: This project was originally scaffolded using Vite (
create-vitewith the React/TypeScript template).
1. Stack & Prerequisites¶
Tech Stack¶
| Category | Technology | Version |
|---|---|---|
| Language | TypeScript | ^5.6.3 |
| Framework | React | ^18.3.1 |
| Build Tool | Vite | ^6.3.5 |
| Styling | Tailwind CSS | ^3.4.11 |
| State Management | Redux Toolkit | ^2.2.7 |
| Routing | React Router DOM | ^6.26.2 |
| HTTP Client | Axios | ^1.7.7 |
| Form | React Hook Form | ^7.53.0 |
| Form Validation | Zod | ^3.23.8 |
| i18n | i18next | ^25.7.4 |
| Testing | Jest | ^29.7.0 |
| Linting | ESLint | ^8.56.0 |
| Container | Docker (multi-stage) | — |
| Web Server | Nginx Alpine | — |
Prerequisites to Install¶
| Tool | Version |
|---|---|
| Node.js | LTS (v20/v22) |
| Yarn | Berry (v2+) |
| Docker | 20+ |
| Git | 2.x |
Verify Installations¶
node -v # v20.x or v22.x
yarn -v # 4.x (Berry)
docker -v # Docker version 20+
git --version # git version 2.x
3. Package Manager¶
This project uses Yarn Berry (v2+) with classic node_modules linker.
Configuration¶
File: .yarnrc.yml
nodeLinker: node-modules
Common Commands¶
# Install all dependencies
yarn install
# Add a new dependency
yarn add <package-name>
# Add a dev dependency
yarn add -D <package-name>
# Remove a dependency
yarn remove <package-name>
Scripts (from package.json)¶
yarn dev # Start Vite dev server (http://localhost:5173)
yarn build # TypeScript check + production build → /dist
yarn test # Run Jest tests
4. Project Architecture¶
4.1 Routing (React Router DOM v6)¶
Entry: src/app/AppRoutes.tsx
All routes are defined in a single file using <Routes> and <Route>. The app wraps routes in a <HelmetLayout> for SEO.
Route groups:
| Group | Path Pattern | Pages |
|---|---|---|
| Landing | / |
Landing page, investment campaign |
| Auth |
/login, /reset-*
|
Login, reset password, Google Authenticator |
| Sign Up (Thai) | /signup/* |
Type select → corporate/individual → basicinfo → suite test → OTP → ID card → verification |
| Sign Up (Foreign) | /foreign/* |
Fullname → CRS → basicinfo → suite test → doc upload → OTP |
| Re-KYC | /rekyc/* |
Re-KYC flow, FATCA, suite test, ID card |
| Portfolio | /portfolio |
Portfolio management |
| Marketplace | /marketplace |
Marketplace + TOTP confirmation |
| Order & Trade | /order-trade |
Order trade + high net worth verification |
| Subscription | /subscription |
Subscription page |
| Asset Details | /asset-details/* |
ICO details, white paper, filing |
| Fraction Exchange | /fraction-exchange |
Fraction exchange |
| Static Pages |
/contact-us, etc. |
Contact, policy, complaint, about us, terms |
| Error | * |
404 page |
4.2 State Management (Redux Toolkit)¶
Store: src/redux/store.tsx
Redux Store
└── redux/
├── store
└── slice/
4.3 API Layer (Axios)¶
File: src/api/axios.tsx
The project creates multiple Axios instances, all with baseURL: window.origin (same-origin proxy):
| Instance | Purpose | Auth |
|---|---|---|
default export |
Basic API calls | Cookies only |
axiosCheckToken |
Authenticated calls with refresh | Auto-refresh |
axiosMultipart |
File uploads (multipart/form-data) | Cookies |
axiosNoAuth |
Public API calls | None |
Token refresh flow:
- Request interceptor checks if refresh token is expired → redirect to login if so
- If access token is expired, calls
POST /api/v1/authen/customers/refresh - Queues failed requests in
failedQueuewhile refresh is in-flight - On success, retries all queued requests with new token
- In dev mode (
isDev()), token checks are skipped entirely
Helper: getCustomAxios() returns axiosCheckToken if access token exists, otherwise axiosNoAuth.
4.4 Form Handling¶
| Library | Purpose |
|---|---|
| React Hook Form | Form state, validation, submission |
| Zod | Schema-based validation |
| @hookform/resolvers | Connects Zod schemas to RHF |
4.5 Internationalization (i18n)¶
File: src/i18n/config.ts
| Setting | Value |
|---|---|
| Library | i18next + react-i18next |
| Languages |
en (English), th (Thai) |
| Fallback | en |
| Detection | localStorage → navigator → htmlTag |
| Storage key |
i18nextLng (localStorage) |
| Translation files | public/locales/{lng}/{ns}.json |
| Namespaces |
common, footer, navbar, landing, signup, portfolio
|
| Default ns | common |
4.6 Styling¶
| Tool | Config File | Notes |
|---|---|---|
| Tailwind CSS | tailwind.config.js |
Custom font: Anuphan, custom colors/screens |
| CSS | src/index.css |
Global styles |
| PostCSS | postcss.config.js |
Tailwind + Autoprefixer plugins |
| tailwind-merge | via src/lib/utils.ts
|
cn() helper for conditional classes |
4.7 Key Libraries¶
| Library | Purpose |
|---|---|
crypto-js |
Encryption/decryption (with pepper keys) |
jwt-decode |
Decode JWT tokens (expiry check) |
js-cookie |
Cookie management (access/refresh token) |
dayjs |
Date/time formatting (with UTC/timezone) |
lodash |
Utility functions |
react-toastify |
Toast notifications |
react-helmet-async |
SEO meta tags |
apexcharts |
Interactive charts |
chart.js |
Canvas-based charts |
echarts-for-react |
ECharts wrapper |
@tanstack/react-table |
Headless table |
swiper |
Carousel/slider |
leaflet |
Map component |
filepond |
File upload widget |
react-webcam |
Webcam capture (ID card/liveness) |
5. Environment Variables¶
5.1 Variable List¶
All variables use the VITE_ prefix (required by Vite to expose to client-side code):
| Variable | Required | Description |
|---|---|---|
VITE_BASE_URL |
Yes | Backend API base URL |
VITE_REKOGNITOR_URL |
Yes | AWS Rekognition / face liveness service URL |
VITE_STATIC_URL |
Yes | Static file server URL (images, documents) |
VITE_BUCKET_URL |
Yes | S3 bucket URL for uploads/downloads |
VITE_PEPER |
Yes | Encryption pepper (used with crypto-js) |
VITE_BACK_REGISTER |
Yes | Backend registration endpoint pepper |
VITE_CID_PEPER |
Yes | CID (citizen ID) encryption pepper |
VITE_ENVIRONMENT |
Yes | Environment name: dev, uat, production
|
5.2 Environment Variable Injection (CI/CD Pipeline)¶
Workflow process:
-
Build Phase: The React app is built using Vite. During this build use config paceholder
VITE_*(e.g., the string"VITE_BASE_URL"instead of a real URL). -
Docker Phase: The
Dockerfilepackages the built static files and anentrypoint.shscript into an Nginx image. -
Environment Fetching: Before the application spins up in the target AWS environment, the real environment values are fetched from an
.envfile securely stored in an AWS S3 bucket. -
Runtime Injection: When the Docker container starts, it executes
entrypoint.sh. This shell script usessedto find all the JS bundles that contain the text placeholders and replaces them with the real environment values fetched from S3.
This pattern allows the same Docker image to be promoted across Dev, UAT, and Prod environments just by fetching a different .env file from S3
5.3 Config File¶
File: src/config/config.ts
This file maps environment variables to exported constants used throughout the app:
| Constant | Source / Env Var |
|---|---|
BASE_URL |
VITE_BASE_URL |
BASE_REKOGNITOR_URL |
VITE_REKOGNITOR_URL |
BASE_STATIC_URL |
VITE_STATIC_URL |
BASE_BUCKET_URL |
VITE_BUCKET_URL |
PEPER |
VITE_PEPER |
PEPER_BACK_REGISTER |
VITE_BACK_REGISTER |
PEPER_CID |
VITE_CID_PEPER |
ENVIRONMENT |
VITE_ENVIRONMENT |
Updated by prin methirattanasophon about 1 month ago · 1 revisions