Connecting…
🔒 Admin
🗺️
Connecting to Google Maps…
Upload CSV or Excel files with addresses
Map Layers
Primary Locations 0
Business Partners 0
Service Partners 0
Caseload 0
Uploaded Records 0
My Addresses 0
Add address to my map
Upload files
Color Groups
Group 1
Group 2
Group 3
Group 4
Service Partners
Florida only (Out of Florida hidden)
Locations
Load and geocode files to see results
Drive time Real roads
Network Overview
🔍
No analysis yet
Upload a file then click Run Analysis to see proximity insights — how far each record is from the nearest location in each layer.
Help Center
Find answers, learn features, and get the most from the app.
Job Match
Job Interest (up to 3 — results merged)
Radius mi
Use job titles, skills, or industry terms — e.g. cashier, forklift, data entry, retail.
💼
Find job opportunities
Enter job interest and click Search
Select a job
Click a job card to see full details here.

Default Locations

Primary Locations
Loading…
Business Partners
Loading…
Service Partners
Loading…
AI Assistant BETA
Groq · Llama 3.1
👋 Hi! I'm your map assistant. I can help you filter locations, find clients by area, run proximity analysis, and more.

Try asking:
Clients in Jacksonville Show only Business Partners Active records far from Primary Locations Reset all filters

Confirm

Input

Admin Settings

General Settings
Configure your app's identity, version info, and branding
🏷️ App Name

Shown in the browser tab and logo area. To make it permanent, update APP_NAME in config.php.

ℹ️ App Info
Version Build Released Developer

Workforce mapping & caseload visualization — PHP / MySQL.
Maps: Google Maps (AdvancedMarkerElement, clustered) · Geocoding: Google.
Integrations: CareerOneStop · Google Places · Microsoft 365 / OneDrive.
AI providers: Groq · OpenAI · Gemini · Claude.

Version is manual in config.php · Build is the cache-buster on script tags · Released auto-updates from the deploy date.

🎨 Branding
Logo
PNG, SVG, JPEG — max 200 KB
Favicon
PNG, ICO — 32×32 px, max 32 KB
Default Location Layers

Click any layer to open the full-screen editor. Add, edit, import CSV, sort, or reset each layer. Changes save to server automatically.

Layer Names

Rename any default layer. Changes apply immediately for all users.

Primary
Business
Service
Caseload
Uploaded Records
Field Visibility Admin-only · applies to non-admin users

Uncheck fields to hide them from non-admin users across pin popups, sidebar filters, color groups, and the Data tab. Admins always see everything. Changes apply immediately on save.

Loading field list…
Changelog
Release notes shown to users in Help → What's New
Loading…
AI Assistant — LLM Providers

Keys are stored server-side in config.php. The default provider is tried first — others activate automatically as backup.

Groq
llama-3.1-8b-instant
OpenAI
gpt-4o-mini
Google Gemini
gemini-2.0-flash
Anthropic Claude
claude-sonnet-4-5

Edit config.php to add or change keys: GROQ_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY.

CareerOneStop API ○ Not tested

Free credentials from the US Dept of Labor. Powers "Open Positions" in Job Match. Request free access →

🕐 Session Timeout

Maximum minutes a signed-in user stays authenticated, counted from login (not from last activity). Users get a 401 and must re-authenticate after this elapses. Allowed range: 5–1440 minutes. Default: 120.

minutes
🔒 Caseload Hierarchy Filter

When ON, non-admin users see only caseload rows where their CRM Name matches one of the columns configured in their role's Caseload Hierarchy Scope. Admins and superadmins always see all rows. Users with no CRM name or an unscoped role see nothing.

Before enabling, audit the user table below — any user with a ⚠ No match badge will see an empty caseload once this is on.

User Registry

Combined view of Microsoft 365 users who have signed in and manually-created local users. Data loads from the database on every refresh. Roles assigned here override the default role on next login.

Name / Email Type Role Last Login CRM Name · match status
Loading users…
Add User

Pre-register a Microsoft 365 user with a specific role. On first sign-in the stored role takes effect.

Role Definitions

Each role defines which layers and features users with that role can access. Admin role is always full access and cannot be modified.

First-time setup: click Migrate defaults to seed built-in roles with sensible Data Studio permissions (supervisor/staff/viewer get view+export; partner gets view-only). Safe to skip if already done.
Add New Role
Microsoft 365 / Azure AD

When enabled, all visitors must sign in with a Microsoft account. Role (Admin / User) is determined by Azure AD group membership.

Enable Azure AD Authentication
Require Microsoft sign-in for all visitors; role is set by Azure AD group membership
Redirect URI (copy this into Azure)
Detecting…
Azure → Entra ID → Overview → “Tenant ID” (same for your whole directory)
Your app registration → Overview → “Application (client) ID”
Members of this group get Admin role
Members of this group get Regular User role
If blank, shows an "Access Denied" page instead
Azure Setup Guide

1. Azure Portal → Entra ID → App registrations → New registration
   Name: FlxMap Portal · Account type: This org only
   Redirect URI type: SPA → paste the URI shown above

2. Manifest tab → find groupMembershipClaims → change null to "SecurityGroup"
   ⚠ Critical: Without this, the groups claim is missing and ALL users are denied.

3. API permissions → Add → Microsoft Graph → Delegated:
   openid · profile · email · GroupMember.Read.All

4. Entra ID → Groups → New group
   Create FlxMap Admins and FlxMap Users · type: Security · Add members

5. Copy Tenant ID, Client ID, and both Group Object IDs into the fields above

Locked accounts

Accounts get a 15-minute lockout after 5 failed sign-in attempts (per IP or per username). Use this if a user is locked out and needs immediate access.

Loading…
Content Security Policy reports

A stricter security policy runs silently in the background. Whenever it sees something that would have been blocked under enforcement, the browser logs it here. Use these reports to see what would break before we tighten the real policy. Empty list = ready to enforce.

Loading…
Content Security Policy enforcement

Decides whether the stricter policy actually blocks violations or just logs them. Permissive (default) just logs \u2014 your existing app behavior is unchanged. Strict blocks any script that\u2019s not from an approved source, raising the bar against cross-site scripting attacks. Flipping is instant either way.

Loading…
Two-factor authentication (for your account)

Add a second layer to your sign-in: a 6-digit code emailed to you at sign-in. No app to install or keep in sync. After enrollment, you'll be asked for a code every time you sign in.

Loading…
Two-factor enforcement policy (admin)

Controls whether enrolled users actually need to enter their code at sign-in. Defaults to Off so you can enroll without risk of locking yourself out. Flip to Required for enrolled users when ready.

Loading…
Session inactivity timeout

Automatically sign people out after a period of no activity \u2014 protects an unattended, signed-in workstation. Everyone, including admins, is subject to this. A warning appears 2 minutes before sign-out with a one-click "Stay signed in".

Loading…
Security alert emails

Email a list of people when notable security events happen (e.g. an account gets locked out after repeated failed sign-ins). Off until you add recipients.

Loading…
Admin IP allowlist

Restrict admin sign-in and admin actions to specific networks (e.g. your office). Leave blank to allow from anywhere. Use with care \u2014 the app refuses to save a list that doesn\u2019t include your current IP, but a changing/dynamic IP could still lock you out (recovery: clear the admin_ip_allowlist setting in the database).

Loading…
Microsoft 365 Connection

FlxMap reads CSV files from your OneDrive and SharePoint using your Microsoft 365 sign-in. Files you can access in OneDrive, FlxMap can access for sync.

Status: Checking…

How it works

When you click Connect, Microsoft asks you to authorize FlxMap to read your files. After that, you can browse your OneDrive and SharePoint sites from inside FlxMap, pick CSV files to sync, and refresh them on demand.

Each user manages their own sync sources independently — sources you configure are saved in your browser, not shared with other users.

Note: Sync is manual — click "Refresh now" on a source whenever you want to pull updated data.

Cron Sync — App-only Credentials

Different from the user connection above. The cron runs unattended at 3 AM with no logged-in user — it needs its own Azure app-only credentials (client credentials grant). Without this, the scheduled sync can't authenticate to Microsoft Graph.

In Azure: register an app (or reuse the existing one) with Application permissions for Files.Read.All and Sites.Read.All, then click Grant admin consent. Copy the three values below.

Stored encrypted. Leaving this blank when editing keeps the existing secret unchanged.

Geocode Cache

When the cron geocodes addresses, results are cached so repeated syncs don't re-call Google. Failed lookups (junk addresses, ZERO_RESULTS) are also cached as negative entries — they're not retried unless the cache is cleared.

If you fix address data in the source file and want the cron to re-try previously-failed rows, clear the negative cache. Successful entries stay.

When to use these:
  • Clear failed geocodes — You corrected addresses in the source file and want the next cron run to retry them (most common).
  • Clear entire cache — You changed the geocoder, suspect bad cached coordinates, or are debugging address quality. Re-geocodes every address on the next run.
Database Maintenance

One-time cleanup actions. Use after a known issue or to free space. Each action shows what it will delete before doing anything.

When to use these:
  • Clean stale service rows — Only needed once after upgrading from a pre-105w5 build. Safe to ignore on clean installs.
  • List service backup tables — Diagnostic. Shows the rolling backups the cron leaves behind during a swap. The janitor cron auto-cleans them after 24 hours.
  • Clean legacy backups — Drops the older _old_<datetime> tables. Run once when you want to reclaim disk space; the current rolling backup is preserved.
All three are rarely needed in normal operation — keep them here for the occasional cleanup or troubleshooting.
Data Schemas

Manage the columns of the active layer's table. Adding a column lets you map and store new fields from your CRM file. Renaming preserves data. Modifying type / length is metadata-only when the new type can hold the old values. Dropping a column is irreversible.

Loading…
Service events
Outages, maintenance windows, and degraded service. Events tied to Primary locations for now — Business and Service location feeds come later.
Loading…
Inbox
Emails from your configured outage/maintenance mailbox. Auto-matched to a location when possible — review the pending queue and link each one to an event.
Loading…
Provider templates
Saved parsing rules per sender. When an email matches a template's sender pattern, regex patterns extract the affected address, type, and severity. If “auto-create” is on AND the address maps to a known location, the event is created with no manual step.
Loading…
Subscriptions
Who gets notified when a Service Event is created. Each row is one user-to-location subscription with a minimum severity threshold and the channels they want (in-app, email).
Loading…
Reports
A snapshot of your outages and maintenance over a period: how many, how long, which offices and providers, plus email-ingestion stats. Pick a time range below — it updates instantly.
Custom date range »
to
Loading…
📧 Scheduled digest email
Email a recurring summary (active / new / resolved events) to a list of people. Set the cron up under Admin → System → Cron Monitor to run it automatically.
Loading…
MySQL Connection

Credentials are stored in DATA_DIR/db_config.json — never in PHP source. Create a MySQL database in cPanel → MySQL Databases first.

📧 Daily Sync Digest
Email a summary of source sync activity (rows loaded, stale sources, errors) to one or more addresses. Use Send Now to test the content and delivery.
Sends automatically the moment the daily sync cron finishes (once per day max). No clock-time setting needed.
Setup Guide

1. cPanel → MySQL Databases
   Create database (e.g. employu_emap)
   Create user → Assign ALL PRIVILEGES

2. Upload to server (same folder as index.html)
   emap_setup.sql · emap_seed.php

3. Enter credentials above → Save & Connect

4. Admin Settings → Database → Run Setup SQL
   Creates all tables automatically

5. Visit /emap_seed.php?key=eMapSeed2026!
   Imports existing JSON layer files into MySQL
   Then delete emap_seed.php from server

Backup & export

Download a backup of your configuration as a single JSON file you can keep offsite. Includes settings, roles & permissions, Service Status setup (templates, subscriptions, watch-folders), custom areas, dashboards, and layer schemas. Configuration + map data also includes the map layer rows (offices, businesses) and service events.

Secret values (passwords, tokens, client secrets) are redacted from the file and must be re-entered after a restore. Even so, treat the file as sensitive and store it securely.

🔐 Security
Change Your Password

For local users, this updates your account password. For the legacy admin login (no user record), this updates the admin-only password. Microsoft 365 users should change their password via their Microsoft account portal. You will be signed out after changing.

Email / Mail Transport

Used to send invite emails to new local users and test notifications. Choose how the app delivers mail.

⚙ Advanced
🗺 Map Clustering

Controls how map pins group into clusters. Applies to every layer automatically. Changes take effect immediately and for all users.

Cron Monitor
Health and history of background scheduled tasks
Loading…