111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
import type { MobileIdentity, RoleKey } from '@/types/api';
|
|
import { apiRequest, isAuthApiError } from './api';
|
|
import {
|
|
clearOrgPosition,
|
|
getSelectedPosition,
|
|
hasOrgPosition,
|
|
isSupervisorLearnerMode,
|
|
leaveSupervisorLearnerMode,
|
|
normalizePosition,
|
|
roleKey,
|
|
saveOrgPosition,
|
|
type UserPosition
|
|
} from './auth';
|
|
|
|
export const fetchMobileIdentity = () => apiRequest<MobileIdentity>({
|
|
url: '/api/aihr/mobile/me',
|
|
method: 'GET',
|
|
timeout: 15000,
|
|
clearAuthOnUnauthorized: false
|
|
});
|
|
|
|
export const userTabPages = new Set([
|
|
'/pages/user/today/index',
|
|
'/pages/user/practice/index',
|
|
'/pages/user/sop/index',
|
|
'/pages/user/profile/index'
|
|
]);
|
|
|
|
export const isUserRoute = (url: string) => url.startsWith('/pages/user/');
|
|
|
|
export const isSupervisorRoute = (url: string) => url.startsWith('/pages/supervisor/');
|
|
|
|
export const routeForMobileRole = (role: RoleKey, requested: string) => {
|
|
if (role === 'supervisor' && isUserRoute(requested) && !isSupervisorLearnerMode()) {
|
|
return '/pages/supervisor/index/index';
|
|
}
|
|
if (role === 'user' && isSupervisorRoute(requested)) {
|
|
return '/pages/user/today/index';
|
|
}
|
|
return requested;
|
|
};
|
|
|
|
export const hydrateMobileRole = async (): Promise<RoleKey> => {
|
|
try {
|
|
const identity = await fetchMobileIdentity();
|
|
const role = identity.role === 'supervisor' ? 'supervisor' : 'user';
|
|
uni.setStorageSync(roleKey, role);
|
|
if (role !== 'supervisor') leaveSupervisorLearnerMode();
|
|
return role;
|
|
} catch {
|
|
// ponytail: role lookup failure fails closed; never reuse a stale supervisor cache.
|
|
uni.removeStorageSync(roleKey);
|
|
return 'user';
|
|
}
|
|
};
|
|
|
|
export const positionSetupUrl = (redirect: string) =>
|
|
`/pages/auth/position/index?redirect=${encodeURIComponent(redirect || '/pages/user/today/index')}`;
|
|
|
|
export const goToMobileTarget = (url: string) => {
|
|
if (userTabPages.has(url)) {
|
|
uni.switchTab({ url });
|
|
return;
|
|
}
|
|
uni.redirectTo({ url });
|
|
};
|
|
|
|
export type OrgPositionResult =
|
|
| { status: 'matched'; position: UserPosition }
|
|
| { status: 'unmatched' }
|
|
| { status: 'unauthorized' }
|
|
| { status: 'error' };
|
|
|
|
export const fetchOrgPositionResult = async (): Promise<OrgPositionResult> => {
|
|
try {
|
|
const identity = await fetchMobileIdentity();
|
|
const officialPosition = saveOrgPosition(identity.positionName);
|
|
if (!officialPosition) {
|
|
clearOrgPosition();
|
|
return { status: 'unmatched' };
|
|
}
|
|
return { status: 'matched', position: officialPosition };
|
|
} catch (error) {
|
|
return { status: isAuthApiError(error) ? 'unauthorized' : 'error' };
|
|
}
|
|
};
|
|
|
|
export const hydratePositionFromOrg = async (): Promise<UserPosition | ''> => {
|
|
const result = await fetchOrgPositionResult();
|
|
if (result.status === 'matched') return result.position;
|
|
if (result.status === 'unmatched') return '';
|
|
return hasOrgPosition() ? getSelectedPosition() : '';
|
|
};
|
|
|
|
export const nextUserRouteAfterLogin = async (redirect: string) => {
|
|
const role = await hydrateMobileRole();
|
|
const roleRoute = routeForMobileRole(role, redirect);
|
|
if (roleRoute !== redirect) return roleRoute;
|
|
if (!isUserRoute(redirect)) return redirect;
|
|
const officialPosition = await hydratePositionFromOrg();
|
|
if (normalizePosition(officialPosition) || hasOrgPosition()) return redirect;
|
|
return positionSetupUrl(redirect);
|
|
};
|
|
|
|
export const ensureEmployeePosition = async (redirect: string) => {
|
|
const nextRoute = await nextUserRouteAfterLogin(redirect);
|
|
if (nextRoute === redirect) return true;
|
|
uni.navigateTo({ url: nextRoute });
|
|
return false;
|
|
};
|