mojmajstor/implementation.md
echo 5102ed23c1 feat: Phase 1 - project setup, auth, and base UI
Set up Expo SDK 56 project with TypeScript and Expo Router (file-based).

Backend:
- Full Convex schema (users, accounts, sessions, ads, categories,
  reviews, chats, messages, posts) deployed to self-hosted instance
- Custom email/password auth with SHA-256 hashing via Convex actions
- Query/mutation scaffolds for all domain tables
- Convex environment variables configured on backend

App structure:
- Root layout with ConvexProvider, ThemeProvider, AuthContext
- (auth) group: login and register screens (Macedonian UI)
  with role selection (handyman/customer)
- (tabs) group: 5 tabs (Home, Explore, Posts, Chat, Profile)
  each with nested Stack layouts
- Placeholder detail screens for ad, post, chat, review flows
- 404 not-found screen in Macedonian

UI primitives (components/ui/):
- Button (4 variants), Input, Card, Loading, Avatar, Rating, Badge
- ThemeProvider with light/dark palette via React context

All user-facing strings in Macedonian. Code identifiers in English.
TypeScript passes with zero errors.
2026-05-29 18:18:58 +02:00

256 lines
9.8 KiB
Markdown

# МојМајстор - Implementation Plan
## Overview
МојМајстор (My Handyman) is a mobile app connecting local handymen with customers in Macedonia. Built with Expo React Native and a self-deployed Convex backend, all UI text in Macedonian.
---
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Frontend | Expo SDK 52+, React Native, TypeScript |
| Navigation | Expo Router (file-based) |
| Backend | Convex (self-hosted) |
| Auth | Convex Auth (email/password + phone) |
| Styling | NativeWind (Tailwind for RN) |
| Chat | Convex real-time queries |
| Maps | react-native-maps |
| Image Upload | Convex file storage + expo-image-picker |
| Deployment | EAS Build + Update |
---
## Data Model (Convex Schema)
### users
- `_id`, `name`, `phone`, `email`, `role` ("handyman" | "customer"), `avatarId?`, `createdAt`
### ads
- `_id`, `handymanId` (ref users), `title`, `description`, `category`, `location`, `lat?`, `lng?`, `priceRange?`, `imageIds[]`, `availability`, `ratingAvg?`, `reviewCount`, `createdAt`, `updatedAt`
### categories
- `_id`, `name` (Macedonian), `slug`, `icon?`, `sortOrder`
### reviews
- `_id`, `adId` (ref ads), `customerId` (ref users), `handymanId` (ref users), `rating` (1-5), `comment`, `createdAt`
### chats
- `_id`, `participantIds[]` (ref users), `lastMessageAt`, `createdBy`
### messages
- `_id`, `chatId` (ref chats), `senderId` (ref users), `content`, `imageId?`, `createdAt`
### posts (customer needs)
- `_id`, `customerId` (ref users), `title`, `description`, `category`, `location`, `budget?`, `status` ("open" | "closed"), `createdAt`, `updatedAt`
---
## Project Structure
```
mojmajstor/
├── app/ # Expo Router pages
│ ├── _layout.tsx # Root layout + auth state
│ ├── (auth)/
│ │ ├── _layout.tsx
│ │ ├── login.tsx
│ │ └── register.tsx
│ ├── (tabs)/
│ │ ├── _layout.tsx # Bottom tab navigator
│ │ ├── index.tsx # Home / search
│ │ ├── explore.tsx # Browse categories & ads
│ │ ├── posts.tsx # Customer needs feed
│ │ ├── chat.tsx # Chat list
│ │ └── profile.tsx # User profile
│ ├── ad/
│ │ ├── [id].tsx # Ad detail
│ │ └── create.tsx # Create ad (handyman)
│ ├── post/
│ │ ├── [id].tsx # Post detail
│ │ └── create.tsx # Create post (customer)
│ ├── chat/
│ │ └── [id].tsx # Chat conversation
│ └── review/
│ └── create.tsx # Leave a review
├── components/
│ ├── ui/ # Reusable UI primitives
│ │ ├── Button.tsx
│ │ ├── Card.tsx
│ │ ├── Input.tsx
│ │ ├── Rating.tsx
│ │ ├── Avatar.tsx
│ │ ├── Badge.tsx
│ │ └── Loading.tsx
│ ├── AdCard.tsx
│ ├── PostCard.tsx
│ ├── ChatListItem.tsx
│ ├── MessageBubble.tsx
│ ├── CategoryGrid.tsx
│ ├── SearchBar.tsx
│ ├── LocationPicker.tsx
│ └── ReviewCard.tsx
├── convex/ # Backend
│ ├── _generated/
│ ├── schema.ts
│ ├── auth.config.ts
│ ├── users.ts
│ ├── ads.ts
│ ├── categories.ts
│ ├── reviews.ts
│ ├── chats.ts
│ ├── messages.ts
│ └── posts.ts
├── lib/
│ ├── constants.ts # Colors, spacing, Macedonian strings
│ ├── hooks.ts # Custom hooks
│ └── utils.ts
├── assets/
│ └── images/
├── app.json
├── package.json
├── tailwind.config.js
├── tsconfig.json
└── eas.json
```
---
## Implementation Phases
### Phase 1: Project Setup & Auth (Week 1)
**Goal:** Bootable app with authentication.
1. Initialize Expo project with TypeScript template
2. Install dependencies: `expo-router`, `nativewind`, `convex`, `@convex-dev/auth`, `expo-image-picker`, `react-native-maps`, `expo-location`
3. Configure NativeWind (Tailwind) + constants (colors, Macedonian strings)
4. Set up Convex project & self-deploy
5. Define `schema.ts` with all tables
6. Configure Convex Auth (email/password + phone)
7. Build root `_layout.tsx` with auth state observer
8. Build `(auth)/login.tsx` - email/phone login
9. Build `(auth)/register.tsx` - role selection (handyman/customer) + profile creation
10. Build UI primitives: Button, Input, Card, Loading
**Deliverable:** User can sign up, log in, see role-specific tab layout.
---
### Phase 2: Home & Categories (Week 2)
**Goal:** Browse and discover handymen.
1. Seed `categories` table (Мајстор за сѐ, Водоинсталатер, Електричар, Тескар, Фарбар, Керамичар, Зидар, Градежник, столар, Молер, Електричар, Клима монтажер, etc.)
2. Build `(tabs)/_layout.tsx` - bottom nav (Почетна, Пребарување, Огласи, Чат, Профил)
3. Build `(tabs)/index.tsx` - home screen with hero search + category grid + featured ads
4. Build `CategoryGrid` component with icons per category
5. Build `SearchBar` component with text + location filter
6. Build `(tabs)/explore.tsx` - category list → filtered ad list
7. Build `AdCard` component (image, title, rating, location, price range)
8. Create `ads.ts` Convex queries: `list`, `getByCategory`, `search`, `getByHandyman`
9. Wire up real data to ad listings
**Deliverable:** Customer can browse categories and see handyman ads.
---
### Phase 3: Ad Creation & Detail (Week 3)
**Goal:** Handymen can post and manage ads.
1. Build `ad/create.tsx` - multi-step form:
- Step 1: Category selection
- Step 2: Title, description, price range
- Step 3: Location (map picker or text)
- Step 4: Availability schedule
- Step 5: Photo upload (up to 5)
2. Build `ad/[id].tsx` - ad detail page:
- Gallery, description, location map, availability, rating, reviews
- "Започни разговор" (Start chat) button
- "Напиши оценка" (Write review) button
3. Create `ads.ts` mutations: `create`, `update`, `delete`
4. Handle image upload via Convex file storage + `expo-image-picker`
5. Build `profile.tsx` - handyman view with their ads list, edit/delete
**Deliverable:** Handymen can create, view, edit, delete their ads. Customers can view full details.
---
### Phase 4: Customer Posts (Week 4)
**Goal:** Customers can post their needs; handymen can respond.
1. Build `post/create.tsx` - customer need form (title, description, category, location, budget, urgency)
2. Build `post/[id].tsx` - post detail with responses
3. Build `(tabs)/posts.tsx` - feed of open customer posts
4. Create `posts.ts` Convex queries & mutations: `create`, `list`, `getByCustomer`, `close`
5. Add "Одговори на оглас" (Respond to post) flow for handymen → starts a chat
**Deliverable:** Customers post needs, handymen browse and respond.
---
### Phase 5: In-App Chat (Week 5)
**Goal:** Real-time messaging between customer and handyman.
1. Build `(tabs)/chat.tsx` - chat list with last message preview, unread indicator
2. Build `chat/[id].tsx` - full conversation screen with:
- Message bubbles (text + optional image)
- Input bar with send button + image picker
- Real-time updates via Convex `onUpdate`
3. Create `chats.ts` Convex queries & mutations: `create`, `listByUser`
4. Create `messages.ts` Convex queries & mutations: `send`, `listByChat` (paginated)
5. Add push notification support via Expo Notifications
6. Add "Започни разговор" button on ad detail → creates or opens existing chat
**Deliverable:** Users can chat in real-time with image support.
---
### Phase 6: Reviews & Ratings (Week 6)
**Goal:** Customer can rate and review handymen.
1. Build `review/create.tsx` - star rating + comment form
2. Build `ReviewCard` component
3. Add review summary to `AdCard` and ad detail page
4. Create `reviews.ts` Convex queries & mutations: `create`, `getByAd`, `getByHandyman`
5. Update `ads` table: compute `ratingAvg` and `reviewCount` on review creation (Convex trigger)
6. Sort ads by rating in search/explore
**Deliverable:** Customers leave reviews; ratings visible on ads.
---
### Phase 7: Polish & Production (Week 7)
**Goal:** Production-ready app.
1. Full Macedonian localization (all strings in `lib/constants.ts`)
2. Error handling & loading states on every screen
3. Pull-to-refresh on list screens
4. Pagination on ad feeds and chat messages
5. Offline-first considerations (Convex optimistic updates)
6. Deep linking configuration
7. App icon & splash screen (Macedonian-themed)
8. EAS Build configuration for iOS + Android
9. App store metadata (Македонски)
10. Performance audit (list virtualization, image optimization)
11. Accessibility pass (labels, contrast, font scaling)
**Deliverable:** Published app on App Store & Google Play.
---
## Key Decisions & Notes
- **Language:** All user-facing text is in Macedonian (Македонски). Code variables and comments in English.
- **Role system:** Determined at registration. Handymen see ad creation; customers see post creation. Both can chat.
- **Maps:** Use `react-native-maps` with OpenStreetMap tiles (no Google API key needed for MVP).
- **Convex self-deploy:** Deploy via Docker on own infra for full data sovereignty.
- **Auth strategy:** Start with email/password, add phone OTP via Twilio in Phase 7.
- **Image optimization:** Compress before upload using `expo-image-manipulator`. Max 5 images per ad.
- **Search:** Convex full-text search on ad title/description. Location filter via lat/lng bounding box.