Aichaku is a React + Firebase application for tracking personal items and computing cost per use (CPU). The codebase is structured to enforce strict separation between UI, stateful logic, and data access.
- React (Vite)
- Tailwind CSS
- Firebase Auth
- Firestore
- lucide-react
src/
├── App.jsx # App-level orchestration only
├── main.jsx # React bootstrap
├── firebase.js # Firebase init (auth, db)
│
├── hooks/ # Stateful logic (no JSX)
│ ├── useAuth.js
│ ├── useCategories.js
│ └── useItems.js
│
├── services/ # External data access (no React)
│ └── firestoreService.js
│
├── components/ # Pure UI (JSX only)
│ ├── layout/
│ ├── dashboard/
│ └── modals/
│
└── utils/ # Pure functions
├── calculations.js
├── currency.js
└── date.js
-
Components
- Render UI only
- Never access Firestore directly
-
Hooks
- Own state and side effects
- Call
services, expose data + handlers to UI
-
Services
- Plain JavaScript
- Firestore CRUD only
- No React imports
-
Utils
- Pure, deterministic helpers
components → hooks → services → Firestore
Reverse dependencies are not allowed.
- Managed in
useAuth - App reacts to auth state declaratively
- Logout handled via Firebase Auth and propagated automatically
- User-scoped data only
- Paths include
user.uid
Example:
users/{uid}/items/{itemId}
users/{uid}/metadata/categories
Firestore rules are expected to enforce:
request.auth.uid == userId
cpu = uses === 0 ? price : price / usesCentralized in utils/calculations.js.
npm install
npm run dev
Firebase config must be provided in firebase.js.
An unused item’s cost-per-use equals full price No division by zero CPU is always ≥ 0
It only talks to Firestore It never touches React state It never imports React or hooks It returns plain data or throws errors
functions: // categories getOrCreateCategories(uid) updateCategories(uid, newCategories)
// items getItems(uid) addItem(uid, itemData) incrementItemUsage(uid, itemId) deleteItem(uid, itemId)
useAuth.js 只做三件事: 监听 Firebase Auth 状态 暴露 user 暴露 logout()
它 不做: Firestore categories items modal 状态