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.
9.8 KiB
МојМајстор - 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.
- Initialize Expo project with TypeScript template
- Install dependencies:
expo-router,nativewind,convex,@convex-dev/auth,expo-image-picker,react-native-maps,expo-location - Configure NativeWind (Tailwind) + constants (colors, Macedonian strings)
- Set up Convex project & self-deploy
- Define
schema.tswith all tables - Configure Convex Auth (email/password + phone)
- Build root
_layout.tsxwith auth state observer - Build
(auth)/login.tsx- email/phone login - Build
(auth)/register.tsx- role selection (handyman/customer) + profile creation - 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.
- Seed
categoriestable (Мајстор за сѐ, Водоинсталатер, Електричар, Тескар, Фарбар, Керамичар, Зидар, Градежник, столар, Молер, Електричар, Клима монтажер, etc.) - Build
(tabs)/_layout.tsx- bottom nav (Почетна, Пребарување, Огласи, Чат, Профил) - Build
(tabs)/index.tsx- home screen with hero search + category grid + featured ads - Build
CategoryGridcomponent with icons per category - Build
SearchBarcomponent with text + location filter - Build
(tabs)/explore.tsx- category list → filtered ad list - Build
AdCardcomponent (image, title, rating, location, price range) - Create
ads.tsConvex queries:list,getByCategory,search,getByHandyman - 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.
- 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)
- Build
ad/[id].tsx- ad detail page:- Gallery, description, location map, availability, rating, reviews
- "Започни разговор" (Start chat) button
- "Напиши оценка" (Write review) button
- Create
ads.tsmutations:create,update,delete - Handle image upload via Convex file storage +
expo-image-picker - 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.
- Build
post/create.tsx- customer need form (title, description, category, location, budget, urgency) - Build
post/[id].tsx- post detail with responses - Build
(tabs)/posts.tsx- feed of open customer posts - Create
posts.tsConvex queries & mutations:create,list,getByCustomer,close - 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.
- Build
(tabs)/chat.tsx- chat list with last message preview, unread indicator - 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
- Create
chats.tsConvex queries & mutations:create,listByUser - Create
messages.tsConvex queries & mutations:send,listByChat(paginated) - Add push notification support via Expo Notifications
- 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.
- Build
review/create.tsx- star rating + comment form - Build
ReviewCardcomponent - Add review summary to
AdCardand ad detail page - Create
reviews.tsConvex queries & mutations:create,getByAd,getByHandyman - Update
adstable: computeratingAvgandreviewCounton review creation (Convex trigger) - 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.
- Full Macedonian localization (all strings in
lib/constants.ts) - Error handling & loading states on every screen
- Pull-to-refresh on list screens
- Pagination on ad feeds and chat messages
- Offline-first considerations (Convex optimistic updates)
- Deep linking configuration
- App icon & splash screen (Macedonian-themed)
- EAS Build configuration for iOS + Android
- App store metadata (Македонски)
- Performance audit (list virtualization, image optimization)
- 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-mapswith 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.