a65ab711ef
W-5 expense editor (S-7): amount, description, category quick row and "Alla" grid with the §9.11 suggestion for a new expense (CS-1 to CS-7), the four fixed option rows (D-102), split modes default, ratio, exact and full with live shares, notes, inline §9.6 validation, one POST /sync per save, delete with confirmation, the discard dialog on Cancel, Esc and in-app navigation (D-080), rejection and network failures shown on the page with the form kept (§15.5). Defaults per AC-8.6 with this browser's last account and category; ?account= preselects the opener's account. W-6 expense detail (S-8): hero, facts, shares and mode, recurring template badge, the AC-12.4 timeline, deleted and missing states. W-12 search with filters in the URL query, count and sum, rows a hundred at a time, and the CSV export (UTF-8 BOM, semicolons, CRLF, decimal comma, ISO dates, header in the page language), built in the browser. The §9.11 port passes contracts/test-vectors/suggest_category.json (65 cases); the keyword table is a Vite raw import of contracts/category-keywords.csv, which needs ../contracts in server.fs.allow (vite.config.ts). Shared editor fields of design-spec §3.7 in web/src/components/editor/ for W5's template editor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import react from '@vitejs/plugin-react';
|
|
import { defineConfig } from 'vitest/config';
|
|
|
|
interface PackageJson {
|
|
version: string;
|
|
kvitt: { versionCode: number };
|
|
}
|
|
|
|
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8')) as PackageJson;
|
|
|
|
/** API paths of contracts/openapi.yaml; `npm run dev` forwards them to a local kvitt-server (README). */
|
|
const API_PREFIXES = [
|
|
'/health',
|
|
'/setup',
|
|
'/invites',
|
|
'/me',
|
|
'/users',
|
|
'/devices',
|
|
'/accounts',
|
|
'/categories',
|
|
'/recurring-templates',
|
|
'/sync',
|
|
'/settlements',
|
|
'/apk',
|
|
];
|
|
const devApi = process.env.KVITT_DEV_API ?? 'http://localhost:8080';
|
|
|
|
export default defineConfig({
|
|
base: '/app/',
|
|
plugins: [react()],
|
|
define: {
|
|
__KVITT_VERSION_NAME__: JSON.stringify(pkg.version),
|
|
__KVITT_VERSION_CODE__: JSON.stringify(pkg.kvitt.versionCode),
|
|
},
|
|
server: {
|
|
// web/ itself, plus contracts/ for the one contract file the app imports at build time: W2's keyword table
|
|
// (SPEC §15.7, D-055). Vite refuses `?raw` imports outside these paths, in Vitest as well as the dev server, and
|
|
// matches the id with its query, so the entry must be the directory, not the file.
|
|
fs: { allow: ['.', '../contracts'] },
|
|
proxy: Object.fromEntries(API_PREFIXES.map((prefix) => [prefix, { target: devApi, changeOrigin: true }])),
|
|
},
|
|
build: {
|
|
outDir: 'dist',
|
|
sourcemap: false,
|
|
rolldownOptions: {
|
|
output: {
|
|
// Libraries in their own chunks: they change less often than the app, so browsers keep them across deploys.
|
|
codeSplitting: {
|
|
groups: [
|
|
{ name: 'react', test: /node_modules[\\/](react|react-dom|scheduler)[\\/]/ },
|
|
{ name: 'vendor', test: /node_modules[\\/]/ },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
test: {
|
|
environment: 'jsdom',
|
|
setupFiles: ['src/test/setup.ts'],
|
|
include: ['src/**/*.test.{ts,tsx}'],
|
|
css: { modules: { classNameStrategy: 'non-scoped' } },
|
|
},
|
|
});
|