The web-based frontend for visualizing and analyzing bowling statistics from PinPal exports.
For an overview of the entire project, see the main README.
This React application allows users to upload PinPal bowling app exports and visualize their bowling statistics through interactive dashboards. All data is processed and stored locally in the browser using IndexedDB for privacy and offline access.
- Import PinPal Data: Upload SQLite database exports from the PinPal mobile app
- Performance Metrics: View bowling averages, strike rates, spare conversion, and game trends
- Visual Analytics: Interactive charts and statistics dashboards
- Offline Storage: All data processed and stored locally in your browser
- Responsive Design: Works on desktop and mobile devices
- Dark Theme: Custom Material Design theme optimized for readability
- Node.js 18+ and npm/pnpm
# Install dependencies
npm install
# Start development server
npm run dev
# Navigate to http://localhost:5173- Navigate to the upload page
- Export your bowling data from PinPal (Settings > Export Database)
- Upload the
.dbfile through the app's onboarding - View your bowling statistics and trends
npm run dev- Start the Vite development server on localhost:5173 with hot reloadnpm run build- Build the project for production (output indist/)npm run lint- Run ESLint on TypeScript/TSX filesnpm run preview- Preview the production build locally
Tests are not currently configured for this project. Testing setup can be added using Vitest or React Testing Library as needed.
src/
├── components/ # React components
│ ├── layout.tsx # Root layout with navigation
│ ├── stat-card.tsx # Statistics card component
│ ├── games-overview.tsx # Games display component
│ ├── league-overview-card.tsx # League summary card
│ ├── ball-card.tsx # Ball statistics card
│ ├── theme-toggle.tsx # Dark/light mode toggle
│ └── ui/ # shadcn/ui components
├── contexts/ # React contexts
│ └── pinpal-service-context.tsx # PinpalService provider
├── lib/ # Utilities
│ ├── theme-provider.tsx # Theme management
│ └── utils.ts # Helper functions
├── pages/ # Route pages
│ ├── home.tsx # Home/dashboard page
│ ├── upload.tsx # Database import page
│ ├── leagues.tsx # League list page
│ ├── league-detail.tsx # League detail page
│ ├── balls.tsx # Ball statistics page
│ └── monthly.tsx # Monthly statistics page
├── services/ # Core services
│ ├── pinpal.service.ts # PinPal database querying
│ ├── db.ts # IndexedDB wrapper (Dexie)
│ ├── pinpal.model.ts # PinPal data models
│ └── pinpal-query.model.ts # Query option types
├── App.tsx # Root component with routing
├── main.tsx # Application entry point
└── index.css # Global styles
PinpalService (src/services/pinpal.service.ts)
- Core service for querying bowling statistics from SQLite databases
- Initializes sql.js from CDN to process SQLite databases in the browser
- Loads SQLite database from IndexedDB via AppDB service
- Provides methods to query leagues, games, ball statistics, and monthly statistics
- Implements complex SQL queries with CTEs for aggregating bowling metrics
- Calculates pin leave types (splits, single pins, regular leaves) on initialization
- Models:
PinpalModelandPinpalQueryModeldefine the data structures
AppDB (src/services/db.ts)
- Dexie wrapper for IndexedDB storage
- Stores the raw SQLite database file (Uint8Array) in IndexedDB
- Provides single table:
databaseFilesindexed bytitle
Import Process
- User uploads PinPal backup file via upload page
PinpalService.importDatabase()extracts SQLite database from backup file by searching for "SQLite format 3" header- Stores extracted database in IndexedDB
- Loads database into sql.js for querying
- React Context for PinpalService singleton (
usePinpalService()hook) - Component-level state using React hooks (
useState,useEffect) - No global state management library (Redux, Zustand, etc.)
Pages (src/pages/):
home.tsx- Dashboard with recent bowling activityupload.tsx- Database file import interfaceleagues.tsx- League list viewleague-detail.tsx- Individual league statistics and gamesballs.tsx- Statistics organized by bowling ballmonthly.tsx- Monthly aggregated statistics and trends
Shared Components (src/components/):
layout.tsx- Root layout with navigationstat-card.tsx- Reusable card for displaying statisticsgames-overview.tsx- Displays game scores and detailsleague-overview-card.tsx- League summary cardball-card.tsx- Ball statistics cardtheme-toggle.tsx- Dark/light mode toggleui/- shadcn/ui component library (buttons, cards, charts, inputs, etc.)
Routes configured in src/App.tsx using React Router v7 with createBrowserRouter:
/- Home page/upload- Upload database/leagues- League list/league/:id- League detail/balls- Ball statistics/monthly- Monthly trends
- React 19 with TypeScript
- Vite for build tooling and development server
- React Router v7 for routing
- Tailwind CSS v4 for styling
- shadcn/ui for UI components
- Recharts for data visualization
- Dexie.js for IndexedDB storage and management
- sql.js for parsing and querying PinPal SQLite exports in the browser
- Strict type checking enabled
- Prefer type inference when obvious
- Avoid
any; useunknownfor uncertain types - Use
import type { ... }for type-only imports
- Functional components with hooks
- Use React Context for shared services (PinpalService)
- Prefer composition over prop drilling
- Type component props explicitly
- Use
useStateanduseEffectfor component state and side effects
- Tailwind CSS v4 with custom design tokens
- Global styles in
src/index.css - OKLCH color space for custom colors
- shadcn/ui components for consistent UI
- Path alias
@/*maps tosrc/*
To build the project for production:
npm run buildBuild artifacts are output to dist/ with production optimizations. The build uses Vite for fast bundling and optimization.
- Ensure you've exported the database from PinPal (Settings > Export Database)
- The
.dbfile must be a valid SQLite database
- Try clearing IndexedDB and re-importing your data
- Check browser console for errors
When contributing to this project, please follow the code style guidelines above and ensure tests pass before submitting changes.