Add PWA mobile attendance shortcut

This commit is contained in:
A R R R Associates
2026-07-07 18:54:37 +05:30
parent ae06956b3b
commit 2ef58ded85
8 changed files with 238 additions and 2 deletions
+19 -2
View File
@@ -1,4 +1,4 @@
from __future__ import annotations
from __future__ import annotations
from datetime import datetime, timezone, timedelta
@@ -26,6 +26,21 @@ from app.modules.email_integration.services import send_auth_otp_email, send_pas
router = APIRouter()
PENDING_POST_LOGIN_REDIRECT_KEY = "pending_post_login_redirect"
SAFE_POST_LOGIN_REDIRECTS = {
"/mobile/attendance",
"/employee/attendance",
"/employee/dashboard",
}
def _consume_safe_post_login_redirect(request: Request) -> str | None:
value = request.session.pop(PENDING_POST_LOGIN_REDIRECT_KEY, None)
value = (value or "").strip()
if value in SAFE_POST_LOGIN_REDIRECTS:
return value
return None
def _dev_otp_print_enabled() -> bool:
settings = get_settings()
@@ -459,9 +474,11 @@ def login_submit(
if active_financial_year:
request.session["active_financial_year"] = active_financial_year
request.session["must_change_password"] = must_change_password
request.session["post_login_redirect"] = _post_login_redirect(
default_post_login_redirect = _post_login_redirect(
must_change_password, permissions, roles
)
pending_post_login_redirect = None if must_change_password else _consume_safe_post_login_redirect(request)
request.session["post_login_redirect"] = pending_post_login_redirect or default_post_login_redirect
if _otp_required(bs, roles):
code = start_otp(request)
+126
View File
@@ -0,0 +1,126 @@
from __future__ import annotations
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
from app.core.db.common import CommonSessionLocal
from app.core.security.session_auth import SESSION_USER_ID_KEY, get_current_user
from app.modules.core.rbac.permission_guard import require_permission
router = APIRouter(tags=["pwa-ui"])
PWA_REDIRECT_SESSION_KEY = "pending_post_login_redirect"
MOBILE_ATTENDANCE_PATH = "/mobile/attendance"
PWA_STATIC_ROOT = Path("app/ui/static/pwa")
def _safe_mobile_redirect_path(value: str | None) -> str | None:
value = (value or "").strip()
if value in {MOBILE_ATTENDANCE_PATH, "/employee/attendance", "/employee/dashboard"}:
return value
return None
def _remember_mobile_attendance_redirect(request: Request) -> None:
request.session[PWA_REDIRECT_SESSION_KEY] = MOBILE_ATTENDANCE_PATH
@router.get("/manifest.webmanifest", include_in_schema=False)
def pwa_manifest():
return JSONResponse(
{
"name": "ARRR ERP Attendance",
"short_name": "ARRR ERP",
"description": "Mobile attendance access for ARRR ERP staff users.",
"id": "/mobile/attendance",
"start_url": "/mobile/attendance",
"scope": "/",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#1e3a8a",
"background_color": "#f8fafc",
"categories": ["business", "productivity"],
"icons": [
{
"src": "/static/pwa/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any",
},
{
"src": "/static/pwa/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any",
},
{
"src": "/static/pwa/icons/maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable",
},
],
"shortcuts": [
{
"name": "Mark Attendance",
"short_name": "Attendance",
"description": "Open mobile attendance punch screen.",
"url": MOBILE_ATTENDANCE_PATH,
"icons": [
{
"src": "/static/pwa/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
}
],
}
],
},
media_type="application/manifest+json",
headers={"Cache-Control": "no-cache"},
)
@router.get("/sw.js", include_in_schema=False)
def pwa_service_worker():
return FileResponse(
PWA_STATIC_ROOT / "sw.js",
media_type="application/javascript",
headers={
"Cache-Control": "no-cache",
"Service-Worker-Allowed": "/",
},
)
@router.get("/mobile", include_in_schema=False)
def mobile_root(request: Request):
return RedirectResponse(url=MOBILE_ATTENDANCE_PATH, status_code=303)
@router.get(MOBILE_ATTENDANCE_PATH, include_in_schema=False)
def mobile_attendance(request: Request):
if not request.session.get(SESSION_USER_ID_KEY):
_remember_mobile_attendance_redirect(request)
return RedirectResponse(url="/login", status_code=303)
if not request.session.get("otp_verified", False):
_remember_mobile_attendance_redirect(request)
return RedirectResponse(url="/otp", status_code=303)
db = CommonSessionLocal()
try:
current_user = get_current_user(request, db=db)
if not current_user:
request.session.clear()
_remember_mobile_attendance_redirect(request)
return RedirectResponse(url="/login", status_code=303)
try:
require_permission(db, current_user, "employees.attendance.view_self")
except Exception:
return RedirectResponse(url="/employee/dashboard", status_code=303)
return RedirectResponse(url="/employee/attendance", status_code=303)
finally:
db.close()