# PageSignal

[![Docs Site](https://img.shields.io/badge/docs-GitHub%20Pages-2ea44f)](https://proval-tech.github.io/PageSignal/)

A Chromium extension that automatically detects ServiceNow and ConnectWise PSA tickets and synchronizes them with HaloPSA.

## Features

- **Automatic PSA Detection**: Detects ServiceNow, ConnectWise, and Jira Cloud pages using DOM-based patterns
- **Ticket Parsing**: Extracts ticket numbers, summaries, details, priority, and status
- **HaloPSA Integration**: Authenticates using your HaloPSA session and syncs tickets
- **Field Mapping**: Automatically maps priority and status fields between systems
- **Change Detection**: Tracks changes and updates HaloPSA tickets when source tickets change
- **Local Storage**: Remembers detected tickets with 30-day retention
- **Real-time Monitoring**: Uses MutationObserver to detect tickets in single-page applications
- **Webhook Middleware**: Receives PSA webhooks and stores them in Supabase for cross-client analysis
- **Weekly AI Analysis**: Creates one HaloPSA ticket per company each week (Monday 00:00 UTC)
- **Client Activity View**: Shows clients sorted by unresolved webhook tickets with quick PSA navigation
- **Organization Picker**: Requires selecting an organization from Supabase when syncing detected tickets
- **Scheduled Halo Org Sync**: Imports Halo organizations into `companies` on a schedule

## Architecture

```
PageSignal/
├── manifest.json           # Extension manifest (Manifest V3)
├── src/
│   ├── config.js          # Organization-specific configuration
│   ├── content.js         # Content script (runs on all pages, includes Jira detection)
│   ├── background.js      # Service worker
│   ├── auth/
│   │   └── session-manager.js    # HaloPSA session authentication
│   ├── detectors/
│   │   ├── servicenow.js         # ServiceNow DOM detection
│   │   └── connectwise.js        # ConnectWise DOM detection
│   ├── parsers/
│   │   ├── servicenow-parser.js  # Extract ServiceNow ticket data
│   │   └── connectwise-parser.js # Extract ConnectWise ticket data
│   ├── mappers/
│   │   ├── field-mapper.js       # Map fields to HaloPSA format
│   │   └── url-company-mapper.js # Map site URLs to Supabase companies
│   ├── api/
│   │   └── halo-client.js        # HaloPSA API client
│   ├── storage/
│   │   └── supabase-client.js    # Supabase ticket data client
│   ├── status/
│   │   └── status-utils.js       # Ticket status helpers
│   ├── settings/
│   │   ├── settings.html         # Extension settings page
│   │   ├── settings.js           # Settings logic
│   │   └── settings.css          # Settings styles
│   ├── sidepanel/
│   │   └── sidepanel.html        # Side panel UI
│   ├── sync/
│   │   ├── ticket-sync.js        # Sync orchestration
│   │   ├── sync-page.html        # Sync confirmation page
│   │   ├── sync-page.js          # Sync page logic
│   │   └── sync-page.css         # Sync page styles
│   └── popup/
│       ├── popup.html            # Extension popup UI
│       ├── popup.js              # Popup logic
│       └── popup.css             # Popup styles
├── supabase/
│   └── functions/
│       ├── webhook-ingest/        # Webhook ingest edge function
│       ├── company-analysis/      # Weekly AI analysis edge function
│       ├── organization-sync/     # Scheduled Halo organization import
│       └── _shared/               # Shared middleware utilities
└── icons/
    ├── icon16.png
    ├── icon48.png
    └── icon128.png
```

## Installation

For detailed browser-specific unpacked install steps (Chrome + Edge), see:
- [USER_GUIDE.md – Install the Unpacked Extension (Chrome and Edge)](USER_GUIDE.md#install-the-unpacked-extension-chrome-and-edge)

### 1. Configure for Your Organization

Edit `src/config.js` and customize the `HALO_ENVIRONMENTS` block. Each environment (`production` / `dev`) has its own URLs and instance-specific IDs:

```javascript
const HALO_ENVIRONMENTS = {
    production: {
        domain: 'yourinstance.halopsa.com',
        authUrl: 'https://yourinstance.halopsa.com/auth/token',
        apiUrl: 'https://yourinstance.halopsa.com/api',
        loginUrl: 'https://yourinstance.halopsa.com',
        defaults: {
            user_id: null,
            client_id: 1,           // REQUIRED — look up in HaloPSA > Clients
            site_id: null,
            tickettype_id: null,    // HaloPSA > Configuration > Tickets > Types
            action_outcome_id: null, // HaloPSA > Configuration > Tickets > Outcomes
            createTicketsInHalo: true
        },
        customFields: {
            externalTicketId: null, // INTEGER field ID, or null to disable
            sourceSystem: null,
            sourceUrl: null,
            lastSyncedHash: null
        }
    }
};
```

The active environment is toggled from the extension Settings page (production/dev). Also customize the priority and status mapping tables at the bottom of `config.js`.

### 2. Create Icons

Create icon files in the `icons/` directory:
- `icon16.png` (16x16 pixels)
- `icon48.png` (48x48 pixels)
- `icon128.png` (128x128 pixels)

### 3. Load Extension in Chrome

1. Open Chrome and navigate to `chrome://extensions/`
2. Enable "Developer mode" (toggle in top right)
3. Click "Load unpacked"
4. Select the `PageSignal` directory
5. The extension should now appear in your browser toolbar

## Usage

### Initial Setup

1. **Login to HaloPSA**: Navigate to your HaloPSA instance and log in
2. **Open Extension**: Click the PageSignal icon in your browser toolbar
3. **Verify Connection**: The extension will detect your HaloPSA session automatically

### Detecting Tickets

1. **Visit ServiceNow or ConnectWise**: Navigate to a ticket page
2. **Automatic Detection**: The extension will detect the ticket and parse its data
3. **Auto-Sync**: If enabled, the ticket will be automatically synced to HaloPSA

### Managing Tickets

Open the extension popup to:
- View all detected tickets
- Filter by system (ServiceNow, ConnectWise, Jira Cloud)
- Check sync status
- Manually trigger syncs
- Copy ticket numbers
- Open tickets in original system or HaloPSA
- Jump to a client's PSA from the left-side client list

## Configuration

### HaloPSA Custom Fields

In HaloPSA, create custom fields for tickets:

1. Navigate to Configuration > Tickets > Custom Fields
2. Create the following fields:
   - **External Ticket ID** (Text) - Stores ServiceNow/ConnectWise ticket number
   - **Source System** (Text) - Stores "ServiceNow" or "ConnectWise"
   - **Source URL** (Text) - Stores original ticket URL
   - **Last Synced Hash** (Text) - Internal field for change tracking

3. Note the Custom Field IDs and update `src/config.js`

### Priority Mapping

Edit the mapping tables in `src/config.js`:

```javascript
// ServiceNow priorities (1-5) to HaloPSA priority_id
export const SERVICENOW_PRIORITY_MAP = {
  '1': 1,  // Critical
  '2': 2,  // High
  '3': 3,  // Moderate
  '4': 4,  // Low
  '5': 5,  // Planning
  'default': 3
};
```

### Status Mapping

```javascript
// ServiceNow state to HaloPSA status_id
export const SERVICENOW_STATUS_MAP = {
  '1': 1,   // New -> Open
  '2': 2,   // In Progress
  '3': 3,   // On Hold
  '6': 9,   // Resolved
  '7': 10,  // Closed
  'default': 1
};
```

## Authentication

The extension uses **OAuth2 token-based authentication** with your HaloPSA session:

1. Log in to HaloPSA normally in your browser
2. The extension reads the `access_token` cookie set by HaloPSA after login
3. API requests include `Authorization: Bearer {token}` using that cookie value
4. When your session expires, the extension will prompt you to log in again

**Security Note**: The extension only accesses cookies from your configured HaloPSA domain.

## Detection Methods

### ServiceNow

Detects using:
- Window objects: `window.NOW`, `window.g_form`, `window.GlideForm`
- DOM elements: `.navpage-layout`, `sn-polaris-layout`
- Body classes: `navpage`, `polaris`
- Meta tags: `<meta name="generator" content="ServiceNow">`

### ConnectWise

Detects using:
- Window objects: `window.cwApp`, `window.screenType`, `window.cwWebApi`
- DOM elements: `.cwPage`, `.cwServiceBoardTicket`
- ID/class prefixes: `[id^="cw_"]`, `[class*="cw"]`
- Iframe structure analysis

### Jira Cloud

Detects using (inlined in `content.js`):
- `<meta name="application-name" content="Jira">`
- `document.body#jira`
- Page title ending with `- Jira`
- URL pattern `/browse/PROJ-123`

Ticket key is extracted from the URL or page title. Make sure you are on the issue view (not a board card) for full field extraction.

## Troubleshooting

### Extension Not Detecting Tickets

1. Check browser console for errors (F12 > Console)
2. Verify you're on a ticket detail page (not list view)
3. Check if the PSA system is supported
4. Ensure the page has fully loaded

### Authentication Issues

1. Verify you're logged into HaloPSA in the same browser
2. Check that the domain in `config.js` matches your HaloPSA instance
3. Look for authentication errors in the extension popup
4. Try logging out and back into HaloPSA

### Sync Failures

1. Check the extension popup for error messages
2. Verify custom field IDs are correct in `config.js`
3. Ensure default user_id, client_id, and tickettype_id are valid
4. Check HaloPSA API permissions
5. Verify you haven't exceeded rate limits (500 requests per 5 minutes)

### Finding Cookie Names

If authentication isn't working, identify the correct cookie name:

1. Log in to HaloPSA in Chrome
2. Open DevTools (F12) > Application > Cookies and locate your HaloPSA domain
3. Find the cookie that holds the OAuth2 access token (typically named `access_token`)
4. Update the `accessTokenCookie` string in `HALO_CONFIG` inside `src/config.js` to match

## Development

### Testing

1. Load extension in Chrome as described above
2. Navigate to ServiceNow or ConnectWise test instances
3. Open browser DevTools (F12) to view console logs
4. Check extension background script logs: `chrome://extensions` > "service worker"

### Debugging

- **Content Script**: Check page console (F12 > Console)
- **Background Script**: Click "service worker" link in chrome://extensions
- **Popup**: Right-click popup > "Inspect"

### Building for Production

1. Update version in `manifest.json`
2. Remove any debug code or console.logs
3. Test thoroughly on production systems
4. Package extension: `chrome://extensions` > "Pack extension"

## Middleware (Supabase Edge Functions)

PageSignal includes a middleware layer powered by Supabase Edge Functions:

- `webhook-ingest`: Receives ConnectWise/ServiceNow webhooks and stores normalized rows in `tickets` (`webhook_payloads`).
- `company-analysis`: Runs weekly (Monday 00:00 UTC), analyzes synced tickets from the last 7 days, and creates one HaloPSA ticket per company. Duplicate weekly runs are prevented by `company_analysis_runs`.
- `organization-sync`: Pulls organizations from HaloPSA and upserts them into `companies` for searchable org selection in the extension.
- `ticket-delete`: Deletes ticket rows using server-side service role permissions, so extension removals are enforced in Supabase even when RLS blocks anon deletes.

See [SUPABASE_SETUP.md](SUPABASE_SETUP.md) for deployment steps, required secrets, and webhook payload format.

## Privacy & Security

- **Minimal Permissions**: Only requests necessary permissions (cookies, storage, tabs)
- **Domain-Specific**: Only accesses cookies from configured HaloPSA domain
- **No Data Collection**: All data stays local or goes directly to your HaloPSA instance
- **Secure Storage**: Uses Chrome's encrypted storage APIs
- **No External Servers**: No data sent to third parties

## License

Proprietary - For internal use only

## Support

For issues or questions, contact your organization's IT department

