mirror of
https://github.com/Sea-Haven-Industries/seahaven-ap.git
synced 2026-09-30 04:33:15 +00:00
feat(frontend): scaffold vite spa and ci (AP-4)
This commit is contained in:
parent
f350ae9b51
commit
bff873c2df
29 changed files with 6275 additions and 1 deletions
2
.env.example
Normal file
2
.env.example
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Default data path until the API is wired (AP later tickets).
|
||||
VITE_USE_MOCKS=true
|
||||
24
.github/dependabot.yml
vendored
Normal file
24
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
minor-and-patch:
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
minor-and-patch:
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
16
.github/workflows/ci.yaml
vendored
Normal file
16
.github/workflows/ci.yaml
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
# Job id MUST stay `ci`: the reusable's job is also `ci`, so the check
|
||||
# context resolves to the required `ci / ci`.
|
||||
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-frontend.yaml@7ac3528750b346f181347bb09f6af927a1c0aa14 # v1.0.6
|
||||
with:
|
||||
node-version: "24"
|
||||
10
.github/workflows/dependency-review.yml
vendored
Normal file
10
.github/workflows/dependency-review.yml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
name: Dependency Review
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
review:
|
||||
uses: Sea-Haven-Industries/.github/.github/workflows/callable-dependency-review.yaml@7ac3528750b346f181347bb09f6af927a1c0aa14 # v1.0.6
|
||||
13
.github/workflows/labeler.yml
vendored
Normal file
13
.github/workflows/labeler.yml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
name: Labeler
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
label:
|
||||
uses: Sea-Haven-Industries/.github/.github/workflows/callable-labeler.yaml@7ac3528750b346f181347bb09f6af927a1c0aa14 # v1.0.6
|
||||
29
.github/workflows/policy.yaml
vendored
Normal file
29
.github/workflows/policy.yaml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: PR Policy
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- edited
|
||||
- labeled
|
||||
- unlabeled
|
||||
- ready_for_review
|
||||
|
||||
concurrency:
|
||||
group: "policy-${{ github.event.pull_request.number }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
policy:
|
||||
uses: Sea-Haven-Industries/.github/.github/workflows/callable-pr-policy.yaml@7ac3528750b346f181347bb09f6af927a1c0aa14 # v1.0.6
|
||||
secrets:
|
||||
JIRA_CLOUD_ID: ${{ secrets.JIRA_CLOUD_ID }}
|
||||
JIRA_SERVICE_ACCOUNT_EMAIL: ${{ secrets.JIRA_SERVICE_ACCOUNT_EMAIL }}
|
||||
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -27,3 +27,6 @@ npm-debug.log*
|
|||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
|
||||
# Generouted (regenerated by Vite plugin)
|
||||
src/router.ts
|
||||
|
|
|
|||
9
.prettierignore
Normal file
9
.prettierignore
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
dist
|
||||
build
|
||||
node_modules
|
||||
coverage
|
||||
package-lock.json
|
||||
*.tsbuildinfo
|
||||
src/router.ts
|
||||
playwright-report
|
||||
test-results
|
||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2
|
||||
}
|
||||
|
|
@ -6,13 +6,15 @@ Internal accounts payable automation for Sea Haven Industries (`ap.seahaven.com`
|
|||
|
||||
```bash
|
||||
npm ci
|
||||
cp .env.example .env # optional; defaults already use mocks
|
||||
npm run dev
|
||||
```
|
||||
|
||||
`VITE_USE_MOCKS=true` is the default data path until the API is wired.
|
||||
App serves at http://localhost:3000. `VITE_USE_MOCKS=true` is the default data path until the API is wired (`src/mocks`).
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
npm run verify
|
||||
npm run test:e2e
|
||||
```
|
||||
|
|
|
|||
7
e2e/home.spec.ts
Normal file
7
e2e/home.spec.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("home page shows Sea Haven AP brand", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.getByRole("heading", { name: "Sea Haven AP" })).toBeVisible();
|
||||
await expect(page.getByTestId("mocks-status")).toContainText("Mocks enabled");
|
||||
});
|
||||
36
eslint.config.js
Normal file
36
eslint.config.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import js from "@eslint/js";
|
||||
import eslintConfigPrettier from "eslint-config-prettier";
|
||||
import pluginReactHooks from "eslint-plugin-react-hooks";
|
||||
import pluginReactRefresh from "eslint-plugin-react-refresh";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ["dist/**", "build/**", "node_modules/**", "coverage/**", "src/router.ts"],
|
||||
},
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
{
|
||||
files: ["src/**/*.{ts,tsx}", "vite.config.ts", "vitest.config.ts", "playwright.config.ts"],
|
||||
languageOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
"react-hooks": pluginReactHooks,
|
||||
"react-refresh": pluginReactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...pluginReactHooks.configs.recommended.rules,
|
||||
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
|
||||
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
||||
},
|
||||
},
|
||||
);
|
||||
15
index.html
Normal file
15
index.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#0b3d5c" />
|
||||
<meta name="description" content="Sea Haven AP — internal accounts payable" />
|
||||
<title>Sea Haven AP</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
5733
package-lock.json
generated
Normal file
5733
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
58
package.json
Normal file
58
package.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"name": "seahaven-ap",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:e2e": "playwright test",
|
||||
"lint": "eslint . --max-warnings=0",
|
||||
"lint:fix": "eslint . --fix --max-warnings=0",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"verify": "npm run format:check && npm run lint && npm run build && npm test"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.22.1"
|
||||
},
|
||||
"packageManager": "npm@11.16.0",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@generouted/react-router": "^1.20.2",
|
||||
"@mui/icons-material": "^7.3.2",
|
||||
"@mui/material": "^7.3.2",
|
||||
"@mui/x-data-grid": "^8.11.0",
|
||||
"@tanstack/react-query": "^5.85.5",
|
||||
"@tanstack/react-query-devtools": "^5.85.5",
|
||||
"ky": "^1.10.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router": "^7.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.34.0",
|
||||
"@playwright/test": "^1.55.0",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@vitejs/plugin-react": "^5.0.2",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"globals": "^16.3.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.41.0",
|
||||
"vite": "^7.1.3",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
26
playwright.config.ts
Normal file
26
playwright.config.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./e2e",
|
||||
fullyParallel: true,
|
||||
forbidOnly: Boolean(process.env.CI),
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: process.env.CI ? "github" : "list",
|
||||
use: {
|
||||
baseURL: "http://localhost:4173",
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
],
|
||||
webServer: {
|
||||
command: "npm run dev -- --port 4173 --strictPort",
|
||||
url: "http://localhost:4173",
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 120_000,
|
||||
},
|
||||
});
|
||||
27
src/lib/mui-theme.ts
Normal file
27
src/lib/mui-theme.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { createTheme } from "@mui/material/styles";
|
||||
|
||||
export const muiTheme = createTheme({
|
||||
palette: {
|
||||
mode: "light",
|
||||
primary: {
|
||||
main: "#0b3d5c",
|
||||
},
|
||||
secondary: {
|
||||
main: "#1f6f8b",
|
||||
},
|
||||
background: {
|
||||
default: "#f5f7fa",
|
||||
paper: "#ffffff",
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: '"IBM Plex Sans", "Helvetica Neue", Arial, sans-serif',
|
||||
h1: {
|
||||
fontFamily: '"IBM Plex Serif", Georgia, serif',
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
});
|
||||
12
src/lib/query-client.ts
Normal file
12
src/lib/query-client.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 5 * 60 * 1000,
|
||||
gcTime: 10 * 60 * 1000,
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
18
src/main.tsx
Normal file
18
src/main.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { Routes } from "@generouted/react-router";
|
||||
import { AppProviders } from "@/providers/AppProviders";
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
|
||||
if (!rootElement) {
|
||||
throw new Error("Root element #root not found");
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(rootElement).render(
|
||||
<React.StrictMode>
|
||||
<AppProviders>
|
||||
<Routes />
|
||||
</AppProviders>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
13
src/mocks/index.test.ts
Normal file
13
src/mocks/index.test.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { areMocksEnabled, getMockInvoices, getMockVendors } from "@/mocks";
|
||||
|
||||
describe("mocks module", () => {
|
||||
it("defaults to mocks enabled", () => {
|
||||
expect(areMocksEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it("exposes sample invoices and vendors", () => {
|
||||
expect(getMockVendors().length).toBeGreaterThan(0);
|
||||
expect(getMockInvoices().length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
77
src/mocks/index.ts
Normal file
77
src/mocks/index.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
export type MockVendor = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type MockInvoice = {
|
||||
id: string;
|
||||
invoiceNumber: string;
|
||||
vendorId: string;
|
||||
vendorName: string;
|
||||
amountDue: number;
|
||||
dueDate: string;
|
||||
payDate: string | null;
|
||||
status: "pending_approval" | "approved" | "scheduled" | "paid";
|
||||
paymentMethod: "check" | "ach";
|
||||
};
|
||||
|
||||
export const mockVendors: MockVendor[] = [
|
||||
{ id: "vendor-1", name: "Acme Supplies Co" },
|
||||
{ id: "vendor-2", name: "Harbor Maintenance LLC" },
|
||||
{ id: "vendor-3", name: "Northwind Utilities" },
|
||||
];
|
||||
|
||||
export const mockInvoices: MockInvoice[] = [
|
||||
{
|
||||
id: "inv-1",
|
||||
invoiceNumber: "INV-1001",
|
||||
vendorId: "vendor-1",
|
||||
vendorName: "Acme Supplies Co",
|
||||
amountDue: 1250.5,
|
||||
dueDate: "2026-08-20",
|
||||
payDate: "2026-08-15",
|
||||
status: "approved",
|
||||
paymentMethod: "check",
|
||||
},
|
||||
{
|
||||
id: "inv-2",
|
||||
invoiceNumber: "INV-1002",
|
||||
vendorId: "vendor-2",
|
||||
vendorName: "Harbor Maintenance LLC",
|
||||
amountDue: 4800,
|
||||
dueDate: "2026-08-25",
|
||||
payDate: null,
|
||||
status: "pending_approval",
|
||||
paymentMethod: "ach",
|
||||
},
|
||||
{
|
||||
id: "inv-3",
|
||||
invoiceNumber: "INV-1003",
|
||||
vendorId: "vendor-3",
|
||||
vendorName: "Northwind Utilities",
|
||||
amountDue: 312.75,
|
||||
dueDate: "2026-08-12",
|
||||
payDate: "2026-08-10",
|
||||
status: "approved",
|
||||
paymentMethod: "check",
|
||||
},
|
||||
];
|
||||
|
||||
/** Mocks are the default data path until the API is wired. */
|
||||
export function areMocksEnabled(): boolean {
|
||||
return import.meta.env.VITE_USE_MOCKS !== "false";
|
||||
}
|
||||
|
||||
export function getMockInvoices(): MockInvoice[] {
|
||||
if (!areMocksEnabled()) {
|
||||
return [];
|
||||
}
|
||||
return mockInvoices;
|
||||
}
|
||||
|
||||
export function getMockVendors(): MockVendor[] {
|
||||
if (!areMocksEnabled()) {
|
||||
return [];
|
||||
}
|
||||
return mockVendors;
|
||||
}
|
||||
26
src/pages/index.tsx
Normal file
26
src/pages/index.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { Box, Container, Stack, Typography } from "@mui/material";
|
||||
import { areMocksEnabled, getMockInvoices, getMockVendors } from "@/mocks";
|
||||
|
||||
export default function HomePage() {
|
||||
const invoices = getMockInvoices();
|
||||
const vendors = getMockVendors();
|
||||
|
||||
return (
|
||||
<Box component="main" sx={{ minHeight: "100vh", py: 8 }}>
|
||||
<Container maxWidth="md">
|
||||
<Stack spacing={2}>
|
||||
<Typography component="h1" variant="h1">
|
||||
Sea Haven AP
|
||||
</Typography>
|
||||
<Typography color="text.secondary">
|
||||
Internal accounts payable automation. Dashboard grids land in AP-5.
|
||||
</Typography>
|
||||
<Typography variant="body2" data-testid="mocks-status">
|
||||
Mocks {areMocksEnabled() ? "enabled" : "disabled"} — {vendors.length} vendors,{" "}
|
||||
{invoices.length} invoices
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
17
src/providers/AppProviders.tsx
Normal file
17
src/providers/AppProviders.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material";
|
||||
import { queryClient } from "@/lib/query-client";
|
||||
import { muiTheme } from "@/lib/mui-theme";
|
||||
|
||||
export function AppProviders({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeProvider theme={muiTheme}>
|
||||
<CssBaseline />
|
||||
{children}
|
||||
{import.meta.env.DEV ? <ReactQueryDevtools initialIsOpen={false} /> : null}
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
1
src/test/setup.ts
Normal file
1
src/test/setup.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
import "@testing-library/jest-dom/vitest";
|
||||
9
src/vite-env.d.ts
vendored
Normal file
9
src/vite-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_USE_MOCKS: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"composite": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
20
tsconfig.node.json
Normal file
20
tsconfig.node.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"strict": true,
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./node_modules/.tmp/tsconfig.node",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts", "vitest.config.ts", "playwright.config.ts"]
|
||||
}
|
||||
19
vite.config.ts
Normal file
19
vite.config.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import generouted from "@generouted/react-router/plugin";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path from "node:path";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), generouted()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
build: {
|
||||
outDir: "dist",
|
||||
},
|
||||
});
|
||||
19
vitest.config.ts
Normal file
19
vitest.config.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import react from "@vitejs/plugin-react";
|
||||
import path from "node:path";
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
setupFiles: ["./src/test/setup.ts"],
|
||||
globals: false,
|
||||
include: ["src/**/*.test.{ts,tsx}"],
|
||||
exclude: ["e2e/**", "node_modules/**"],
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue