A responsive single‑page application for transferring money and viewing transaction history.
Built with Angular 20, standalone components, signals, reactive forms, Angular Material Dialog, SCSS with BEM, and a desktop‑first responsive approach.
- Overview
- Technology Stack
- Key Decisions & Motivations
- Project Structure
- Setup & Running
- Features Implemented
- Responsive Design
The application consists of two main parts:
- Transfer Money Form – allows users to enter a recipient and amount, preview the transfer in a dialog, and confirm. The balance updates immediately and a new transaction appears at the top of the list.
- Transactions List – displays recent transactions with live search (by merchant/beneficiary) and sorting (by date, beneficiary, amount). The sort order persists across all sorting options.
All components are standalone, use signals for reactive state, and follow a clean, DRY, BEM‑based SCSS methodology.
| Tool / Library | Purpose |
|---|---|
| Angular v20 | Core framework, standalone components |
| Angular Signals | Reactive state management (no external lib) |
| Angular Reactive Forms | Form validation & control |
| Angular Material Dialog | Preview modal (lightweight, accessible) |
| SCSS | Custom styling with BEM |
| Desktop‑first CSS | Responsive design via max-width media queries |
| CSS Grid / Flexbox | Layout control |
| TypeScript | Strict typing, interfaces |
| Google Fonts (Kanit) | Clean, modern typography |
No CSS frameworks (Tailwind, Bootstrap) were used – all styles are handcrafted for maximum control and minimal bundle size.
- Why? Angular v20 promotes standalone as the default; signals provide fine‑grained reactivity without zone.js overhead.
- Benefit: Cleaner code, better performance, and future‑proof.
- Why? After initial attempts with Angular Material
mat-form-field, customisation proved too verbose (::ng-deep, appearance limitations). - Decision: Replaced all Material form fields with plain HTML
<input>+<label>+ custom SCSS. - Result: 100% control over every pixel, no style encapsulation fights, smaller bundle.
- Why? BEM provides a predictable, maintainable naming system. Desktop‑first (
max-width) reduces code complexity – we start from the largest screen and scale down. - Benefit: Readable stylesheets, easy to extend, no CSS leaks.
- Why? Both cards (Transfer Form and Transactions List) share identical header styles (teal background, icon, centered title).
- Implementation: Used Sass placeholders (
%card,%card-header,%card-icon,%card-title) and variables defined insrc/styles.scss, then@extended in each component. - Benefit: Single source of truth – change one file, update both components.
- Why? Building a fully accessible modal from scratch is time‑consuming and error‑prone.
MatDialogis already in the project (viang add @angular/material) and works perfectly. - Trade‑off: Adds a small dependency, but the component is only used for this one feature.
- Why? Requirement: “The Sorting order (ascending/descending) should be persistent across all sorting options.”
- Implementation: The current
sortOrdersignal is not reset when changingsortBy– only toggled when clicking the same field. - Benefit: Consistent UX – users don’t lose their preferred direction when switching sort criteria.
- Why? The app is simple – balance and transactions are managed in the root
Appcomponent using signals and passed down viainput(). - Benefit: Keeps complexity low; follows Angular’s local reasoning principle.
Transfer Money
- Pre‑filled, disabled FROM ACCOUNT with current balance.
- TO ACCOUNT and AMOUNT fields with reactive validation:
- Required
- Minimum: $0.01
- Maximum: $500.00
- Submit button opens a preview dialog showing entered data.
- Transfer button inside dialog confirms – emits event, updates balance, resets form.
- Form reset after successful transfer.
- Fully responsive (mobile, tablet, desktop).
- Displays mock transactions loaded from
src/app/mock/transactions.json. - Live search – filters by
merchantorbeneficiaryon every keystroke. - Clear search – × button appears when search term is non‑empty.
- Sorting by:
- Date (newest/oldest)
- Beneficiary (A–Z / Z–A)
- Amount (ascending/descending)
- Persistent sort order – when switching between sort fields, the current direction (asc/desc) is preserved.
- Table‑like layout on desktop:
- Colored left border (via
categoryCode) - Short date (e.g.,
Oct 19) - Merchant logo (Base64)
- Company name (bold) + payment method (gray) below
- Amount signed (+/–) and colored (credit/debit), right‑aligned
- Colored left border (via
- Mobile‑optimised – below 770px, layout switches to a clean stacked grid:
- Top row: logo, date, amount (right‑aligned)
- Bottom row: merchant + payment method (full width)
- Empty state message when no transactions match the search.
The project is a solid foundation with clean code, modern Angular practices, and full feature implementation. However, it is not yet production‑ready. The following areas require attention before deployment to real users.
Testing No unit, integration, or E2E tests → regressions may go unnoticed.
- Unit tests (Jasmine/Karma) for components, services, sorting/filtering logic.
- Integration tests for user flows (transfer → transaction list).
- E2E tests (Cypress/Playwright) for critical journeys.
Accessibility
- Missing ARIA labels, focus management.
- Some colour contrasts may fail WCAG.
- Keyboard navigation untested.
- Add aria-label to icon buttons, aria-live for errors.
- Trap focus in modal, return focus after dialog close.
- Audit with axe DevTools; fix contrast ratios.
Performance
- No lazy loading – fine now, but doesn’t scale.
- Default change detection (ChangeDetectionStrategy.Default) causes unnecessary checks.
- Base64 logos bloat bundle; background image not optimised.
- Use ChangeDetectionStrategy.OnPush in all components.
- Optimise images (WebP, srcset).
- Add service worker (@angular/pwa) for offline capability.
Error Handling
- No global error handler → uncaught errors break silently.
- No loading/feedback states on form submission.
- No resilience for network failures.
- Implement ErrorHandler to log errors and show user‑friendly UI.
- Add loading spinner & disable button during submit.
- Use toast/snackbar for success/error feedback.
Code Quality
- Mock JSON imported as any – no type safety.
- Hardcoded strings (colours, asset paths) scattered.
- No enforced linting/formatting.
- Define TypeScript interface for mock data.
- Move hardcoded values to constants / environment files.
- Add ESLint, Prettier, Husky pre‑commit hooks.
Environment
- No environment configuration – balance, API endpoints hardcoded.
- Use Angular environments (environment.ts, environment.prod.ts).
- Differentiate dev/prod settings.
Deployment
- No CI/CD pipeline, no automated build/test/deploy.
- Set up GitHub Actions / GitLab CI.
- Deploy to static hosting (Netlify, Vercel).
Documentation
- Add JSDoc comments for public methods & computed signals.
- Create CONTRIBUTING.md.
# 1. Clone the repository
git clone <repo-url>
cd peachtree
# 2. Install dependencies
npm install
# 3. Start the development server
ng serve
# 4. Open the app
http://localhost:4200