Instruction file imported from naorleizer/pixie-angel (
.github/instructions/frontend.instructions.md). Copyright stays with the author.
Frontend Instructions for Pixie
These instructions apply to all work in the frontend/ directory.
Frontend Architecture
File Organization
frontend/
├── Dockerfile # Multi-stage build (Node 20 → nginx:alpine)
├── nginx.conf # Serves SPA, proxies /api/ to backend
├── public/
│ └── assets/ # Static images (db/, images/) - copied by Vite to dist/
├── src/
│ ├── screens/ # HTML templates (one per view)
│ │ ├── login.html
│ │ ├── dashboard.html
│ │ ├── chat.html
│ │ ├── challenge.html # Challenge creation form
│ │ ├── challenges.html # All challenges list with filters
│ │ ├── account-settings.html # Combined settings (privacy + account mgmt)
│ │ ├── transactions.html
│ │ ├── import-transactions.html
│ │ └── ...
│ ├── services/ # Shared services
│ │ ├── tour-manager.js # Shepherd.js wrapper for interactive tours
│ │ ├── tour-service.js # Tour registry + help button handler
│ │ └── location-service.js # Geolocation permissions
│ ├── tours/ # Tour configurations (one per screen)
│ │ ├── dashboard-tour.js
│ │ ├── chat-tour.js
│ │ ├── challenges-tour.js
│ │ └── transactions-tour.js
│ ├── main.js # App initialization & screen injection
│ ├── navigation.js # URL routing & back button support
│ ├── api.js # Backend API client wrapper
│ ├── state.js # Global app state
│ ├── chat.js # Chat UI logic & message handling
│ ├── dashboard.js # Dashboard carousel, challenge cards, modal
│ ├── challenge.js # Challenge creation form logic
│ ├── challenges.js # Challenges list, filters, detail modal (REAL-TIME UPDATES)
│ ├── auth.js # Login/register/auth logic + account settings init
│ ├── transactions.js # Transactions UI & filtering
│ ├── import-transactions.js # Import flow UI
│ ├── budget.js # Budget adjustment logic
│ ├── notifications.js # Notification handling
│ ├── onboarding.js # Onboarding flow
│ └── styles.css # Minimal custom CSS (use Tailwind mostly)
├── index.html # Root div shell only
├── vite.config.js # Vite config (HMR, build)
├── tailwind.config.js # Tailwind setup
├── package.json # Dependencies (Vite, Tailwind, PostCSS)
└── .env.example # Example env vars (VITE_API_URL)
Static Assets Note: All static assets (images, etc.) must be in public/assets/ for Vite to copy them to the build output. Reference them with /assets/... paths in HTML/CSS.
How Screens Work
- HTML templates are stored in
src/screens/as plain HTML files main.jsimports all screens usingimport screenHtml from "./screens/screen-name.html?raw"- At DOMContentLoaded,
main.jsinjects all screen HTML into#app-container - Each screen has a unique
id="screen-*"for CSS display toggling - Navigation uses
navigate(screenName)to show/hide screens and update the URL hash
When adding a new screen:
- Create
src/screens/my-screen.htmlwith a root<section id="screen-my-screen"> - Import it in
main.js:import myScreenHtml from "./screens/my-screen.html?raw"; - Add to injection:
appContainer.innerHTML = ... + myScreenHtml + ...; - Add to screenMap in
navigation.js:"my-screen": "screen-my-screen", - Call
navigate("my-screen")to show it
State Management Pattern
- No Redux/MobX — Just a simple global object in
src/state.js - Example:
export const state = { screenStack: [], currentUser: null, challenges: [], }; - Update state directly:
state.currentUser = userData - No watchers/observers — manually update UI after state changes
API Communication Pattern
ALWAYS use src/api.js — Never call fetch() directly in components.
See src/api.js for all backend communication functions. Key patterns:
- All functions use
apiRequest()for JWT injection and error handling - Signed amounts (positive=savings, negative=spending) passed as numbers
- Filters (current/past/all) as query parameters
Challenge API Functions available in src/api.js:
getChallenges(filter)— List challenges (filter: current, past, all)getChallengeDetail(challengeId)— Get single challenge with historycreateChallenge(data)— Create new challengeaddChallengeUpdate(challengeId, amount, description)— Add signed amount update
Navigation & URL Routing
Use navigate() for screen changes, not showScreen(). This ensures the URL updates and the back button works.
Pattern:
navigate(screenName)= user-initiated navigation → updates URL hash, enables back buttonshowScreen(screenId, setHistory)= internal app logic only → no URL update
See src/navigation.js for implementation. Always use navigate() from user interactions (button clicks, sidebar links, etc.)
Challenge UI Patterns
The app has three challenge-related screens/views. See src/challenges.js and src/dashboard.js for implementation details:
-
Dashboard Challenge Carousel (src/dashboard.js):
- Swipeable carousel showing current challenges
- Cards display: title, deadline, status badge, current amount with arrow, progress bar
- Clicking card opens modal on dashboard (not navigation) for detail view
- Balance widget shows total: "Saved: X₪" (green) or "Overspent: X₪" (red)
- Auto-refreshes when returning to dashboard
-
All Challenges Screen (src/challenges.html + src/challenges.js):
- Filter tabs: Current / Past / All
- Card grid showing matching challenges
- Clicking card opens detail modal within this screen
- New Challenge button navigates to creation form
-
Challenge Detail Modal (shared component on both screens):
- Shows title, status badge, deadline, current amount, target
- Updates timeline (newest first) with signed amounts and descriptions
- Add update form (only for active challenges)
- Close on background click or close button
Key UI Details:
- Status badges: On Track/Below Target (active), Completed/Failed (past)
- Amount arrows: ↑ green for positive (savings), ↓ red for negative (spending)
- Updates display signed amounts; frontend calculates from sum of all updates
Component Patterns
Page Initialization (see src/challenges.js or similar modules):
- Export
initMyScreen()function that sets up event listeners and loads initial data - Called from src/main.js or src/navigation.js after DOM ready
- Always check element exists before binding events
Event Handling with API:
- Import API function from src/api.js
- Wrap calls in try/catch
- Update UI after successful response
- Show error message to user on failure
DOM Rendering:
- Use
innerHTMLwith template literals for dynamic content - Set
onclickhandlers directly on HTML elements or via event delegation - Keep rendering functions pure (no side effects except DOM updates)
Common Tasks
Account Settings Screen (src/screens/account-settings.html)
Unified settings screen combining data personalization toggles and user preference management. See src/auth.js for initialization logic.
Structure (initialized by initAccountManagement() function):
-
Data Personalization Section — 4 toggle switches (Interests, Location, Motivations, Communication Style)
- Each toggle persists preference via API
- Includes link to privacy policy
- Toggles managed by
togglePreference()function
-
Your Interests Section — Dynamic pill buttons for interest selection
- Pills generated from
VALID_INTERESTSarray - Click toggles selection (adds ✕ symbol when selected)
- Save button updates user preferences via
updateUserPreferences()API call
- Pills generated from
-
Financial Persona Section — Dropdown selector
- Options: The Analyst, The Driver, The Promoter, The Supportive
- Loaded from user's
preferred_personafield - Save button persists choice
-
Your Motivations Section — Dynamic pill buttons for motivation selection
- Pills generated from
VALID_MOTIVATIONSarray - Same toggle behavior as interests
- Save button updates via API
- Pills generated from
Key Implementation Notes:
- Replaces old separate
screen-privacyandscreen-account-managementscreens - Navigation handler in src/navigation.js calls combined initialization
initAccountManagement()loads both preference toggles and interest/motivation pills on screen load- All API calls use
updateUserPreferences()from src/api.js - Location permission syncing via
syncLocationPermission()from location-service.js
Tour System (Interactive Walkthroughs)
Shepherd.js-based guided tours for each screen. Users click the "?" help button to start. See src/services/tour-manager.js for core implementation.
Architecture:
tour-manager.js— Wraps Shepherd.js with mobile optimizations (auto-positioning, scroll-to-view, large touch targets)tour-service.js— Registry mapping screen IDs to tour configs;openHelp()handler for help buttontours/*.js— Individual tour configurations (dashboard, chat, challenges, transactions)
Adding a New Tour (2-step process):
-
Create config file
tours/my-screen-tour.js:export const myScreenTour = { title: "My Screen Tour", steps: [ { target: "#element-id", // CSS selector title: "Feature Name", // Bold heading description: "Friendly explanation.", // 2-3 sentences max position: "bottom" // Auto-adjusted for mobile } ] }; -
Register in
tour-service.js:import { myScreenTour } from '../tours/my-screen-tour.js'; const tourRegistry = { 'screen-my-screen': myScreenTour // Map screen ID to config };
Mobile Optimizations (automatic):
- Tooltips auto-position to bottom on screens < 768px
- Elements scroll into view when highlighted
- 48px+ touch-friendly buttons
- Responsive text sizing
Current Tours:
- ✅ Dashboard, Chat, Challenges, Transactions configured
- ⭕ Other screens show "Help for this page is coming soon!" message
Best Practices:
- Keep tours 2-6 steps (focus on high-value features only)
- Use plain English ("Save your progress" not "Persist state")
- Target interactive elements (buttons, inputs, key sections)
- Descriptions should be 1-2 sentences max
Transaction List Pattern (Mobile-Friendly 2-Row Layout)
The transactions list uses a compact 2-row layout per transaction for optimal mobile readability. See src/transactions.js for implementation.
Layout:
- Row 1: Date | Description | Category Dropdown
- Row 2: Account/Metadata | Amount (color-coded: green for income, red for expenses)
Key Patterns:
- Row 1 has light border (slate-100) separating related rows
- Row 2 has darker border (slate-300) separating transactions
- Compact padding: py-1 throughout
- Amounts color-coded based on sign (income positive green, expenses negative red)
Adding an API Call
- Add function to src/api.js that calls
apiRequest() - Import and use in your component
- Handle errors with try/catch
Adding a New Screen
- Create
src/screens/my-screen.htmlwith root<section id="screen-my-screen"> - Import in src/main.js
- Add to
screenMapin src/navigation.js - Create init function in relevant module or main.js
- Call
navigate("my-screen")to show it
Updating Global State
- Modify src/state.js
- Update state directly:
state.myVar = value - Re-render UI after state change (manually)
Styling
- Use Tailwind CSS classes in HTML
- Avoid custom CSS unless necessary (animations, complex layouts)
- Keep custom CSS in
styles.cssminimal and well-documented
Common Patterns to Follow
Error Handling
Always wrap async calls in try/catch and show user-friendly error messages.
Loading States
Disable buttons and show "Loading..." text during API calls. Use finally to re-enable after completion.
Authentication Check
Auth is handled by src/auth.js. checkAuthAndRedirect() runs on page load and redirects logged-out users to login. Don't manually check tokens in components.
Testing & Debugging
- Browser DevTools: F12 → Console to see logs and errors
- Network Tab: Check API requests and responses
- localStorage: Check
pixie_auth_tokenis present after login - Component State: Add
console.log(state)to debug state issues
Before Submitting
- ✅ No direct
fetch()calls — useapi.jswrappers - ✅ New screens use
navigate()for routing (URL-aware) - ✅ Error handling on all async/await calls
- ✅ Manual testing in browser (login → navigate → check console)
- ✅ No hardcoded user data (mock OK for demo, but flag for real API integration)
Remember: Frontend is Vanilla JS with no build artifacts. Keep it simple, use patterns from existing code, and trust the navigation system for routing.
Maintaining These Instructions
These instructions serve as style guides and pattern references, not comprehensive code documentation. Key principles for keeping them current:
Code Examples vs References
- Use code examples only for essential patterns that can't be explained concisely (e.g., JSX-like rendering, event binding patterns)
- Replace full code blocks with source file references — e.g., instead of copying entire function code, link to src/challenges.js and describe what it does
- Agents should read source code for implementation details; instructions point them there
Single Source of Truth
- Keep actual implementations in source files, not replicated in docs
- Update code first, then update references in instructions
- Link to specific files using markdown file paths:
[api.js](../../frontend/src/api.js)for source references - Never copy/paste code that will drift from reality
Structure for New Features
When adding documentation for a new screen or feature:
- Create HTML file in
src/screens/ - Create/update JS module in
src/with init function - Add API wrappers to src/api.js
- Document in instructions:
- List new files in file organization section
- Describe UI patterns and behavior
- Link to implementation files
- Show only critical patterns (data flow, key state updates, modal behavior)
What to Document vs What to Link
| Should Document | Should Link To Source |
|---|---|
| Navigation patterns (navigate vs showScreen) | Full navigation.js implementation |
| API function signatures | Full function implementations |
| UI layout patterns (2-row, carousel, modal) | Full screen HTML/JS |
| State structure | Full state.js definitions |
| Event handling patterns | Complete event handler code |
| Styling classes & approach (Tailwind) | Full CSS/Tailwind usage |
Keeping References Fresh
- When moving/renaming files, update all markdown links
- When adding screens, add to the file organization section
- When updating a component, update pattern description AND link to source
- Test that links resolve before submitting