The Field Intel Console is not a knowledge base in the traditional sense. It does not contain how-to articles, vendor documentation, or step-by-step guides for known procedures. What it contains are field resolutions — documented instances where an engineer encountered a unique or non-obvious problem, identified the root cause, and applied a specific fix that worked.
The design premise is recognition-first. When an engineer picks up a P1, they are not browsing — they are scanning for a match. The card layout surfaces the symptom triggers prominently so an engineer can confirm or rule out a match in under five seconds. Only if a match is found does the engineer expand the detail to read the fix.
Who adds entries: A small team of engineers logs resolutions after closing incidents where the fix was non-obvious, undocumented, or likely to recur. Standard troubleshooting steps that are already in vendor documentation do not belong here. If a vendor KB article already covers it, it does not need a Field Intel entry.
Current state: All UI is fully built. The list view, board view, detail modal, add entry panel, filters, and completeness tracking all render correctly in demo mode with 20 pre-loaded seed entries. All SharePoint wiring is written, commented, and ready to activate behind DEMO_MODE = true.
The console follows a simple browser → SharePoint REST pattern with no intermediate proxy required. SharePoint's built-in authentication handles identity. The browser calls the SP list API directly using the active SharePoint session cookie.
nav.js which renders the top bar. The file's <body> tag carries data-nav-active and data-root attributes that nav.js reads to set the active nav state and resolve relative paths. padding-top: var(--th) offsets the fixed nav bar.
Four stat cells run in a single full-width strip below the search and filter bar. In demo mode they reflect live counts derived from the seed data on each render. When the SP list is connected, they recalculate on every loadDashboard() call.
| Cell | Value | Calculation |
|---|---|---|
| Total Entries | All entries in the list | entries.length |
| P1 Resolutions | Entries where sev = "P1" | entries.filter(e => e.sev === 'P1').length |
| Added This Month | Entries with a date in the current calendar month | Compares e.date year + month against new Date() |
| Unique Tags | Distinct tag values across all entries | new Set(entries.flatMap(e => e.tags)).size |
The default view. Entries render as stacked cards in reverse-chronological order. The list is re-rendered on every filter or search change. Cards are pure launchers — clicking anywhere on a card opens the detail modal. There is no inline expand.
Each list card shows: entry ID, severity badge, title, "IF YOU'RE SEEING" trigger bullets, Seen On location, product badges, tag chips, and the completeness track. The OPEN › indicator in the top-right corner signals the click behavior.
| Element | Source Field | Notes |
|---|---|---|
| Left accent stripe | e.sev | Red (P1), Yellow (P2), Cyan (P3) |
| Entry ID | e.id | Format: FI-0001, FI-0002… auto-assigned on save |
| Severity badge | e.sev | P1 / P2 / P3 |
| Title | e.title | Short descriptive title |
| Trigger bullets | e.triggers[] | One bullet per array item — symptom statements |
| Seen On | e.seenOn | Client name / device / environment context |
| Product badges | e.products[] | Cyan outlined badges — vendor/product tags |
| Tag chips | e.tags[] | Clickable — clicking a tag activates that tag filter |
| Completeness track | Computed | 4-node rail — see Completeness Track section |
| Date / Author | e.date / e.author | Top-right metadata |
An alternate view toggled via the ≡ LIST / ⊞ BOARD buttons in the page header. The board organises entries into three columns by severity: P1 Critical (red), P2 Moderate (yellow), P3 Low (cyan). Each column header shows a live count of entries matching the current filters.
Board cards are compact versions of list cards — they show ID, title, Seen On, product badges, and the completeness track, but omit the trigger bullets to save column space. Clicking any board card opens the same detail modal as the list view. All filters and search apply identically to both views — switching views does not reset filter state.
| Trigger | Action |
|---|---|
| Click ≡ LIST | Shows #entriesContainer, hides #boardContainer, calls renderEntries() |
| Click ⊞ BOARD | Shows #boardContainer, hides #entriesContainer, calls renderBoard() |
| Search / filter change | Calls currentView==='board' ? renderBoard() : renderEntries() — both views respect active filters |
| Tag chip click (on card) | Activates tag filter and re-renders the current view — does not switch view |
Clicking any card in either list or board view opens a framed detail window over the page. The modal contains the full entry — all four sections with a clickable section navigation rail at the top. It does not navigate away from the current page.
| Element | Detail |
|---|---|
| Window chrome | Entry ID, full title, severity badge, date, author. Top accent stripe matches severity color. |
| Section nav | 4-node clip-path rail: 01 Triggers → 02 Root Cause → 03 Fix Applied → 04 Result. Clicking a node smooth-scrolls to that section. |
| Triggers section | Symptom bullets, Seen On, product badges |
| Root Cause section | Plain text explanation of what was actually happening |
| Fix Applied section | Code block with COPY button — copies the exact fix to clipboard. Labeled "PowerShell / NinjaRMM Console". |
| Result section | Two-column row: Result (green border) + Security Note (orange border) if present |
Every card — in both list and board view — carries a 4-node completeness rail. This is a read-only indicator that shows which fields are populated on each entry without opening it. Engineers can scan the list and immediately identify incomplete entries that need follow-up documentation.
| Node | Complete (cyan) | Partial (yellow) | Empty (dim) |
|---|---|---|---|
| 01 Triggers | e.triggers.length > 0 | — | No trigger bullets logged |
| 02 Root Cause | !!e.rootCause | — | Root cause field empty |
| 03 Fix | !!e.fix | — | Fix field empty |
| 04 Verified | Both result and security note present | Result present, security note missing | No result logged |
Opened by the + LOG RESOLUTION button in the page header. The panel slides in from the right side over the current view. The form submits to saveEntry() which routes to the SP list in live mode or pushes directly into the in-memory entries[] array in demo mode.
| Field | Required | Notes |
|---|---|---|
| Priority | Yes | P1 / P2 / P3 dropdown |
| Added By | Yes | Engineer name or initials — free text |
| Title | Yes | Short descriptive title — what was the problem |
| If You're Seeing... | Yes | One symptom per line. Each line becomes a trigger bullet on the card. |
| Seen On | Recommended | Client name / device / environment. Not required but important for context. |
| Root Cause | Yes | What was actually happening — plain text |
| Fix Applied | Yes | Monospace textarea — paste exact PowerShell, CLI steps, or console instructions |
| Result | Recommended | What happened after the fix was applied |
| Security Note | Optional | Security implications, trade-offs, coverage changes. Drives node 04 to cyan. |
| Products Involved | Optional | Grouped multi-select — see Product Groups section |
| Tags | Optional | Comma-separated — used for filtering and tag chip display |
All filter controls sit in the page header above the stat strip. They apply to both list and board views simultaneously. Changing any filter re-renders the current view without resetting the other filters.
| Control | Filters On | Behaviour |
|---|---|---|
| Search input | title, rootCause, fix, result, seenOn, secNote, triggers[], tags[] | Case-insensitive substring match across all text fields. Updates on every keystroke. |
| All Priority dropdown | e.sev | P1 / P2 / P3 — single select. In board view, selecting P1 collapses the P2 and P3 columns to empty. |
| All Tags dropdown | e.tags[] | Single-tag filter populated from all unique tags across all entries. Updates when entries change. |
| Quick tag chips | e.tags[] | Top 10 most frequent tags shown as clickable chips. Clicking toggles the tag filter and syncs with the dropdown. Active chip is highlighted cyan. |
Each entry is a flat object. In demo mode entries live in the in-memory entries[] array. In live mode they map to SharePoint list columns via mapSPItem() on read and mapToSP() on write.
| Field | Type | SP Column | Notes |
|---|---|---|---|
| id | string | Title | Format: FI-0001. Auto-assigned. Read-only after save. |
| sev | string | Priority | "P1" | "P2" | "P3" |
| title | string | EntryTitle | Short descriptive title |
| triggers | string[] | Triggers | JSON-serialised array. Each item is one symptom bullet. |
| seenOn | string | SeenOn | Free text — client/device/environment context |
| rootCause | string | RootCause | Plain text explanation |
| fix | string | FixApplied | Raw commands or steps — rendered in monospace code block |
| result | string | Result | Outcome description |
| secNote | string | SecurityNote | Optional. Security implications or coverage notes. |
| tags | string[] | Tags | Comma-separated string in SP, parsed to array in JS |
| products | string[] | Products | Comma-separated string in SP, parsed to array in JS |
| author | string | Author | Engineer name — free text |
| date | string | Created | ISO date string (YYYY-MM-DD). SP Created field on write. |
The Products Involved field in the add entry panel uses a custom grouped multi-select. Products are defined in PRODUCT_GROUPS at the top of the JS block. Items are grouped by category matching the krawczyk.solutions console suite. An Other row at the bottom accepts free-text entries — type the product name and press Enter.
| Group | Items |
|---|---|
| Security & Threat | Microsoft Defender, SentinelOne, Huntress, RocketCyber, SOAR, Shadow AI Governance |
| Network & Firewall | FortiGate, SonicWall, Meraki, Auvik |
| Backup & Recovery | Datto, Cove (N-able), ShadowProtect SPX |
| RMM & Endpoint | NinjaRMM, ConnectWise Automate, Intune, ScalePad |
| PSA & Ticketing | ConnectWise Manage, ConnectWise IVR Triage, Engineer Triage |
| Communications | Dialpad, 8x8 |
| Identity & Access | Keeper, Duo, Mimecast |
| Microsoft Stack | Azure, Microsoft 365, Purview, Power BI |
| AI & Automation | Copilot, AI Agent Platform, API Command Console |
| Business & Finance | Ingram Micro, TD Synnex, Azure Provisioner |
PRODUCT_GROUPS array in the JS block. Existing entries with custom "Other" products are unaffected — those values are stored as plain strings and will continue to display correctly. No migration is needed.
The SP connection is controlled by two variables at the top of the JS block. Setting DEMO_MODE = false activates live SP calls. SP_CONFIG holds the site URL and list name.
| Column Name | Type | Notes |
|---|---|---|
| Title | Single line | Built-in SP field — used for the FI-XXXX entry ID |
| Priority | Single line | Values: P1, P2, P3 |
| EntryTitle | Single line | The human-readable entry title |
| Triggers | Multiple lines | JSON array of trigger strings |
| SeenOn | Single line | Client/device context |
| RootCause | Multiple lines | Plain text explanation |
| FixApplied | Multiple lines (plain text) | Commands or steps — preserves whitespace |
| Result | Single line | Outcome description |
| SecurityNote | Multiple lines | Optional — security implications |
| Tags | Single line | Comma-separated tag string |
| Products | Single line | Comma-separated product string |
| Author | Single line | Engineer name — not the SP system author field |
FieldIntelEntries on the target SharePoint site. Add all columns from the Required Columns table above. Match column names exactly — they are case-sensitive in the REST API.
SP_CONFIG.siteUrl to your SharePoint site URL and confirm SP_CONFIG.listName matches the list name exactly.
DEMO_MODE to false. The SEED data array is ignored when demo mode is off — only SP data is shown.
fetchEntries(), uncomment the SP REST GET call. In saveEntry(), uncomment the POST block including the getDigest() call. Both have complete endpoint paths, headers, and body structure already written.
FieldIntelEntries list to read and write entries. Read-only users will see entries but the + LOG RESOLUTION form will fail with a 403 on submit.
SEED array is ignored in live mode and has no effect on SP data. It can be left in place for future demo use or removed to reduce file size — either is fine.
| Symptom | Cause | Fix |
|---|---|---|
| Entries load but submit fails with 403 | User has Read but not Contribute on the SP list | Grant Contribute permission to the list for the accessing user or group. |
| Submit fails with "Invalid request digest" | getDigest() returned a stale or invalid token |
Confirm SP_CONFIG.siteUrl is the correct site (not a sub-site). The _api/contextinfo call must hit the same site as the list. |
| Entries load but all fields are empty except ID | SP column names do not match mapSPItem() field mapping |
Log the raw SP response. Verify column internal names (not display names). SP uses the internal name in API responses. |
| Triggers render as "[object Object]" or raw JSON | Triggers column stored as text but not JSON-parsed | Confirm JSON.parse(i.Triggers || '[]') in mapSPItem() is present. The Triggers SP column must store a valid JSON array string. |
| Tag chips and filter dropdown are empty | buildTagFilters() called before entries are loaded |
Confirm buildTagFilters() is called inside init() after entries = await fetchEntries() resolves. |
| Board view columns all show "NO ENTRIES" | Active severity filter is set to a value that excludes all entries in those columns | Clear the severity filter dropdown. The board respects all active filters — a P1 filter will empty the P2 and P3 columns. |
| Detail modal opens but Fix block is empty | e.fix is empty string or undefined for that entry |
Check the SP list entry directly. If the FixApplied column contains content but the modal shows empty, check the mapSPItem() mapping for that field name. |
| COPY button on fix block does not work | navigator.clipboard requires a secure context (HTTPS) |
Ensure the page is served over HTTPS. SharePoint always serves over HTTPS so this should not occur in production — it may fail on localhost testing. |
| + LOG RESOLUTION panel does not open | openPanel() not firing — likely a JS error earlier in the page load |
Check browser console for errors on page load. A JS syntax error can silently block all button handlers. |