5 Commits

Author SHA1 Message Date
bc22f0c6f6 feat: EAS build config for Android APK + BUILD.md (FIBEROPS-210)
- Add versionCode: 1 to app.json android section
- Add preview-local build profile to eas.json (local Gradle build)
- Add build:android:apk and build:android:local npm scripts
- Add BUILD.md documenting EAS cloud and local build instructions
2026-04-01 01:08:04 +00:00
c649b2f61b Merge pull request 'feat: app icon/splash asset guidelines + app.json fixes (FIBEROPS-209)' (#2) from feat/FIBEROPS-209-app-assets into main 2026-04-01 01:06:09 +00:00
313a2f8a03 feat: app icon/splash asset guidelines + app.json config (FIBEROPS-209)
- Fix slug typo: "firberops" → "fiberops"
- Update splash backgroundColor to brand #F8FAFC (was Expo default #2563EB)
- Update android adaptiveIcon backgroundColor to brand cyan #0891B2
- Add assets/ASSETS.md with full brand guidelines (colors, fonts, dimensions)
- Add scripts/generate-assets.sh to regenerate PNGs from SVG sources via inkscape/rsvg-convert
2026-04-01 01:05:28 +00:00
c0d36fd895 Merge pull request 'fix: Mobile QA — manual tasks + remittance data unwrap bug (FIBEROPS-207-208)' (#1) from fix/FIBEROPS-207-208-mobile-qa into main 2026-04-01 01:02:53 +00:00
7d82ddc316 fix: Mobile QA — manual tasks + remittance review (FIBEROPS-207-208)
- manual-tasks/[id].tsx: use r.data?.data ?? r.data for detail query to
  handle API response wrapper envelope, matching list screen pattern
- remittances/[id].tsx: same fix — detail query now unwraps correctly
- services/api.ts: use STORAGE_KEYS.TOKEN constant instead of hardcoded
  string literals in SecureStore calls
2026-04-01 01:01:59 +00:00
9 changed files with 307 additions and 8 deletions

59
BUILD.md Normal file
View File

@@ -0,0 +1,59 @@
# Building FiberOps Android APK
## Requirements
- [Expo account](https://expo.dev) — register at expo.dev
- EAS CLI: `npm install -g eas-cli`
- Authenticate: `eas login`
## Build (EAS Cloud — recommended)
Builds the APK on Expo's cloud infrastructure. No local Android SDK needed.
```bash
eas build --profile preview --platform android
```
Or via npm script:
```bash
npm run build:android:apk
```
The build will be queued on EAS. Once complete, download the `.apk` from the Expo dashboard or the URL printed in the CLI output.
## Build (local — requires Android SDK)
Requires Android SDK installed and `ANDROID_HOME` set.
```bash
npm run build:android:local
# or directly:
eas build --local --profile preview --platform android
```
Alternatively, use the Expo CLI directly:
```bash
npx expo run:android --variant release
```
## Distribution
The `preview` profile is configured for **internal distribution** (`buildType: apk`). Share the `.apk` file directly with testers — no Play Store submission needed.
## Current status
- QA passed: MOB.1 (manual tasks), MOB.2 (remittance review)
- EAS project ID: `15d21320-57f5-4fb6-a116-82d222d914e2`
- Package: `com.fiberops.mobile`
- Version: `1.0.0` (versionCode: 1)
## EAS build profiles
| Profile | Distribution | Output | Use case |
|---------|-------------|--------|----------|
| `preview` | internal | APK (EAS cloud) | QA / internal testing |
| `preview-local` | internal | APK (local Gradle) | Offline / CI builds |
| `development` | internal | Dev client | Development |
| `production` | store | AAB | Play Store release |

View File

@@ -1,7 +1,7 @@
{
"expo": {
"name": "FiberOps",
"slug": "firberops",
"slug": "fiberops",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
@@ -10,7 +10,7 @@
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#2563EB"
"backgroundColor": "#F8FAFC"
},
"ios": {
"supportsTablet": false,
@@ -19,9 +19,10 @@
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#2563EB"
"backgroundColor": "#0891B2"
},
"package": "com.fiberops.mobile",
"versionCode": 1,
"permissions": [
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO",

View File

@@ -61,7 +61,7 @@ export default function ManualTaskDetailScreen() {
const { data: task, isLoading, refetch } = useQuery({
queryKey: ['manual-task', id],
queryFn: () => api.get(`/api/v1/manual-tasks/${id}`).then(r => r.data),
queryFn: () => api.get(`/api/v1/manual-tasks/${id}`).then(r => r.data?.data ?? r.data),
staleTime: 0,
});

View File

@@ -25,7 +25,7 @@ export default function RemittanceDetailScreen() {
const { data, isLoading } = useQuery({
queryKey: ['remittance', id],
queryFn: () => api.get(`/api/v1/remittances/${id}`).then(r => r.data),
queryFn: () => api.get(`/api/v1/remittances/${id}`).then(r => r.data?.data ?? r.data),
});
if (isLoading) {

87
assets/ASSETS.md Normal file
View File

@@ -0,0 +1,87 @@
# FiberOps Mobile — Asset Guidelines
> The files in this directory are currently **placeholder** Expo defaults.
> Replace them with brand-correct assets using the specs below.
> Run `scripts/generate-assets.sh` to regenerate from SVG sources once installed.
---
## Brand Colors
| Token | Hex | Usage |
|-------|-----|-------|
| Primary cyan | `#0891B2` | App icon background, adaptive icon background, tint color |
| Splash background | `#F8FAFC` | Splash screen background (near-white) |
| Text on cyan | `#FFFFFF` | "FO" / "FiberOps" text on colored backgrounds |
| Text on light | `#0891B2` | "FiberOps" wordmark on splash |
## Brand Font
**Fira Sans** (Bold / SemiBold) — preferred for the "FO" monogram and "FiberOps" wordmark.
Fallback: Inter, SF Pro, or any geometric sans-serif.
---
## Asset Inventory
### `icon.png` — App Icon
- **Dimensions:** 1024 × 1024 px
- **Format:** PNG, no transparency
- **Design:** Solid `#0891B2` background with rounded corners (corner radius ~22.5 % of width ≈ 230 px), centered white "FO" wordmark in Fira Sans Bold at ~460 px.
- **Used by:** iOS home screen, App Store listing, Expo Go thumbnail.
### `adaptive-icon.png` — Android Adaptive Icon Foreground
- **Dimensions:** 1024 × 1024 px
- **Format:** PNG with **transparent** background
- **Design:** Centered white "FO" wordmark (same as icon, no background fill). Android clips to a circle/squircle and composites over `backgroundColor` (`#0891B2`).
- **Safe zone:** Keep visual content within the inner 66 % (≈ 672 px) to avoid clipping on all Android shapes.
### `android-icon-background.png`
- **Dimensions:** 1024 × 1024 px
- **Format:** PNG
- **Design:** Solid `#0891B2` fill — no other content.
### `android-icon-foreground.png`
- **Dimensions:** 1024 × 1024 px
- **Format:** PNG with transparent background
- **Design:** Same as `adaptive-icon.png`.
### `android-icon-monochrome.png`
- **Dimensions:** 1024 × 1024 px
- **Format:** PNG, single-channel (white on transparent or black on transparent)
- **Design:** Outline / solid "FO" mark only — no color fill. Used by Android 13+ themed icons.
### `splash-icon.png` — Splash Screen Logo
- **Dimensions:** 1284 × 2778 px (iPhone 14 Pro Max native) or at minimum 1024 × 1024 px centered mark
- **Format:** PNG with transparent background (Expo composites over `backgroundColor`)
- **Design:** Centered "FO" logomark (200300 px) above "FiberOps" wordmark in `#0891B2`. Background is handled by `app.json``splash.backgroundColor` (`#F8FAFC`).
- **Resize mode:** `contain` (set in app.json) — keep content in the center 40 % of canvas.
### `favicon.png` — Web Favicon
- **Dimensions:** 48 × 48 px (also works at 32 × 32 and 16 × 16)
- **Format:** PNG
- **Design:** `#0891B2` square background with white "F" or "FO" mark.
---
## Generating Assets
See `../scripts/generate-assets.sh` for the full generation pipeline using Inkscape or rsvg-convert.
Quick summary:
1. Edit `scripts/fiberops-icon.svg` (source of truth)
2. Run `scripts/generate-assets.sh`
3. All PNG files in `assets/` are overwritten with fresh exports
---
## app.json Reference
| Key | Value |
|-----|-------|
| `expo.icon` | `./assets/icon.png` |
| `expo.splash.image` | `./assets/splash-icon.png` |
| `expo.splash.backgroundColor` | `#F8FAFC` |
| `expo.android.adaptiveIcon.foregroundImage` | `./assets/adaptive-icon.png` |
| `expo.android.adaptiveIcon.backgroundColor` | `#0891B2` |
| `expo.web.favicon` | `./assets/favicon.png` |

View File

@@ -13,6 +13,14 @@
"buildType": "apk"
}
},
"preview-local": {
"distribution": "internal",
"android": {
"buildType": "apk",
"gradleCommand": ":app:assembleRelease"
},
"env": {}
},
"production": {
"autoIncrement": true
}

View File

@@ -6,7 +6,9 @@
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
"web": "expo start --web",
"build:android:apk": "eas build --profile preview --platform android --non-interactive",
"build:android:local": "eas build --local --profile preview --platform android"
},
"dependencies": {
"@react-native-async-storage/async-storage": "2.2.0",

142
scripts/generate-assets.sh Executable file
View File

@@ -0,0 +1,142 @@
#!/usr/bin/env bash
# generate-assets.sh — Generate FiberOps PNG assets from SVG sources
#
# Requirements (install one):
# brew install inkscape (macOS / Linux)
# brew install librsvg (lighter: rsvg-convert)
# apt-get install librsvg2-bin (Debian/Ubuntu)
#
# Usage:
# ./scripts/generate-assets.sh
#
# Output: assets/*.png (overwrites existing files)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
ASSETS="$REPO_ROOT/assets"
SVGS="$SCRIPT_DIR/svg"
# ---------------------------------------------------------------------------
# Check for a suitable SVG renderer
# ---------------------------------------------------------------------------
if command -v inkscape &>/dev/null; then
RENDERER="inkscape"
elif command -v rsvg-convert &>/dev/null; then
RENDERER="rsvg"
else
echo "ERROR: Install inkscape or librsvg (rsvg-convert) first." >&2
echo " macOS: brew install inkscape OR brew install librsvg" >&2
echo " Linux: apt-get install inkscape OR apt-get install librsvg2-bin" >&2
exit 1
fi
export_png() {
local svg="$1" out="$2" w="$3" h="$4"
echo "$out (${w}×${h})"
if [[ "$RENDERER" == "inkscape" ]]; then
inkscape --export-type=png \
--export-filename="$out" \
--export-width="$w" \
--export-height="$h" \
"$svg" 2>/dev/null
else
rsvg-convert -w "$w" -h "$h" -o "$out" "$svg"
fi
}
mkdir -p "$SVGS"
# ---------------------------------------------------------------------------
# Inline SVG sources (written to scripts/svg/ so they can be edited)
# ---------------------------------------------------------------------------
# 1. icon-bg.svg — opaque cyan square with "FO" (used for icon.png)
cat > "$SVGS/icon-bg.svg" <<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<rect width="1024" height="1024" rx="230" ry="230" fill="#0891B2"/>
<text x="512" y="590"
font-family="'Fira Sans', 'Inter', sans-serif"
font-weight="700"
font-size="460"
text-anchor="middle"
fill="#FFFFFF">FO</text>
</svg>
SVG
# 2. icon-fg.svg — transparent background, white "FO" (adaptive icon foreground)
cat > "$SVGS/icon-fg.svg" <<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<text x="512" y="590"
font-family="'Fira Sans', 'Inter', sans-serif"
font-weight="700"
font-size="460"
text-anchor="middle"
fill="#FFFFFF">FO</text>
</svg>
SVG
# 3. splash.svg — transparent, centered logo for splash (composited over #F8FAFC)
cat > "$SVGS/splash.svg" <<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<!-- Circle backdrop -->
<circle cx="512" cy="430" r="220" fill="#0891B2"/>
<!-- "FO" inside circle -->
<text x="512" y="505"
font-family="'Fira Sans', 'Inter', sans-serif"
font-weight="700"
font-size="200"
text-anchor="middle"
fill="#FFFFFF">FO</text>
<!-- "FiberOps" wordmark below -->
<text x="512" y="730"
font-family="'Fira Sans', 'Inter', sans-serif"
font-weight="600"
font-size="110"
text-anchor="middle"
fill="#0891B2">FiberOps</text>
</svg>
SVG
# 4. favicon.svg — tiny "F" on cyan
cat > "$SVGS/favicon.svg" <<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
<rect width="48" height="48" rx="8" fill="#0891B2"/>
<text x="24" y="36"
font-family="'Fira Sans', 'Inter', sans-serif"
font-weight="700"
font-size="30"
text-anchor="middle"
fill="#FFFFFF">F</text>
</svg>
SVG
# 5. monochrome.svg — white "FO" on transparent (Android 13 themed icon)
cat > "$SVGS/monochrome.svg" <<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<text x="512" y="590"
font-family="'Fira Sans', 'Inter', sans-serif"
font-weight="700"
font-size="460"
text-anchor="middle"
fill="#FFFFFF">FO</text>
</svg>
SVG
# ---------------------------------------------------------------------------
# Export PNGs
# ---------------------------------------------------------------------------
echo "Exporting PNGs..."
export_png "$SVGS/icon-bg.svg" "$ASSETS/icon.png" 1024 1024
export_png "$SVGS/icon-fg.svg" "$ASSETS/adaptive-icon.png" 1024 1024
export_png "$SVGS/icon-fg.svg" "$ASSETS/android-icon-foreground.png" 1024 1024
export_png "$SVGS/icon-bg.svg" "$ASSETS/android-icon-background.png" 1024 1024
export_png "$SVGS/monochrome.svg" "$ASSETS/android-icon-monochrome.png" 1024 1024
export_png "$SVGS/splash.svg" "$ASSETS/splash-icon.png" 1024 1024
export_png "$SVGS/favicon.svg" "$ASSETS/favicon.png" 48 48
echo ""
echo "Done. Files written to assets/"
echo "Verify with: ls -lh assets/*.png"

View File

@@ -11,7 +11,7 @@ export const api = axios.create({
});
api.interceptors.request.use(async (config) => {
const token = await SecureStore.getItemAsync('fiberops_token');
const token = await SecureStore.getItemAsync(STORAGE_KEYS.TOKEN);
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
@@ -22,7 +22,7 @@ api.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
await SecureStore.deleteItemAsync('fiberops_token');
await SecureStore.deleteItemAsync(STORAGE_KEYS.TOKEN);
await SecureStore.deleteItemAsync('fiberops_tenant');
}
return Promise.reject(error);