Skip to content

Commit 4bd6674

Browse files
Kikolatorclaude
andauthored
Add Expo mobile app with multi-tenant support (#97)
## Summary Introduces a new Expo-based React Native mobile application (`apps/mobile`) as a tenant-branded, multi-tenant mobile client. Also establishes a shared package (`packages/shared`) for logic reuse between web and mobile. ## Key Changes - **New mobile app** (`apps/mobile/`) - Expo SDK 55 with Expo Router for file-based routing - TypeScript strict mode with matching tsconfig to web - NativeWind for Tailwind CSS styling in React Native - Supabase client integration with AsyncStorage for persistent auth - TanStack Query v5 for data fetching - React Hook Form + Zod for form validation - Grouped layouts: `(auth)/` and `(app)/` to mirror web structure - EAS Build/Submit configuration for iOS and Android deployment - Environment variables using `EXPO_PUBLIC_` prefix - **New shared package** (`packages/shared/`) - Placeholder for extracting shared validation schemas, constants, and types - Used by both web and mobile applications - Strict TypeScript configuration - **Documentation updates** (`CLAUDE.md`) - Added mobile stack details (framework, styling, auth, deployment) - Updated project structure to include mobile and shared packages - Added mobile-specific commands and conventions - Clarified that mobile uses client-side Supabase with RLS as security boundary ## Implementation Details - Mobile auth persists via AsyncStorage with auto-refresh enabled - All Supabase calls go through the client SDK directly (no server components/actions) - RLS policies enforce security on the database layer - Shared business logic will be centralized in `@cowork/shared` to avoid duplication - Mobile env vars follow Expo conventions (`EXPO_PUBLIC_` prefix) - Babel configured with NativeWind JSX transform for Tailwind support https://claude.ai/code/session_015AhxfccaYtwr8yymr9oes6 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5555d33 commit 4bd6674

28 files changed

+13352
-8376
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ jobs:
6565

6666
- uses: actions/setup-node@v4
6767
with:
68-
node-version: 22
69-
cache: npm
68+
node-version: 24
7069

71-
- run: npm ci
70+
- name: Install dependencies
71+
run: corepack disable && rm package-lock.json && npm install --no-audit --no-fund
7272

7373
- name: Turbo lint
7474
run: npx turbo run lint ${{ env.TURBO_FILTER }}
@@ -89,10 +89,10 @@ jobs:
8989

9090
- uses: actions/setup-node@v4
9191
with:
92-
node-version: 22
93-
cache: npm
92+
node-version: 24
9493

95-
- run: npm ci
94+
- name: Install dependencies
95+
run: corepack disable && rm package-lock.json && npm install --no-audit --no-fund
9696

9797
- name: Turbo check-types
9898
run: npx turbo run check-types ${{ env.TURBO_FILTER }}
@@ -114,10 +114,10 @@ jobs:
114114

115115
- uses: actions/setup-node@v4
116116
with:
117-
node-version: 22
118-
cache: npm
117+
node-version: 24
119118

120-
- run: npm ci
119+
- name: Install dependencies
120+
run: corepack disable && rm package-lock.json && npm install --no-audit --no-fund
121121

122122
- name: Turbo build
123123
run: npx turbo run build ${{ env.TURBO_FILTER }}
@@ -138,10 +138,10 @@ jobs:
138138

139139
- uses: actions/setup-node@v4
140140
with:
141-
node-version: 22
142-
cache: npm
141+
node-version: 24
143142

144-
- run: npm ci
143+
- name: Install dependencies
144+
run: corepack disable && rm package-lock.json && npm install --no-audit --no-fund
145145

146146
- uses: supabase/setup-cli@v1
147147
with:
@@ -181,10 +181,10 @@ jobs:
181181

182182
- uses: actions/setup-node@v4
183183
with:
184-
node-version: 22
185-
cache: npm
184+
node-version: 24
186185

187-
- run: npm ci
186+
- name: Install dependencies
187+
run: corepack disable && rm package-lock.json && npm install --no-audit --no-fund
188188

189189
- uses: supabase/setup-cli@v1
190190
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ packages/db/supabase/snippets/
3636
out/
3737
build
3838
dist
39+
*.tsbuildinfo
3940

4041

4142
# Debug

CLAUDE.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ White-label coworking management platform. Turborepo monorepo.
55
## Structure
66

77
```
8-
apps/web → Next.js 16 (App Router) — tenant-facing product
8+
apps/web → Next.js 16 (App Router) — tenant-facing web product
99
apps/admin → Next.js 16 (App Router) — platform admin dashboard (admin.rogueops.app)
10+
apps/mobile → Expo (React Native) — tenant-branded mobile app (single app, multi-tenant)
1011
packages/db → Supabase: migrations, edge functions, generated types
12+
packages/shared → Shared logic: Zod schemas, validation, constants, types (used by web + mobile)
1113
```
1214

1315
## Stack
@@ -21,6 +23,19 @@ packages/db → Supabase: migrations, edge functions, generated types
2123
- **Data fetching**: Supabase JS (server + client via `@supabase/ssr`)
2224
- **Deployment**: Vercel
2325

26+
## Mobile Stack
27+
28+
- **Framework**: Expo SDK (latest), Expo Router (file-based routing)
29+
- **Language**: TypeScript (strict, same tsconfig as web)
30+
- **Styling**: NativeWind (Tailwind CSS for React Native)
31+
- **Components**: Custom (no shadcn/ui equivalent in RN)
32+
- **Auth**: `@supabase/supabase-js` + AsyncStorage (client-side only, no SSR)
33+
- **Data fetching**: TanStack Query v5 + direct Supabase client (RLS enforced)
34+
- **Validation**: Zod (shared with web via `@cowork/shared`)
35+
- **Forms**: React Hook Form + Zod resolvers (same as web)
36+
- **Build/Deploy**: EAS Build + EAS Submit (cloud CI, no local Xcode/Gradle needed)
37+
- **OTA Updates**: EAS Update for JS bundle hot-patches without store review
38+
2439
## Commands
2540

2641
```bash
@@ -41,6 +56,12 @@ supabase gen types typescript --local > types/database.ts
4156
# Testing (from apps/web/)
4257
turbo test # Unit tests (Vitest)
4358
turbo test:e2e # E2E tests (Playwright)
59+
60+
# Mobile (from apps/mobile/)
61+
npx expo start # Dev server (scan QR or open simulator)
62+
eas build --platform ios # Cloud build for iOS
63+
eas build --platform android # Cloud build for Android
64+
eas submit # Submit to App Store / Play Store
4465
```
4566

4667
## Conventions
@@ -51,6 +72,10 @@ turbo test:e2e # E2E tests (Playwright)
5172
- Database types auto-generated. Never hand-edit `database.ts`.
5273
- Small files (~200 lines max). One component per file.
5374
- Colocate related files: page, components, actions, hooks in the same directory.
75+
- Mobile screens use Expo Router file conventions. Group layouts in `(auth)/` and `(app)/` match web.
76+
- All Supabase calls in mobile go through the client SDK directly — no server components, no server actions. RLS is the security boundary.
77+
- Shared business logic (validation, calculations, constants) lives in `@cowork/shared`, imported by both web and mobile.
78+
- Mobile env vars use `EXPO_PUBLIC_` prefix (equivalent to `NEXT_PUBLIC_` on web).
5479

5580
## DB Schema Spec
5681

apps/admin/tsconfig.tsbuildinfo

Lines changed: 0 additions & 1 deletion
This file was deleted.

apps/mobile/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
EXPO_PUBLIC_SUPABASE_URL=http://localhost:54321
2+
EXPO_PUBLIC_SUPABASE_PUB_KEY=your-anon-key

apps/mobile/.gitignore

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2+
3+
# dependencies
4+
node_modules/
5+
6+
# Expo
7+
.expo/
8+
dist/
9+
web-build/
10+
expo-env.d.ts
11+
12+
# Native
13+
.kotlin/
14+
*.orig.*
15+
*.jks
16+
*.p8
17+
*.p12
18+
*.key
19+
*.mobileprovision
20+
21+
# Metro
22+
.metro-health-check*
23+
24+
# debug
25+
npm-debug.*
26+
yarn-debug.*
27+
yarn-error.*
28+
29+
# macOS
30+
.DS_Store
31+
*.pem
32+
33+
# local env files
34+
.env*.local
35+
36+
# typescript
37+
*.tsbuildinfo
38+
39+
# generated native folders
40+
/ios
41+
/android

apps/mobile/app.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"expo": {
3+
"name": "mobile",
4+
"slug": "mobile",
5+
"version": "1.0.0",
6+
"orientation": "portrait",
7+
"icon": "./assets/icon.png",
8+
"scheme": "cowork",
9+
"userInterfaceStyle": "light",
10+
"splash": {
11+
"image": "./assets/splash-icon.png",
12+
"resizeMode": "contain",
13+
"backgroundColor": "#ffffff"
14+
},
15+
"ios": {
16+
"supportsTablet": true,
17+
"bundleIdentifier": "com.cowork.mobile"
18+
},
19+
"android": {
20+
"adaptiveIcon": {
21+
"backgroundColor": "#E6F4FE",
22+
"foregroundImage": "./assets/android-icon-foreground.png",
23+
"backgroundImage": "./assets/android-icon-background.png",
24+
"monochromeImage": "./assets/android-icon-monochrome.png"
25+
},
26+
"package": "com.cowork.mobile"
27+
},
28+
"web": {
29+
"favicon": "./assets/favicon.png",
30+
"bundler": "metro"
31+
},
32+
"plugins": ["expo-router"]
33+
}
34+
}

apps/mobile/app/(app)/_layout.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Slot } from "expo-router";
2+
3+
export default function AppLayout() {
4+
return <Slot />;
5+
}

apps/mobile/app/(auth)/_layout.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Slot } from "expo-router";
2+
3+
export default function AuthLayout() {
4+
return <Slot />;
5+
}

apps/mobile/app/_layout.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2+
import { Slot } from "expo-router";
3+
import { StatusBar } from "expo-status-bar";
4+
5+
const queryClient = new QueryClient();
6+
7+
export default function RootLayout() {
8+
return (
9+
<QueryClientProvider client={queryClient}>
10+
<Slot />
11+
<StatusBar style="auto" />
12+
</QueryClientProvider>
13+
);
14+
}

0 commit comments

Comments
 (0)