Product Requirements Document
HRSanad Android App
Mobile HR platform for field workers, supervisors and employees. Designed for construction sites, outdoor teams and on-the-go HR access.
1. Overview
The HRSanad Android app is a lightweight, field-ready mobile companion to the HRSanad web HRMS. It targets two primary user types: supervisors who need to log team attendance quickly on-site, and employees who need self-service access to their payslips, leave, loans, and attendance records.
⚠️ Design Constraint — Field Use
This app will be used on construction sites, outdoors, and in high-ambient-light environments. Every screen must prioritise large tap targets (minimum 48dp), high contrast text, and minimal cognitive load. No complex multi-step flows, no small icons, no tiny text. When in doubt: bigger, simpler, faster.
1.1 Target Users
| User Type | Role in HRSanad | Primary Use Case | Volume |
| Supervisor |
line_manager / hr_officer with supervised access granted |
Log Time In / Time Out for their team on-site with camera photo |
High frequency — multiple times daily |
| Employee |
employee |
View own payslips, leave balance, attendance, loan status |
Low frequency — a few times per month |
| HR Officer |
hr_admin / hr_officer |
Monitor team attendance, approve leave requests |
Medium frequency — daily check |
1.2 Modules in Scope
2. App Icon & Branding
2.1 App Icon Design
🏢
Icon Concept — "The Shield Monogram"
A bold white "H" letterform set inside a rounded-square background using the HRSanad blue gradient. The letter is slightly stylised — the crossbar of the H has a small upward arc suggesting a person (the dot of a silhouette), reinforcing that this is a people platform.
The corners are softly rounded (radius = 22% of icon size) following Android adaptive icon guidelines. A subtle inner shadow gives depth without complexity.
2.2 Icon Specifications
| Attribute | Value |
| Background shape | Rounded square — Android adaptive icon (foreground + background layers) |
| Background gradient | Top-left #003d7a → Bottom-right #2B88D8 (Microsoft Blue family) |
| Foreground | Bold white "H" letterform, weight 900, with subtle person-silhouette arc on crossbar |
| Corner radius | 22% adaptive (system-masked) |
| Safe zone | Foreground artwork within inner 66% of canvas (Android adaptive spec) |
| Sizes required | 48×48 mdpi · 72×72 hdpi · 96×96 xhdpi · 144×144 xxhdpi · 192×192 xxxhdpi · 512×512 Play Store |
| Notification icon | Flat white "H" on transparent background (monochrome) |
| Splash screen | Logo centred on deep blue #003d7a background, fade-in animation 0.4s |
2.3 Colour Palette — Dark Glassmorphism Theme
📐 Based on Final-Design.png — Approved Dark Theme
The app uses a
dark glassmorphism design — deep navy/black background with semi-transparent glass tiles that have glowing blue borders. All text is white or light grey. This is high-contrast and works well in sunlight.
2.3a Glass Tile Specification
| Property | Value |
| Background | rgba(255, 255, 255, 0.07) — semi-transparent white over dark background |
| Border | 1dp solid rgba(0, 120, 212, 0.5) — subtle blue glow border |
| Border radius | 16dp — rounded corners |
| Backdrop blur | 12dp — glass blur effect (RenderEffect on Android 12+ / RenderScript fallback on older) |
| Pressed state | Border brightens to rgba(0, 120, 212, 0.9) + slight scale down (0.97) |
| Icon tint | White #FFFFFF |
| Label | White, 13sp, weight 700, all caps |
2.3b Camera Scan Frame Specification
| Property | Value |
| Frame shape | Rectangle with rounded corners (12dp radius), full viewfinder width minus 32dp margins |
| Corner brackets | Four L-shaped bracket lines at each corner, 24dp long, 3dp thick, blue (#0078D4) |
| Frame colour (scanning) | #0078D4 blue glow |
| Frame colour (detected) | #22c55e green glow — transitions with 300ms animation |
| Scan animation | Horizontal blue line sweeps top-to-bottom inside the frame while FOCUSING |
| Status text | "FOCUSING..." in white bold 14sp centred inside bottom of viewfinder. Changes to "FACE VERIFIED ✅" on detection. |
| Background overlay | Dark semi-transparent vignette around the scan frame to focus attention |
2.4 Typography
| Style | Font | Size | Weight | Use |
| App Name / Hero | Roboto | 28sp | 900 | Employee name on home, section headers |
| Screen Title | Roboto | 20sp | 700 | Top app bar titles |
| Card Title | Roboto | 16sp | 600 | Employee name in team list |
| Body | Roboto | 15sp | 400 | Standard content text |
| Caption | Roboto | 13sp | 400 | Timestamps, secondary info |
| Button Label | Roboto | 16sp | 700 | All buttons — large and bold |
| Badge / Status | Roboto | 12sp | 700 | Status chips — PRESENT, ABSENT, etc. |
3. UX Design Principles
✅ The One Rule
A supervisor standing in the sun at a construction site, wearing work gloves, with 20 people waiting to be checked in — must be able to log Time In for an employee in under 5 taps from the home screen.
3.1 Field-First Design Rules
- Minimum touch target size: 56dp × 56dp for all action buttons (Time In / Time Out)
- Minimum font size: 15sp for any text users need to read. No text below 12sp except legal/technical labels
- High contrast — minimum 4.5:1 ratio (WCAG AA). Use white text on blue backgrounds, dark text on white
- No more than 3 taps from home to core action (Time In for a team member)
- Avoid dropdowns and small checkboxes. Use large toggle buttons, chip groups, full-width selectors
- All status badges must use both colour AND text — never colour alone (sunlight washes out colour perception)
- Camera preview must be full-width. No small thumbnails for photo capture
- Success/failure feedback must be haptic + visual (vibration + green/red flash)
- Every critical button must have a confirmation micro-animation — no silent operations
- Loading states must show a skeleton screen, never a blank white page
3.2 Accessibility
- Support Android system font size scaling up to 1.3× without layout breaking
- All interactive elements have content descriptions for TalkBack
- Arabic (RTL) layout support — mirror layout when device language is set to Arabic
- Dark mode support — system-default dark theme with appropriate colour variants
3.3 Performance
- App cold start: under 2 seconds on mid-range Android device (2GB RAM)
- Team list load: under 1.5 seconds on 4G connection
- Camera opens within 0.5 seconds of tap
- API calls show a loading indicator if response takes > 300ms
4. Navigation Structure
📐 Based on approved mockup screen (Mockup-Screen.png)
The home screen uses a
2×3 tile grid layout — not a bottom navigation bar. Navigation is via a
top app bar with hamburger menu (≡) on the left and notification bell (🔔) on the right. Each tile is a large, labelled icon button. This keeps the interface immediately usable in sunlight with gloves.
4.1 Top App Bar
| Element | Position | Behaviour |
| Hamburger ≡ | Top-left | Opens side drawer: Settings, Change PIN, Language, Sign Out, About |
| Screen Title | Centre | Shows current screen name in bold. Home = "ESS FIELD PORTAL" |
| Notification Bell 🔔 | Top-right | Opens notification list. Badge shows unread count. |
4.2 Home Screen — 2×3 Module Grid
The home screen is a 6-tile grid (2 columns × 3 rows). Every tile is a large square button with an icon (top) and bold label (bottom). Tiles fill the screen — no scrolling needed. The current site is pinned at the bottom as a status bar.
| Position | Icon | Label | Who Sees It | Navigates To |
| Row 1, Col 1 | 👥 People group | TEAM LOGGING | Supervisors only | Team Logging screen |
| Row 1, Col 2 | 📅 Calendar ✓ | MY ATTENDANCE | All users | My Attendance screen |
| Row 2, Col 1 | 💵 Payslip doc | MY PAYSLIP | All users | My Payslip screen |
| Row 2, Col 2 | 🌴 Palm tree | MY LEAVE | All users | My Leave screen |
| Row 3, Col 1 | 💰 Coins stack | MY LOAN | All users | My Loan screen |
| Row 3, Col 2 | 👤 Person | MY PROFILE | All users | My Profile screen |
💡 Supervisor vs Employee
For regular employees (no supervised access), the
TEAM LOGGING tile is hidden and the remaining 5 tiles reflow into a 2×2 + 1 centred layout. The grid always feels balanced.
4.3 Current Site Bar
A persistent footer bar on the Home screen shows the current site context. This is important for construction companies with multiple active sites.
| Element | Details |
| Icon | ✅ Green checkmark (site verified) |
| Label | "CURRENT SITE: Al Reem Construction" — pulls from employee's work_location field or company name |
| Tap action | If supervisor: opens site/shift selector to change active site |
| Colour | Light grey background (#f0f0f0), dark text, green check icon |
4.4 In-Module Back Navigation
Once inside a module, the top app bar shows a back arrow ← on the left and the screen title in the centre. There is no persistent bottom tab bar — navigation is purely via the home grid and the back button.
4.5 Screen Mockups — Approved Final Design (Dark Glassmorphism)
☰
ESS FIELD PORTAL
🔔
✅ CURRENT SITE: Al Reem Construction
Home — Dark Glass Grid
←
MY ATTENDANCE
📅 16/04
🔍 Search Employee ID...
● Face Detected & Verified
📍 24.4539°N, 54.3773°E (Within Range)
→| CONFIRM TIME IN
|← CONFIRM TIME OUT
Team Logging — Scan Screen
5. Authentication
5.1 Login Flow
- Login identifier accepts email address (e.g.
john@example.com) OR mobile number in international format (e.g. 971508150115 — country code prefix, no +, no spaces, no hyphens). App auto-detects which was entered.
- Password login via Supabase Auth (same credentials as web portal)
- JWT token stored in Android EncryptedSharedPreferences (not plain SharedPreferences)
- Auto-refresh token silently — user should never be unexpectedly logged out mid-shift
- After first successful login, a mandatory PIN setup screen is shown before the home screen is accessible. The employee creates a 4–6 digit PIN. All subsequent app opens use PIN entry (or biometric) instead of full credentials.
- Biometric (fingerprint / face unlock) as secondary unlock after initial login — optional but recommended
- Session persists for 30 days (refresh token); force re-login after 30 days
5.2 Login Screen Requirements
| Element | Requirement |
| Logo | HRSanad icon + name, centred, on blue gradient background |
| Identifier field | Large (52dp height), placeholder "Email or Mobile Number (e.g. 971508150115)". Keyboard type defaults to emailAddress; switches to phone keyboard if first characters are all digits. Auto-detects entry type: if input matches ^\d{10,15}$ → treated as mobile number; otherwise → treated as email. Validation error shown inline if format is invalid. |
| Password field | Large (52dp height), toggle show/hide password icon |
| Login button | Full width, 56dp height, bold label "SIGN IN", primary blue |
| Biometric button | Shows on subsequent opens if enrolled — fingerprint/face icon, full width |
| Error state | Red card below button with clear message — "Incorrect email or password" |
| Loading state | Button shows spinner + "Signing in…" — disabled to prevent double-tap |
| Forgot password | Text link below button — opens device browser to web portal reset page |
5.3 First-Login PIN Setup Flow
Triggered once — immediately after the employee's very first successful authentication (email/mobile + password). The PIN setup screen must be completed before the home screen is shown.
| Step | Detail |
| Trigger | First login detected via EncryptedSharedPreferences key pin_setup_complete = false (set to true after PIN saved) |
| Screen title | "Set Your PIN" — full-screen dark overlay with number keypad; back button disabled |
| PIN length | 4 to 6 digits. Dots show as each digit is entered. Confirm step: re-enter PIN to validate they match. |
| Mismatch error | Red message "PINs do not match — please try again". Both fields cleared. |
| Storage | PIN stored encrypted in EncryptedSharedPreferences using Android Keystore-backed AES-256. Never stored in plain text or sent to the server. |
| Subsequent opens | App opens directly to PIN entry screen (or biometric prompt if enrolled). Full credentials screen is not shown again unless PIN reset is triggered. |
| Forgot PIN | "Forgot PIN?" link on PIN entry screen. Tapping it shows: "You will be signed out and must log in again to reset your PIN." On confirm → clear JWT + PIN → navigate to credentials login screen. After re-authentication, PIN setup flow runs again. |
| Biometric fast-track | If device has biometric enrolled, after PIN setup the app offers "Enable fingerprint / face unlock?" — optional shortcut for subsequent opens. PIN remains as fallback. |
5.4 Post-Login Role Detection
After successful authentication (and PIN setup if first login), the app reads user_role and tenant_plan from the JWT and calls GET /tna/supervised/today-log. Based on the response:
- If 200 → user has supervised access → show Team tab in bottom navigation
- If 403 → regular employee → hide Team tab, show 4-tab navigation
6. 📸 Team Logging P1 — Critical
⚠️ This is the most important module
Team Logging is the primary reason this app exists. A supervisor at a construction site must log Time In and Time Out for every team member quickly — with face detection verification and GPS confirmation. Design and test this module first.
📐 Based on Final-Design.png — Approved Screen Layout
The Team Logging / My Attendance screen follows the approved final design: dark background, glass search bar at top, full-width camera viewfinder with a
blue scanning rectangle + corner markers (like a QR scanner frame), "FOCUSING..." text overlay, face detection status line, GPS coordinates line, then two large full-width buttons:
green CONFIRM TIME IN and
red CONFIRM TIME OUT — both always visible.
6.1 Module Overview
The Supervisor taps TEAM LOGGING from the home grid. They type an Employee ID into the search bar, the matched employee's details appear, the camera opens with a face-scan rectangle overlay. When the device detects a face inside the frame and GPS is within the site range, both action buttons become active. The supervisor taps either CONFIRM TIME IN or CONFIRM TIME OUT. The server records: employee ID, action, server-side timestamp, supervisor ID, photo, and GPS coordinates.
6.2 Screen: Team Logging — Full Layout
| Element | Details |
| Top App Bar |
← back arrow | "MY ATTENDANCE" title (centre) | Date widget top-right (e.g. "📅 16/04") |
| Search bar |
Glass-style dark input: "🔍 Search Employee ID..." — Supervisor types employee code or name. Instant match shows employee name below the bar. Large (52dp height), full-width. |
| Camera viewfinder |
Full-width rectangle fills most of the screen. Dark live camera feed. A blue glowing rectangle with corner bracket markers (like a document/QR scanner) sits centred in the frame. Employee faces the camera and supervisor points phone at them. |
| Scan overlay |
"FOCUSING..." text shown in white bold inside the viewfinder while the system is detecting. Changes to "FACE VERIFIED ✅" once detection succeeds. |
| Status line — Face |
Below viewfinder: ● Face Detected & Verified — green dot + white text on dark background. Shows during/after successful face detection. |
| Status line — GPS |
📍 24.4539°N, 54.3773°E (Verified, Within Range) — shows live device GPS coordinates and whether they fall within the registered site geofence. |
| CONFIRM TIME IN button |
Full-width, 56dp height, solid green (#107c10) background, bold white label "→| CONFIRM TIME IN". Arrow icon on left pointing right into a bar. |
| CONFIRM TIME OUT button |
Full-width, 56dp height, dark red (#c0392b) background, bold white label "|← CONFIRM TIME OUT". Bar then arrow icon pointing left. |
| Button states |
Both buttons always visible. If employee not yet checked in: TIME OUT is dimmed (50% opacity). If already checked in: TIME IN is dimmed. Tapping a dimmed button shows a toast warning. |
6.3 Face Detection Requirements
| Requirement | Detail |
| Library | ML Kit Face Detection (Google) — on-device, no internet needed, fast |
| Scan frame | Blue glowing rectangle with corner bracket markers centred in viewfinder. Animates a subtle blue scan-line sweep while FOCUSING. |
| Detection trigger | As soon as a face is detected inside the frame bounds, status changes to "Face Detected & Verified" — no tap required from supervisor |
| Liveness check (v1) | Basic: detect at least one face with confidence > 0.85. Full liveness detection (blink/smile) is v2 scope. |
| No face detected | Frame stays blue, "FOCUSING..." remains. Buttons remain in waiting state. No timeout — supervisor holds until face is in frame. |
| Capture photo | When CONFIRM is tapped, capture current frame as JPEG (800×800px max, quality 80%), convert to base64 for API upload. |
| Face optional | If face detection is taking too long (> 15 sec), a small "Skip Face Check" link appears — allows logging without face verification. This is recorded as face_verified: false in the payload. |
6.4 GPS Verification Requirements
| Requirement | Detail |
| GPS reading | Uses Android FusedLocationProvider for accurate, battery-efficient location |
| Geofence radius | Configurable per company site. Default: 500m radius from site coordinates. Stored in server as part of company/work_location record. |
| Display | Show raw coordinates: "24.4539°N, 54.3773°E" + verification status: "(Verified, Within Range)" in green or "(Outside Range)" in orange |
| Outside range | Show orange warning but do NOT block logging. Recorded as gps_within_range: false in payload. HR can review anomalies. |
| GPS unavailable | If GPS disabled or no fix after 10 sec: show "📍 GPS unavailable" in grey. Still allow logging. Recorded as gps_coordinates: null. |
| API payload additions | Add latitude, longitude, gps_accuracy_meters, face_verified to existing check-in/check-out POST body. |
6.5 Complete Logging Flow (Mockup-Accurate)
→
Type Employee ID in search
→
Employee matched — camera live
→
Point phone at employee face
→
→
Tap CONFIRM TIME IN / OUT
→
✅ Logged — back to search
| Step | Action | Expected Duration |
| 1 | Open app + navigate to Team Logging | 3 sec |
| 2 | Type employee ID (e.g. "EMP00142") — auto-complete after 3 chars | 3 sec |
| 3 | Point camera at employee face — FOCUSING... | 2 sec |
| 4 | Face detected, GPS confirmed — buttons active | 1 sec |
| 5 | Tap CONFIRM TIME IN | 1 sec |
| 6 | Photo uploaded, success flash + haptic | 2 sec |
| 7 | Screen resets to empty search for next employee | 0.5 sec |
✅ Target: Under 12 seconds per employee including face detection and photo upload
6.6 Error & Edge Cases
- API error: show red banner "Failed to log. Tap to retry." — keep photo + GPS in memory
- Offline: queue the record locally (SQLite) with ⏳ badge. Sync when back online.
- Employee not found: "No employee found for EMP00XXX — check the code and try again"
- Already checked in (TIME IN tapped): "Ahmed Hassan checked in at 07:52. Log again?" confirmation dialog
- No check-in recorded (TIME OUT tapped first): "No check-in found for today — log Time In first" warning
- Multiple face detected: "Multiple faces in frame — ensure only one employee is visible" — scan pauses
6.7 Attendance History (Supervisor)
- Accessible via history icon in top-right of Team Logging screen
- Filters: Today / This Week / This Month / Custom date range + optional employee search
- Each row: employee name, date, Time In, Time Out, hours, face verified badge, GPS badge
- Tap row: full-screen photo viewer for that attendance record
- Export via Android share sheet as CSV
6.8 API Endpoints Used
GET/v1/tna/supervised/eligible-employees?q={employeeCode}Search by employee ID/code as user types
GET/v1/tna/supervised/employee/{id}/todayGet today's status for matched employee
POST/v1/tna/supervised/check-inBody: { employee_id, photo_base64, latitude, longitude, gps_accuracy_meters, face_verified }
POST/v1/tna/supervised/check-outBody: same as check-in
GET/v1/tna/supervised/today-logFull team list with today's status (history / summary view)
GET/v1/tna/supervised/history?from=&to=&employee_id=Attendance history with date range
📌 API change needed
The existing check-in/check-out endpoints accept only
{ employee_id, photo_base64 }. Add optional fields
latitude,
longitude,
gps_accuracy_meters,
face_verified to the Zod schema and INSERT in
apps/api/src/routes/tna/supervised.ts. The DB columns will also need to be added via a migration.
7. 📅 My Attendance P2
7.1 Module Overview
The employee can view their own attendance records, see their check-in/check-out times for any day, and perform their own self check-in if their company uses the ESS self-service attendance (not supervised mode).
7.2 Screens
DateToday's date, day name, large
StatusLarge status chip: PRESENT / ABSENT / ON LEAVE
Time In07:58 AM — large text, green
Time Out— (not yet) or 17:02 PM
Hours8h 04m worked today
Self Check-inLarge button: "CHECK IN NOW" (if not yet checked in)
🗓️
Monthly View
Calendar + list
Month pickerLeft/right arrow navigation. Large month+year label.
Calendar gridColour-coded days: green=present, red=absent, orange=late, grey=weekend/holiday, blue=leave
SummaryRow of 4 counters: Present / Absent / Late / Leave days
Day detailTap any day → shows Time In, Time Out, Hours, Status
PhotoIf photo available, thumbnail shown on day detail — tap for full screen
7.3 Self Check-In Requirements
💡 Self check-in is for non-supervised employees only
Employees whose attendance is managed via Team Logging do not see this button. The button is shown only if the employee's grade does NOT have supervised attendance enabled.
- Camera opens (front-facing preferred for self check-in). Same confirmation flow as Team Logging.
- Photo compressed and sent as base64 to
POST /v1/ess/attendance/check-in
- Check-out button: appears once employee has checked in today — "CHECK OUT NOW"
- Cannot check in twice in the same day — show error: "You have already checked in at 07:58 AM today"
7.4 API Endpoints Used
GET/v1/ess/attendance/todayToday's own attendance record
GET/v1/ess/attendance?from=&to=Monthly attendance records (up to 60 days)
POST/v1/ess/attendance/check-inSelf check-in with photo
POST/v1/ess/attendance/check-outSelf check-out with photo
GET/v1/ess/attendance/photo/{filename}View attendance photo (public URL, no auth header needed)
8. 👤 My Profile P3
8.1 Module Overview
A read-only view of the employee's personal and employment details. No editing in v1 — changes go through the web portal and HR. The goal is quick reference: "Who is my manager?", "What is my employee code?", "When does my visa expire?"
8.2 Screen Sections
👤
Profile Header
Always visible at top
PhotoCircular avatar (80dp). Fallback: initials on blue background
NameFull name in English + Arabic below (if available)
DesignationJob title, Department name
Employee CodeEMP00142 — with copy-to-clipboard icon
Status badgeACTIVE / PROBATION / ON LEAVE
📋
Info Sections
Scrollable cards
EmploymentJoin date, Grade, Company, Contract type, Direct manager
ContactWork email (tap to copy), Mobile (tap to call), Personal email
DocumentsEmirates ID expiry, Passport expiry, Visa expiry — colour coded by urgency
EmergencyEmergency contact name + phone (tap to call)
Payslips linkShortcut card → My Payslip screen
8.3 Document Expiry Colour Logic
| Days Until Expiry | Colour | Icon | Label |
| > 90 days | Green | ✅ | Valid — shows expiry date |
| 31–90 days | Orange | ⚠️ | "Expiring in X days" |
| 1–30 days | Red | 🚨 | "Expires in X days — urgent" |
| Expired | Red background | ❌ | "EXPIRED — contact HR" |
8.4 Settings (in Profile tab)
- Language toggle: English / العربية (AR) — switches app locale
- Biometric unlock: Enable / Disable toggle
- Notification preferences: Leave approval, Payslip available, Document expiry
- Change PIN
- Sign Out button (prominent, red — confirmation dialog before logout)
- App version info and Help link
8.5 API Endpoints Used
GET/v1/ess/meOwn profile: name, designation, department, grade, join date, contact, document expiry
9. 🌴 My Leave P2
9.1 Module Overview
Employees view their leave balance, apply for leave, and track request status. The design must make the balance prominently visible — field workers frequently ask "how many days do I have left?"
9.2 Screen: Leave Dashboard
🌴
Leave Balances
Top of screen — always visible
Balance cardsHorizontal scroll. One card per leave type. Large number (40sp), type name below, progress bar showing used vs. entitlement
Annual leaveProminently first. "12 days remaining" in large green text
Sick leaveSecond card. Shows accrued vs. taken
Other typesEmergency, Hajj, Unpaid — scrollable cards
Apply buttonFull-width blue button "APPLY FOR LEAVE" below balance cards
📋
Request History
Scrollable list below
Filter chipsAll · Pending · Approved · Rejected — large scrollable chip row
Request rowLeave type, date range, number of days, status badge, submitted date
Status badgesPENDING (yellow), APPROVED (green), REJECTED (red), CANCELLED (grey)
Row tapExpands to show: approver name, rejection reason (if any), notes
Cancel pendingRed "CANCEL" button shown on pending requests. Confirmation dialog required.
9.3 Screen: Apply for Leave
💡 Keep it simple — 4 fields only
Field workers should be able to apply for leave in under 30 seconds. No complicated forms.
| Field | Component | Validation |
| Leave Type | Full-width selection list — each type shows remaining balance next to name. No dropdown — full-screen picker. | Required |
| From Date | Large date picker button. Shows selected date in readable format: "Mon, 22 Apr 2026" | Required. Cannot be in the past (configurable per company) |
| To Date | Large date picker button. Auto-sets to From Date if not changed. | Required. Must be ≥ From Date |
| Notes | Multi-line text input (optional). Placeholder: "Reason for leave (optional)" | Optional, max 500 chars |
| Days preview | Auto-calculated: "This request = 3 working days. Remaining after: 9 days." Updates live as dates change. | Read-only display |
| Submit button | Full-width, 56dp, "SUBMIT LEAVE REQUEST". Disabled until Type + From + To filled. | — |
9.4 Post-Submit Behaviour
- On success: green success screen with "Request Submitted ✅" and request summary. One button: "DONE" → returns to leave dashboard
- Request immediately appears in history list with PENDING status
- Push notification when request is approved or rejected (requires FCM integration)
- If leave balance insufficient: show warning "You only have X days remaining. Continue?" — allow submission with warning, server will enforce if hard limit set
9.5 API Endpoints Used
GET/v1/ess/leave-balanceAll leave type balances for current year
GET/v1/ess/leave-typesActive leave types with current balance
GET/v1/ess/leave-requests?status=&year=Own leave request history
POST/v1/ess/leave-requestsBody: { leave_type_id, start_date, end_date, notes }
POST/v1/ess/leave-requests/{id}/cancelCancel a pending request
10. 💰 My Payslip P2
10.1 Module Overview
Employees can browse their salary history and view the full breakdown of each payslip. PDF download is a key requirement — many employees need to share payslips for visa applications, bank loans, or renting apartments.
10.2 Screen: Payslip List
💰
Payslip History
Last 24 months
List itemMonth + Year (large), Net Pay in AED (bold, large), WPS status badge
Net payLarge amount: "AED 4,500" — most important figure
WPS badgePAID (green) · PENDING (yellow) · PROCESSING (blue)
Latest badge"LATEST" chip on most recent payslip
Row tapOpens payslip detail screen
Year filterTabs for each year: 2026 · 2025 · 2024
📄
Payslip Detail
Breakdown view
Header cardCompany logo + name, Employee name, Month + Year, Period dates
EarningsList: Basic Salary, Housing, Transport, other allowances — each on its own row with amount
DeductionsList: Loan instalment, Absence, other deductions — red amounts
Net PayLarge bold: "NET PAY: AED 4,500" — highlighted box at bottom
Download PDFLarge "📥 DOWNLOAD PDF" button — triggers server-generated PDF via share sheet
ShareShare icon in app bar — opens Android share sheet with PDF
10.3 PDF Download Flow
- Tap "DOWNLOAD PDF" → call
GET /v1/ess/payslips/{id} to get full data, then render PDF using device-side PDF generation library (e.g. iTextG or AndroidPDF) or a server-generated PDF endpoint
- PDF saved to device Downloads folder. Toast: "Payslip saved to Downloads"
- Share button: opens Android share sheet immediately after generation
- PDF filename format:
HRSanad_Payslip_EMP00142_Mar2026.pdf
- PDF content: Company letterhead, employee details, earnings/deductions table, net pay, WPS reference
10.4 API Endpoints Used
GET/v1/ess/payslipsList of own payslips (last 24 months)
GET/v1/ess/payslips/{id}Full payslip with earnings/deductions line items
11. 🏦 My Loan P3
11.1 Module Overview
Employees can check the status of their active loans, view the repayment schedule, and see how much is outstanding. Loan application in the app is a v2 feature — v1 is read-only.
11.2 Screen: My Loans
No loansEmpty state: "No active loans" with info text about loan policy
Loan cardLoan type name, Total amount (AED), Outstanding balance (large, prominent), Monthly instalment, Status badge
Progress barVisual repayment progress: "AED 3,000 of AED 5,000 repaid (60%)"
StatusACTIVE (blue), PENDING APPROVAL (yellow), SETTLED (green), ON HOLD (orange)
Instalment info"Next deduction: April 2026 payroll · AED 500"
Tap cardOpens instalment schedule
📅
Instalment Schedule
Detail view
HeaderLoan name, total amount, outstanding balance, monthly instalment
Schedule listEach row: Instalment #, Month, Amount, Status (PAID/PENDING/DEDUCTED)
Paid rowsGrey text, ✓ icon, deduction date shown
Current rowHighlighted in blue — "This Month"
Upcoming rowsNormal text, pending icon
11.3 API Endpoints Used
GET/v1/ess/loansOwn active loans: amount, outstanding, instalment, status
📌 API Gap — Instalment Schedule
The current ESS API returns loan summary only. A new endpoint
GET /v1/ess/loans/{id}/schedule will need to be added to
apps/api/src/routes/ess/index.ts to return the full instalment schedule from
finance.loan_installments.
12. 🔌 API Reference Summary
12.1 Base Configuration
| Config | Value |
| Base URL (production) | https://hrsanad.justsimple.online/api/v1 |
| Base URL (development) | http://localhost:3001/v1 |
| Authentication | Bearer token in Authorization header on every request |
| Content-Type | application/json for all requests with a body |
| Error format | { "error": { "code": "...", "message": "..." } } |
| Success format | { "data": [...] } for lists · plain object for single records |
| Date format | ISO 8601: YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps |
| Photo upload | Base64-encoded JPEG string in JSON body field photo_base64. Max 1MB after encoding. |
12.2 All Endpoints Used by the App
| Method | Path | Module | Auth Required |
| GET | /ess/me | My Profile | employee+ |
| GET | /ess/attendance/today | My Attendance | employee+ |
| GET | /ess/attendance?from=&to= | My Attendance | employee+ |
| POST | /ess/attendance/check-in | My Attendance | employee+ |
| POST | /ess/attendance/check-out | My Attendance | employee+ |
| GET | /ess/attendance/photo/{filename} | My Attendance | None (public) |
| GET | /ess/leave-balance | My Leave | employee+ |
| GET | /ess/leave-types | My Leave | employee+ |
| GET | /ess/leave-requests | My Leave | employee+ |
| POST | /ess/leave-requests | My Leave | employee+ |
| POST | /ess/leave-requests/{id}/cancel | My Leave | employee+ |
| GET | /ess/payslips | My Payslip | employee+ |
| GET | /ess/payslips/{id} | My Payslip | employee+ |
| GET | /ess/loans | My Loan | employee+ |
| GET | /ess/loans/{id}/schedule | My Loan | employee+ NEW |
| GET | /tna/supervised/today-log | Team Logging | supervisor |
| GET | /tna/supervised/eligible-employees | Team Logging | supervisor |
| POST | /tna/supervised/check-in | Team Logging | supervisor |
| POST | /tna/supervised/check-out | Team Logging | supervisor |
| GET | /tna/supervised/history | Team Logging | supervisor |
| GET | /subscription/me | Plan check | employee+ |
12.3 New API Endpoints Required (Backend Work)
| Endpoint | Purpose | Priority |
GET /ess/loans/{id}/schedule |
Return full instalment schedule from finance.loan_installments for a specific loan. Fields: instalment_number, month, amount, status, paid_date. |
P2 |
POST /auth/fcm-token |
Register device FCM token for push notifications. Body: { fcm_token, device_id, platform: "android" }. Store in new public.device_tokens table. |
P3 |
DELETE /auth/fcm-token |
Remove device FCM token on logout (prevent notifications to logged-out device). |
P3 |
13. 📶 Offline & Connectivity
⚠️ Construction sites often have poor connectivity
The app must handle no internet gracefully. The Team Logging flow in particular cannot fail silently on bad connections — every attendance record must eventually reach the server.
13.1 Offline Queue for Team Logging
- When a check-in/check-out POST fails due to no network: store the record in a local SQLite queue (employee_id, photo_base64, action, timestamp, retry_count)
- When network is restored: automatically retry all queued records in order. Show a notification: "Syncing 3 pending attendance records…"
- Queued records shown with a ⏳ icon on the employee row — so supervisor knows it's pending sync
- Never delete a queued record until the server confirms with 200/201
- Max 3 retry attempts per record. After 3 failures, mark as FAILED and alert supervisor to re-log
- Offline queue limited to 200 records (prevents excessive local storage use)
13.2 Cached Data
| Data | Cache Duration | Behaviour When Offline |
| Employee list (for Team Logging) | Until app restart or manual refresh | Show cached list with offline banner. Cannot log (no server write). |
| Today's team log | Auto-refresh every 60 sec when online | Show last cached state with "Last updated X min ago" notice |
| My profile | 24 hours | Show cached data with "Offline" badge |
| Leave balance | 1 hour | Show cached balance with staleness warning |
| Payslip list | 24 hours | Show cached list — PDF download requires connectivity |
| Loan data | 24 hours | Show cached data |
13.3 Connectivity Banner
- Show a persistent orange banner at top when offline: "⚠️ No internet connection — some features unavailable"
- Banner disappears automatically when connection restored
- When coming back online: auto-trigger offline queue sync and refresh visible data
14. 🛡️ Android Permissions
| Permission | Why Needed | When Requested | If Denied |
CAMERA |
Team Logging and self check-in photo capture |
First time user taps Time In / Check-In button |
Show explanation dialog: "Camera is required to log attendance. Please enable it in Settings." — cannot log without it |
USE_BIOMETRIC / USE_FINGERPRINT |
Biometric unlock (fingerprint / face ID) |
When user enables biometric in Settings |
Fall back to PIN unlock silently |
INTERNET |
All API calls |
Implicit — no runtime request needed |
N/A (always granted) |
WRITE_EXTERNAL_STORAGE / MediaStore (API 29+) |
Save payslip PDF to Downloads folder |
When user taps "Download PDF" |
Offer share-only option instead of save |
POST_NOTIFICATIONS (API 33+) |
Push notifications for leave approval, payslip available |
On first app open, after onboarding |
App works fully without notifications |
RECEIVE_BOOT_COMPLETED |
Restart offline queue sync service after device reboot |
Install time |
N/A (most devices grant this) |
15. 🔔 Push Notifications
| Event | Notification Text | Priority | Who Receives |
| Leave approved | "Your leave request (22–24 Apr) has been approved ✅" | High | Requesting employee |
| Leave rejected | "Your leave request (22–24 Apr) was not approved. Tap to view reason." | High | Requesting employee |
| New leave request (for approvers) | "Ahmed Hassan has applied for 3 days leave — tap to review" | Normal | Approver / line manager |
| Payslip available | "Your payslip for March 2026 is ready. Net Pay: AED 4,500" | Normal | All employees |
| Document expiry (30 days) | "⚠️ Your Passport expires in 28 days — contact HR to renew" | High | Employee |
| Offline queue synced | "✅ 5 attendance records uploaded successfully" | Low | Supervisor only |
16. 🗓️ Development Roadmap
Phase 1 — Core (Weeks 1–4)
Foundation + Team Logging
App scaffold, authentication (login + biometric), bottom navigation, Team Logging module (today log, check-in, check-out, camera flow, offline queue). This is the only deliverable that blocks field operations.
Phase 2 — ESS Core (Weeks 5–7)
My Attendance + My Leave + My Profile
Self check-in flow, monthly attendance calendar, leave balance cards, apply for leave form, profile screen. Add new backend endpoint for loan instalment schedule.
Phase 3 — Finance (Weeks 8–9)
My Payslip + My Loan
Payslip list and detail, PDF generation and download, loan summary and instalment schedule. Thorough testing of PDF output on multiple Android versions.
Phase 4 — Polish (Weeks 10–11)
Notifications + Arabic RTL + Dark Mode
FCM push notification integration, Arabic language switching with full RTL layout mirroring, dark mode theme. Performance profiling and battery impact review.
Phase 5 — Release (Week 12)
QA, UAT + Play Store Submission
User acceptance testing with 5 real construction site supervisors. Fix issues. Prepare Play Store listing (screenshots, description EN/AR). Submit for review. Internal distribution via Play Store internal track first.
Out of Scope for v1.0
- Loan application form (view-only in v1 — apply on web portal)
- OT request submission from app
- Leave approval workflow for managers in app
- Bulk team check-in (select multiple, one photo) — v2 feature
- GPS geo-fencing for attendance validation
- Facial recognition / liveness detection
- iOS version (Android only for v1)
Recommended Tech Stack (Android)
| Component | Recommended Choice | Why |
| Language | Kotlin | Modern Android standard, concise, null-safe |
| UI Framework | Jetpack Compose | Declarative, fast iteration, Material 3 built-in |
| Navigation | Compose Navigation | Type-safe routes, bottom nav integration |
| Networking | Retrofit 2 + OkHttp | Stable, well-documented, interceptor for auth tokens |
| JSON parsing | Kotlinx Serialization | Kotlin-native, no reflection overhead |
| Image/Camera | CameraX | Lifecycle-aware, works on all API 21+ devices |
| Local DB (offline queue) | Room (SQLite) | Official Android persistence, type-safe queries |
| Secure storage | EncryptedSharedPreferences | JWT token storage, Android Keystore backed |
| PDF generation | Android PdfDocument + custom renderer | No external library needed for basic PDF |
| Async | Kotlin Coroutines + Flow | Structured concurrency, reactive UI updates |
| DI | Hilt | Official Android DI, Compose integration |
| Push Notifications | Firebase Cloud Messaging (FCM) | Standard Android push, reliable delivery |
| Min SDK | API 28 (Android 9.0) | Covers 95%+ of active Android devices in UAE/GCC |
HRSanad Android App — Requirements Specification v1.0
Last updated: 2026-04-18 · Owner: Waheed Rahuman Kamaludeen
This document is the single source of truth for Android app development scope and design.