TECHNICAL GUIDE - mattnotmatthew/budget-tracker GitHub Wiki
Comprehensive technical documentation for the Budget vs Actual Tracker 2025 React TypeScript application, covering architecture, business logic, data flow, and development guidelines.
- Architecture Overview
- Technology Stack
- Project Structure
- Core Components
- Business Logic
- Data Models & Types
- State Management
- Utility Functions
- File Management System
- Calculation Engine
- UI/UX Implementation
- Feature Flags
- Testing Strategy
- Development Workflow
- Deployment & Maintenance
The Budget Tracker follows a component-based architecture with clear separation of concerns:
├── Presentation Layer (React Components)
├── Business Logic Layer (Context + Reducers)
├── Data Access Layer (File Management + Utilities)
└── Calculation Engine (Pure Functions)
- Unidirectional Data Flow: State flows down, events flow up
- Pure Functions: All calculations are side-effect free
- Type Safety: Comprehensive TypeScript typing throughout
- Component Isolation: Each component handles specific responsibilities
- Immutable State: State updates through reducers only
- File System API: Native browser file handling
- Local Storage: Cached data and user preferences
- Context API: Global state management
- Custom Hooks: Reusable business logic
Frontend Framework:
- React 18: Latest features including concurrent rendering
- TypeScript 4.9+: Full type safety and modern language features
- JSX/TSX: Component templating with type checking
State Management:
- React Context API: Global application state
- useReducer Hook: Complex state logic management
- Custom Hooks: Reusable stateful logic
Styling & UI:
- CSS3: Modern CSS with Grid and Flexbox
- CSS Modules: Scoped styling approach
- Responsive Design: Mobile-first responsive layout
Development Tools:
- Create React App: Build toolchain and development server
- ESLint: Code quality and consistency
- Prettier: Code formatting
- TypeScript Compiler: Type checking and compilation
{
"react": "^18.0.0",
"react-dom": "^18.0.0",
"typescript": "^4.9.0",
"date-fns": "^2.29.0",
"recharts": "^2.5.0",
"file-saver": "^2.0.5"
}
src/
├── components/ # React components
│ ├── Dashboard.tsx # Main application shell
│ ├── MonthlyView.tsx # Monthly budget interface
│ ├── YearlyBudgetDashboard.tsx # Executive dashboard
│ ├── VendorManagement.tsx # Vendor tracking
│ ├── ExecutiveSummary/ # Executive reporting
│ ├── Planning/ # Future planning features
│ └── ...
├── context/ # State management
│ └── BudgetContext.tsx # Global state and actions
├── types/ # TypeScript definitions
│ └── index.ts # Core type definitions
├── utils/ # Utility functions
│ ├── budgetCalculations.ts # Calculation engine
│ ├── currencyFormatter.ts # Display formatting
│ ├── fileManager.ts # File operations
│ ├── featureFlags.ts # Feature toggles
│ └── ...
├── styles/ # CSS styling
│ └── App.css # Global styles
└── services/ # External integrations
└── ...
-
Components: PascalCase (e.g.,
BudgetInput.tsx
) -
Utilities: camelCase (e.g.,
budgetCalculations.ts
) -
Types: PascalCase interfaces (e.g.,
BudgetEntry
) -
Constants: UPPER_SNAKE_CASE (e.g.,
COMP_AND_BENEFITS_CATEGORIES
)
Purpose: Main application container and navigation hub
Key Responsibilities:
- Year and quarter selection
- View mode coordination
- Global navigation state
- Feature integration point
State Management:
const { state, dispatch } = useBudget();
const [activeView, setActiveView] = useState<ViewMode>("monthly");
Key Functions:
-
handleYearChange()
: Updates global year selection -
handleQuarterChange()
: Updates quarter focus -
navigateToView()
: Switches between main views
Purpose: Detailed monthly budget entry and management
Core Features:
- IOSToggle integration for Final/Forecast modes
- Category-based budget entry
- Real-time variance calculation
- Quarterly summary aggregation
Data Flow:
Monthly Entries → Category Summaries → Quarterly Aggregation → YTD Calculations
Key Functions:
-
calculateMonthlyData()
: Processes monthly entries -
handleToggleMode()
: Switches Final/Forecast states -
generateQuarterlySummary()
: Aggregates monthly data
Purpose: Executive-level performance monitoring
Performance Metrics:
- YTD Actual vs Budget
- Full Year Forecast
- Budget Utilization
- Remaining Budget Analysis
Calculation Dependencies:
-
calculateYTDData()
: Year-to-date aggregation -
calculateBudgetTracking()
: Performance metrics -
generateExecutiveInsights()
: Automated insights
Purpose: Vendor tracking and budget allocation
Features:
- Vendor data entry with validation
- Read-only mode for completed entries
- Edit mode toggle functionality
- Automatic row creation
Data Model:
interface VendorData {
id: string;
vendorName: string;
billingType: string;
budget: number;
notes: string;
year: number;
createdAt: Date;
updatedAt: Date;
}
Core Concept: Each month can be in "Final" or "Forecast" mode
Final Mode:
- Uses actual spending data
- Adjustments appear in Actual column
- Variance calculated against actuals
- Represents completed/closed months
Forecast Mode:
- Uses projected spending data
- Adjustments appear in Forecast column
- Variance calculated against forecasts
- Represents future/projected months
Implementation:
const getMonthForecastMode = (month: number): boolean => {
return state.monthlyForecastModes[state.selectedYear]?.[month] ?? false;
};
// true = Final (actual), false = Forecast (projected)
Primary Rule:
if (actual !== 0) {
variance = (actual - budget) * -1;
} else {
variance = (reforecast - budget) * -1;
}
Interpretation:
- Positive Variance: Under budget (good)
- Negative Variance: Over budget (needs attention)
Purpose: Net spending after adjustments
Formula:
const budgetTrackingActual = netTotal.actual - netTotal.adjustments;
const budgetTrackingReforecast = netTotal.actual === 0
? netTotal.reforecast - netTotal.adjustments
: netTotal.reforecast;
Cost of Sales Categories:
const COST_OF_SALES_CATEGORIES = [
"cos-recurring-software",
"cos-onetime-software",
"cos-recurring-service",
"cos-onetime-service",
"cos-reclass-from-opex",
"cos-other"
];
OpEx Categories:
const COMP_AND_BENEFITS_CATEGORIES = [
"opex-base-pay",
"opex-capitalized-salaries",
"opex-commissions",
"opex-bonus",
"opex-benefits",
// ... more categories
];
BudgetEntry:
interface BudgetEntry {
id: string;
categoryId: string;
year: number;
quarter: number;
month: number;
budgetAmount: number;
actualAmount?: number;
reforecastAmount?: number;
adjustmentAmount?: number;
notes?: string;
createdAt: Date;
updatedAt: Date;
}
BudgetState:
interface BudgetState {
entries: BudgetEntry[];
categories: BudgetCategory[];
viewMode: ViewMode;
selectedYear: number;
selectedQuarter?: number;
currentFile?: FileInfo;
yearlyBudgetTargets: Record<number, number>;
monthlyForecastModes: Record<number, Record<number, boolean>>;
persistence: PersistenceState;
vendorData?: VendorData[];
// Planning features (optional)
planningMode?: boolean;
planningData?: Record<number, PlanningData>;
}
Strict TypeScript Configuration:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
Type Guards:
const isBudgetEntry = (obj: any): obj is BudgetEntry => {
return obj && typeof obj.id === 'string' && typeof obj.categoryId === 'string';
};
BudgetContext.tsx provides global state management:
const BudgetContext = createContext<{
state: BudgetState;
dispatch: Dispatch<BudgetAction>;
} | null>(null);
Action Types:
type BudgetAction =
| { type: "ADD_ENTRY"; payload: BudgetEntry }
| { type: "UPDATE_ENTRY"; payload: { id: string; updates: Partial<BudgetEntry> } }
| { type: "DELETE_ENTRY"; payload: string }
| { type: "SET_YEAR"; payload: number }
| { type: "TOGGLE_FORECAST_MODE"; payload: { year: number; month: number } }
| { type: "LOAD_DATA"; payload: BudgetState }
// ... more actions
Reducer Implementation:
const budgetReducer = (state: BudgetState, action: BudgetAction): BudgetState => {
switch (action.type) {
case "ADD_ENTRY":
return {
...state,
entries: [...state.entries, action.payload],
persistence: { ...state.persistence, hasUnsavedChanges: true }
};
// ... other cases
}
};
useBudget Hook:
export const useBudget = () => {
const context = useContext(BudgetContext);
if (!context) {
throw new Error('useBudget must be used within a BudgetProvider');
}
return context;
};
Core Calculation Functions:
// Calculate category summary for any period
export const calculateCategorySummary = (
entries: BudgetEntry[],
category: BudgetCategory,
quarter?: number,
month?: number,
year?: number
): CategorySummary => {
// Implementation details...
};
// Calculate YTD data through specific month
export const calculateYTDData = (
entries: BudgetEntry[],
categories: BudgetCategory[],
year: number
): { data: QuarterlyData; lastMonthWithActuals: number } => {
// Implementation details...
};
// Budget tracking calculations
export const calculateBudgetTracking = (
netTotal: NetTotalData,
isForecastMode?: boolean
) => {
// Implementation details...
};
Excel-Style Formatting:
export const formatCurrencyExcelStyle = (value: number): string => {
if (value === 0) return '-';
const absValue = Math.abs(value);
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(absValue);
return value < 0 ? `(${formatted.replace('$', '$')})` : formatted;
};
File Operations:
// Save budget data to file
export const saveBudgetData = async (
state: BudgetState,
filename?: string
): Promise<void> => {
// Implementation details...
};
// Load budget data from file
export const loadBudgetData = async (
file: File
): Promise<BudgetState> => {
// Implementation details...
};
Auto-Save Features:
- Periodic auto-save every 5 minutes
- Save on navigation between views
- Save on year/quarter changes
- Recovery from unexpected closures
File Formats:
- Native Format: JSON with full application state
- Export Formats: CSV, HTML, PDF (via print)
- Import Formats: JSON, CSV (limited)
State Caching:
interface PersistenceState {
hasUnsavedChanges: boolean;
lastCacheUpdate: Date | null;
lastFileSave: Date | null;
isFirstTimeUser: boolean;
cacheAutoSaveInterval: number;
}
File System Integration:
- Uses modern File System Access API when available
- Fallback to traditional download/upload for compatibility
- Maintains file handles for direct file updates
Data Aggregation Flow:
Individual Entries → Category Summaries → Monthly Totals → Quarterly Aggregation
Key Functions:
-
calculateMonthlyData()
: Processes single month -
calculateQuarterlyData()
: Aggregates quarter -
calculateYTDData()
: Year-to-date through specific month
Multi-Level Variance:
- Category Level: Individual category performance
- Group Level: Cost of Sales vs OpEx performance
- Total Level: Overall budget performance
Smart Variance Logic:
- Uses actual amounts when available
- Falls back to forecast for future periods
- Adjusts calculation based on IOSToggle state
Calculation Caching:
const memoizedCalculation = useMemo(() => {
return calculateComplexData(entries, categories, year);
}, [entries, categories, year]);
Efficient Updates:
- Only recalculate affected periods
- Batch multiple updates together
- Debounce rapid state changes
Breakpoint Strategy:
/* Mobile-first approach */
.container {
width: 100%;
}
@media (min-width: 768px) {
.container { width: 750px; }
}
@media (min-width: 1024px) {
.container { width: 970px; }
}
Higher-Order Components:
const withErrorBoundary = <P extends object>(
Component: React.ComponentType<P>
) => {
return (props: P) => (
<ErrorBoundary>
<Component {...props} />
</ErrorBoundary>
);
};
Render Props Pattern:
interface DataProviderProps {
children: (data: CalculatedData) => React.ReactNode;
}
const DataProvider: React.FC<DataProviderProps> = ({ children }) => {
const data = useCalculatedData();
return <>{children(data)}</>;
};
ARIA Support:
<button
aria-label="Toggle forecast mode for January"
aria-pressed={isForecastMode}
role="switch"
>
{isForecastMode ? 'Forecast' : 'Final'}
</button>
Keyboard Navigation:
- Tab order management
- Enter/Space for activation
- Escape for cancellation
- Arrow keys for grid navigation
Feature Flag System:
const FEATURE_FLAGS = {
BUDGET_PLANNING: process.env.REACT_APP_ENABLE_BUDGET_PLANNING === 'true',
ADVANCED_ANALYTICS: process.env.REACT_APP_ENABLE_ANALYTICS === 'true',
EXPORT_FEATURES: process.env.REACT_APP_ENABLE_EXPORTS === 'true'
};
export const isFeatureEnabled = (flag: keyof typeof FEATURE_FLAGS): boolean => {
return FEATURE_FLAGS[flag] ?? false;
};
Conditional Rendering:
{isFeatureEnabled('BUDGET_PLANNING') && (
<PlanningDashboard />
)}
.env Files:
# .env.development
REACT_APP_ENABLE_BUDGET_PLANNING=true
REACT_APP_ENABLE_ANALYTICS=false
# .env.production
REACT_APP_ENABLE_BUDGET_PLANNING=false
REACT_APP_ENABLE_ANALYTICS=true
Unit Tests (70%):
- Utility functions
- Pure calculation logic
- Individual component behavior
Integration Tests (20%):
- Component interactions
- Context provider behavior
- Data flow validation
End-to-End Tests (10%):
- Complete user workflows
- File operations
- Cross-browser compatibility
Jest Configuration:
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
moduleNameMapping: {
'\\.(css|less|scss)$': 'identity-obj-proxy'
}
};
React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import { BudgetProvider } from '../context/BudgetContext';
test('should calculate variance correctly', () => {
render(
<BudgetProvider>
<MonthlyView />
</BudgetProvider>
);
// Test implementation...
});
- Clone Repository
-
Install Dependencies:
npm install
-
Environment Setup: Configure
.env
files -
Start Development:
npm start
-
Run Tests:
npm test
ESLint Configuration:
{
"extends": [
"react-app",
"@typescript-eslint/recommended"
],
"rules": {
"no-console": "warn",
"@typescript-eslint/explicit-function-return-type": "error"
}
}
Prettier Configuration:
{
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"semi": true
}
Branch Strategy:
-
main
: Production-ready code -
develop
: Integration branch -
feature/*
: Feature development -
hotfix/*
: Critical fixes
Commit Message Format:
type(scope): description
feat(dashboard): add quarterly summary component
fix(calculations): correct variance calculation logic
docs(readme): update installation instructions
Production Build:
npm run build
Build Output:
build/
├── static/
│ ├── css/
│ ├── js/
│ └── media/
├── index.html
└── asset-manifest.json
Key Metrics:
- Bundle size optimization
- Load time performance
- Memory usage monitoring
- Error tracking and reporting
Regular Maintenance:
- Dependency updates
- Security patch application
- Performance optimization
- Bug fix deployment
Monitoring:
- Application error tracking
- User feedback collection
- Performance metric analysis
- Feature usage analytics
// Add new budget entry
dispatch({
type: "ADD_ENTRY",
payload: newEntry
});
// Update existing entry
dispatch({
type: "UPDATE_ENTRY",
payload: { id: entryId, updates: changes }
});
// Toggle forecast mode
dispatch({
type: "TOGGLE_FORECAST_MODE",
payload: { year: 2025, month: 3 }
});
// Calculate category summary
const summary = calculateCategorySummary(
entries,
category,
quarter,
month,
year
);
// Calculate YTD data
const { data, lastMonthWithActuals } = calculateYTDData(
entries,
categories,
year
);
// Format currency
const formatted = formatCurrencyExcelStyle(amount);
This technical documentation provides a comprehensive overview of the Budget Tracker application's architecture, implementation details, and development practices. The application is built with scalability, maintainability, and type safety as core principles.
For user-facing documentation, see the [User Guide](./USER_GUIDE]].
Last Updated: January 2025
Version: 2025.1
Maintainers: Development Team
License: Internal Use Only