Add PocketBase login, first-sign-in password changes, account settings, and user activation, reactivation and deletion with self-removal guards. Protect application pages and APIs and keep private pages out of PWA caches. Resolve plant locations from site assignments and improve map controls, profit chart views, navigation and mobile table layouts. Validation: production build and TypeScript passed; 28 unit tests passed. Backend access integration tests and mobile verification are documented.
34 lines
1.9 KiB
TypeScript
34 lines
1.9 KiB
TypeScript
import type { Asset, Site } from "./data";
|
|
import headOffice from "./plant-location-policy.json";
|
|
|
|
export const HEAD_OFFICE = headOffice;
|
|
|
|
export type MapAsset = Pick<Asset, "id" | "code" | "name" | "category" | "type" | "is_active" | "site" | "latitude" | "longitude">;
|
|
export type MapSite = Pick<Site, "id" | "name" | "latitude" | "longitude">;
|
|
type Coordinates = { latitude: number | null; longitude: number | null };
|
|
export type MapLocation = Coordinates & {
|
|
source: "site" | "missing-site" | "head-office" | "manual" | "unlocated";
|
|
siteName: string | null;
|
|
};
|
|
|
|
export function hasCoords(point: Coordinates): boolean {
|
|
const { latitude, longitude } = point;
|
|
return latitude != null && longitude != null && Number.isFinite(latitude) && Number.isFinite(longitude)
|
|
&& Math.abs(latitude) <= 90 && Math.abs(longitude) <= 180 && (latitude !== 0 || longitude !== 0);
|
|
}
|
|
|
|
// Current assignment controls placement, independently of historic invoice hires.
|
|
// Resolve at read time so existing assignments and site-coordinate edits work
|
|
// immediately, without overwriting a machine's separately saved manual pin.
|
|
export function resolveAssetLocation(asset: Coordinates & { site?: string }, sites: MapSite[]): MapLocation {
|
|
if (asset.site) {
|
|
const site = sites.find(s => s.id === asset.site);
|
|
return site && hasCoords(site)
|
|
? { latitude: site.latitude, longitude: site.longitude, source: "site", siteName: site.name }
|
|
: { latitude: null, longitude: null, source: "missing-site", siteName: site?.name ?? "Unknown site" };
|
|
}
|
|
if (!hasCoords(asset)) return { latitude: null, longitude: null, source: "unlocated", siteName: null };
|
|
const atOffice = Math.abs(asset.latitude! - HEAD_OFFICE.latitude) < 0.000001
|
|
&& Math.abs(asset.longitude! - HEAD_OFFICE.longitude) < 0.000001;
|
|
return { latitude: asset.latitude, longitude: asset.longitude, source: atOffice ? "head-office" : "manual", siteName: null };
|
|
}
|