feat: initialize landing application with Vite and Mantine configuration

This commit is contained in:
Firman Ramdhani
2026-04-06 12:11:17 +07:00
parent 988826ac8e
commit 8c7b6d3017
16 changed files with 307 additions and 43 deletions
+46 -21
View File
@@ -1,39 +1,64 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
// For format details, see https://aka.ms/devcontainer.json
// This container standardizes the local development environment for the Monorepo.
{
"name": "Node.js & TypeScript",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"name": "FE Monorepo Template",
// Base image providing Node.js v24, TypeScript, and a Debian Bookworm environment.
"image": "mcr.microsoft.com/devcontainers/typescript-node:24-bookworm",
// Features to add to the dev container. More info: https://containers.dev/features.
// DevContainer Features: Pre-installed tools needed for our workflow.
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers-extra/features/pnpm:2": {},
"ghcr.io/devcontainers/features/sshd:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [6006, 5173, 5174, 5175],
// ─── Port Topology ──────────────────────────────────────────────────────────
// 6006: Storybook (Docs)
// 5173: Web App (Must be strictly 5173 for Electron IPC compatibility)
// 3000: Landing App (Isolated from Vite's default 517x blast radius)
"forwardPorts": [6006, 5173, 3000],
"portsAttributes": {
"6006": {
"label": "Storybook",
"onAutoForward": "notify"
},
"5173": {
"label": "Vites Application 1",
"label": "Web App (Electron Target)",
"onAutoForward": "notify"
},
"5174": {
"label": "Vites Application 2",
"onAutoForward": "notify"
},
"5175": {
"label": "Vites Application 3",
"3000": {
"label": "Landing App (Public)",
"onAutoForward": "notify"
}
}
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "yarn install",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
},
// ─── Lifecycle Hooks ────────────────────────────────────────────────────────
// Automatically install all workspace dependencies once the container is built.
"postCreateCommand": "pnpm install",
// ─── Editor Standardization ─────────────────────────────────────────────────
// Enforces code style and tooling across all developers' machines.
"customizations": {
"vscode": {
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"extensions": [
"dbaeumer.vscode-eslint", // Linting
"esbenp.prettier-vscode", // Formatting
"bradlc.vscode-tailwindcss", // Tailwind v4 IntelliSense
"yoavbls.pretty-ts-errors", // Human-readable TypeScript errors
"editorconfig.editorconfig" // Standardized indentation
]
}
},
// ─── Security & Permissions ─────────────────────────────────────────────────
// CRITICAL: Run the container as 'node' instead of 'root'.
// This prevents 'Permission Denied' errors on the host OS when trying to
// delete or modify the 'node_modules' folder created inside the container.
"remoteUser": "node"
}
+33 -10
View File
@@ -28,6 +28,7 @@ The monorepo is organized into **Apps** (deployable applications) and **Packages
.
├── apps/
│ ├── web/ # Main React Application (Vite + TypeScript)
│ ├── landing/ # Public Promotional SPA (Vite + TypeScript)
│ ├── desktop/ # Electron Desktop Wrapper (electron-vite)
│ └── docs-dev/ # Component Documentation & Playground (Storybook)
@@ -71,12 +72,16 @@ This repository uses **Turborepo** to orchestrate tasks efficiently. All command
### Development
| Command | Description |
| -------------------- | --------------------------------------------------------------------------- |
| `pnpm dev` | Start **all applications** (`web` and `docs-dev`) in parallel |
| `pnpm dev:web` | Start only the **Main Web App** (usually at `http://localhost:5173`) |
| `pnpm dev:docs-dev` | Start **Storybook** for UI development (usually at `http://localhost:6006`) |
| `pnpm dev:desktop` | Start the **Web App + Electron** in parallel for desktop development |
| Command | Description |
| -------------------- | ---------------------------------------------------------------------------------- |
| `pnpm dev` | Start **all applications** (`web` and `docs-dev`) in parallel |
| `pnpm dev:web` | Start only the **Main Web App** (strictly at `http://localhost:5173`) |
| `pnpm dev:landing` | Start the **Public Landing App** (strictly at `http://localhost:3000`) |
| `pnpm dev:docs-dev` | Start **Storybook** for UI development (strictly at `http://localhost:6006`) |
| `pnpm dev:desktop` | Start the **Web App + Electron** in parallel for desktop development |
> [!NOTE]
> **Port Topology**: `electron-vite` dynamically allocates a background port (usually `5174`) for its internal renderer shell during `pnpm dev:desktop`. We strictly isolate `web` (`5173`) and `landing` (`3000`) onto separate port ranges to prevent race conditions during parallel execution.
### Building & Quality
@@ -84,6 +89,7 @@ This repository uses **Turborepo** to orchestrate tasks efficiently. All command
| --------------------- | ------------------------------------------------------- |
| `pnpm build` | Build all apps and packages using Turbo cache |
| `pnpm build:web` | Build only the web application |
| `pnpm build:landing` | Build only the landing page |
| `pnpm build:docs-dev` | Build only the docs-dev application |
| `pnpm build:desktop` | Build the web app, then compile the Electron app |
| `pnpm test` | Run unit tests (Vitest) across all packages |
@@ -172,7 +178,24 @@ The **Electron desktop wrapper** that embeds `apps/web` for native desktop exper
---
### 3. `apps/docs-dev` (Storybook)
### 3. `apps/landing`
The **public promotional website** — a standalone SPA for the company profile and marketing pages.
* Deployed independently to the web (e.g., Vercel) — no interaction with Electron
* Consumes shared UI components from `@repo/ui` and utilities from `@repo/utils`
* Locked to port **3000** (`strictPort: true`) — evacuated from the `517x` range to avoid `electron-vite` port collisions
**Tech Stack**:
* React
* Vite
* TypeScript
* Tailwind CSS v4
---
### 4. `apps/docs-dev` (Storybook)
An isolated environment for developing and documenting UI components.
@@ -181,7 +204,7 @@ An isolated environment for developing and documenting UI components.
---
### 4. `packages/utils`
### 5. `packages/utils`
Shared business logic and reusable utility modules that can be consumed across multiple applications. Fully tested using Vitest.
@@ -189,7 +212,7 @@ This package is intended to hold non-UI, cross-cutting logic such as date/time h
---
### 5. `packages/ui`
### 6. `packages/ui`
Shared UI component library (Buttons, Inputs, Cards, Layouts).
@@ -198,7 +221,7 @@ Shared UI component library (Buttons, Inputs, Cards, Layouts).
---
### 6. `packages/configs`
### 7. `packages/configs`
Single source of truth for tooling configuration.
+5
View File
@@ -0,0 +1,5 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ['@repo/eslint-config/react.js'],
};
+14
View File
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Eigen — Company Profile</title>
<meta name="description" content="Eigen — Enterprise-grade solutions for modern businesses. Discover our products, services, and vision." />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
{
"name": "landing",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint \"src/**/*.ts\""
},
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/utils": "workspace:*",
"@tailwindcss/vite": "^4.1.18",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"tailwindcss": "^4.1.18"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"eslint": "^8.57.1",
"typescript": "5.5.4",
"vite": "^5.1.4"
}
}
+37
View File
@@ -0,0 +1,37 @@
import { ThemeProvider } from '@repo/ui/provider';
import { Button } from '@repo/ui/components';
export default function App() {
return (
<ThemeProvider>
<div className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-br from-gray-950 via-gray-900 to-gray-800 text-white">
<div className="mx-auto max-w-3xl px-6 text-center">
<h1 className="text-5xl font-bold tracking-tight sm:text-6xl">
Hello from{' '}
<span className="bg-gradient-to-r from-blue-400 to-violet-400 bg-clip-text text-transparent">
Landing Page
</span>
</h1>
<p className="mt-6 text-lg leading-8 text-gray-300">
Enterprise-grade solutions for modern businesses.
This page is served from{' '}
<code className="rounded bg-gray-700/50 px-2 py-0.5 text-sm font-mono text-blue-300">
apps/landing
</code>{' '}
on port <strong>5174</strong>.
</p>
<div className="mt-10 flex items-center justify-center gap-4">
<Button>Get Started</Button>
<Button variant="outline">Learn More</Button>
</div>
<p className="mt-12 text-xs text-gray-500">
@repo/ui workspace link verified Button component rendered successfully.
</p>
</div>
</div>
</ThemeProvider>
);
}
+3
View File
@@ -0,0 +1,3 @@
@import 'tailwindcss';
@import '@repo/ui/theme.css';
@source "../../../packages/ui/src";
+10
View File
@@ -0,0 +1,10 @@
import './main.css';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './app';
createRoot(document.getElementById('app')!).render(
<StrictMode>
<App />
</StrictMode>,
);
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "@repo/typescript-config/react-app.json",
"include": ["src"],
"compilerOptions": {
"jsx": "react-jsx"
}
}
+11
View File
@@ -0,0 +1,11 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
port: 3000,
strictPort: true, // Evacuated from 517x range to avoid electron-vite port collision.
},
});
+2 -1
View File
@@ -1,4 +1,5 @@
import { useState, useCallback } from 'react';
import { useIsElectron } from './use-is-electron';
// ─── Types ──────────────────────────────────────────────────────
@@ -50,7 +51,7 @@ export function useElectronPrinter(): UseElectronPrinterReturn {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const isElectron = typeof window !== 'undefined' && !!window.electronAPI;
const isElectron = useIsElectron();
const refreshPrinters = useCallback(async (): Promise<ElectronPrinterInfo[]> => {
if (!window.electronAPI) {
+2 -1
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import { useIsElectron } from './use-is-electron';
// ─── Types ──────────────────────────────────────────────────────
@@ -64,7 +65,7 @@ export function useElectronUpdater(): UseElectronUpdaterReturn {
const [updateInfo, setUpdateInfo] = useState<ElectronUpdateInfo | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const isElectron = typeof window !== 'undefined' && !!window.electronAPI;
const isElectron = useIsElectron();
useEffect(() => {
if (!window.electronAPI) return;
+7
View File
@@ -0,0 +1,7 @@
/**
* A utility hook to determine if the React application is running
* inside the Electron desktop wrapper or a standard web browser.
*/
export function useIsElectron(): boolean {
return typeof window !== 'undefined' && !!window.electronAPI;
}
+4
View File
@@ -4,4 +4,8 @@ import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
port: 5173,
strictPort: true, // Fail if 5173 is in use. Electron NEEDS this exact port.
},
});
+2
View File
@@ -6,10 +6,12 @@
"build": "turbo run build",
"build:web": "turbo run build --filter=web",
"build:docs-dev": "turbo run build --filter=docs-dev",
"build:landing": "turbo run build --filter=landing",
"build:desktop": "turbo run build --filter=web && turbo run build --filter=desktop",
"dev": "turbo run dev",
"dev:web": "turbo run dev --filter=web",
"dev:docs-dev": "turbo run dev --filter=docs-dev",
"dev:landing": "turbo run dev --filter=landing",
"dev:desktop": "turbo run dev --filter=web --filter=desktop --parallel",
"package:desktop": "pnpm run build:desktop && cd apps/desktop && pnpm run package",
"package:mac": "pnpm run build:desktop && cd apps/desktop && pnpm run package:mac",
+94 -10
View File
@@ -96,7 +96,53 @@ importers:
version: 5.5.4
vite:
specifier: ^5.1.4
version: 5.4.17(@types/node@22.19.3)
version: 5.4.17
apps/landing:
dependencies:
'@repo/ui':
specifier: workspace:*
version: link:../../packages/ui
'@repo/utils':
specifier: workspace:*
version: link:../../packages/utils
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@5.4.17)
react:
specifier: ^19.2.3
version: 19.2.3
react-dom:
specifier: ^19.2.3
version: 19.2.3(react@19.2.3)
tailwindcss:
specifier: ^4.1.18
version: 4.1.18
devDependencies:
'@repo/eslint-config':
specifier: workspace:*
version: link:../../packages/configs/eslint
'@repo/typescript-config':
specifier: workspace:*
version: link:../../packages/configs/typescript
'@types/react':
specifier: ^19.2.7
version: 19.2.7
'@types/react-dom':
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.7)
'@vitejs/plugin-react':
specifier: ^5.1.2
version: 5.1.2(vite@5.4.17)
eslint:
specifier: ^8.57.1
version: 8.57.1
typescript:
specifier: 5.5.4
version: 5.5.4
vite:
specifier: ^5.1.4
version: 5.4.17
apps/web:
dependencies:
@@ -148,7 +194,7 @@ importers:
version: 5.5.4
vite:
specifier: ^5.1.4
version: 5.4.17(@types/node@22.19.3)
version: 5.4.17
vitest:
specifier: ^4.0.17
version: 4.0.17
@@ -246,7 +292,7 @@ importers:
version: 5.5.4
vite:
specifier: ^5.1.4
version: 5.4.17(@types/node@22.19.3)
version: 5.4.17
vitest:
specifier: ^4.0.17
version: 4.0.17
@@ -1151,7 +1197,7 @@ packages:
magic-string: 0.27.0
react-docgen-typescript: 2.4.0(typescript@5.5.4)
typescript: 5.5.4
vite: 5.4.17(@types/node@22.19.3)
vite: 5.4.17
dev: true
/@jridgewell/gen-mapping@0.3.13:
@@ -1958,7 +2004,7 @@ packages:
browser-assert: 1.2.1
storybook: 8.6.15(prettier@3.5.3)
ts-dedent: 2.2.0
vite: 5.4.17(@types/node@22.19.3)
vite: 5.4.17
dev: true
/@storybook/components@8.6.15(storybook@8.6.15):
@@ -2100,7 +2146,7 @@ packages:
resolve: 1.22.11
storybook: 8.6.15(prettier@3.5.3)
tsconfig-paths: 4.2.0
vite: 5.4.17(@types/node@22.19.3)
vite: 5.4.17
transitivePeerDependencies:
- rollup
- supports-color
@@ -2287,7 +2333,7 @@ packages:
'@tailwindcss/node': 4.1.18
'@tailwindcss/oxide': 4.1.18
tailwindcss: 4.1.18
vite: 5.4.17(@types/node@22.19.3)
vite: 5.4.17
/@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
@@ -3048,7 +3094,7 @@ packages:
'@rolldown/pluginutils': 1.0.0-beta.53
'@types/babel__core': 7.20.5
react-refresh: 0.18.0
vite: 5.4.17(@types/node@22.19.3)
vite: 5.4.17
transitivePeerDependencies:
- supports-color
dev: true
@@ -9411,6 +9457,43 @@ packages:
vfile-message: 4.0.3
dev: false
/vite@5.4.17:
resolution: {integrity: sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || >=20.0.0
less: '*'
lightningcss: ^1.21.0
sass: '*'
sass-embedded: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
esbuild: 0.21.5
postcss: 8.5.3
rollup: 4.39.0
optionalDependencies:
fsevents: 2.3.3
/vite@5.4.17(@types/node@22.19.3):
resolution: {integrity: sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -9444,10 +9527,11 @@ packages:
dependencies:
'@types/node': 22.19.3
esbuild: 0.21.5
postcss: 8.5.3
rollup: 4.39.0
postcss: 8.5.6
rollup: 4.55.1
optionalDependencies:
fsevents: 2.3.3
dev: true
/vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}