# CollabSpace Integration Guide

This guide shows how to integrate CollabSpace (co-browsing, screen sharing, in-session chat) into any ERP module in GB4.7MFE.

---

## One-Time Host Setup

In `gblayout.component.ts`, mount the four globally-used UI shells once:

```typescript
import {
  CollabToolbarComponent,
  ChatPanelComponent,
  DetailPanelComponent,
  CursorOverlayComponent,
} from 'features/gbcollabspace';

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    CollabToolbarComponent,   // floating draggable session pill at bottom
    ChatPanelComponent,       // floating chat panel (right side)
    DetailPanelComponent,     // slide-in participant panel (right side)
    CursorOverlayComponent,   // renders other users' cursors on top of everything
  ],
  template: `
    <!-- ...existing layout... -->
    <collab-toolbar />
    <collab-chat-panel />
    <collab-detail-panel />
    <collab-cursor-overlay />
  `,
})
export class GbLayoutComponent { ... }
```

Register the routes and providers in `gbhost`:

```typescript
// gbhost routing (projects/gbhost/src/app.routes.ts):
{
  path: 'collaboration',
  loadChildren: () =>
    import('features/gbcollabspace/collaboration.routes')
      .then(m => m.COLLABORATION_ROUTES),
}

// Also ensure withComponentInputBinding() is in provideRouter() for the join/:token route:
provideRouter(APP_ROUTES, withComponentInputBinding())

// In provideCollaboration (app.config.ts or gbhost providers):
provideCollaboration({
  hubUrl:     environment.collabHubUrl ?? '/hubs/collab',
  apiBaseUrl: environment.apiUrl ?? '',
})
```

---

## Minimal Integration (~10 lines — read-only awareness)

Use when you only want to show a "Collaborate" button and disable inputs during an active session:

```typescript
import { CollabBridgeService } from 'features/gbcollabspace';
import { CollabViewOnlyDirective } from 'features/gbcollabspace';

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [CollabViewOnlyDirective],
})
export class MyRecordComponent {
  private readonly collab = inject(CollabBridgeService);

  readonly inSession = this.collab.isSessionActive;
  readonly canEdit   = this.collab.hasControl;
}
```

```html
@if (!inSession()) {
  <button (click)="startCollab()">👥 {{ 'common.collaborate' | transloco }}</button>
}
@if (inSession() && !canEdit()) {
  <div class="view-only-banner">{{ 'collabspace.viewOnly' | transloco }}</div>
}

<!-- collabViewOnly automatically disables the element when canEdit() is false in a session -->
<input formControlName="amount" collabViewOnly />
<mat-select formControlName="status" collabViewOnly></mat-select>
<textarea formControlName="notes" collabViewOnly></textarea>
```

---

## Full Integration (with form sync)

```typescript
ngOnInit(): void {
  // Broadcast the current record to other participants
  this.collab.notifyRouteChange({
    moduleCode:   'AP',
    recordId:     this.invoice().id,
    currentRoute: this.router.url,
  });
  // Register the reactive form — field changes are synced in both directions
  this.collab.registerForm('ap-invoice-form', this.form);
}

ngOnDestroy(): void {
  this.collab.unregisterForm('ap-invoice-form');
}
```

---

## Starting a Session From a Record

```typescript
async collaborateOnThisRecord(): Promise<void> {
  await this.collab.startSession({
    moduleCode:   'AP',
    recordId:     this.invoice().id,
    recordLabel:  `Invoice #${this.invoice().invoiceNumber}`,
    currentRoute: this.router.url,
  });
  // CollabSpace shows the floating toolbar automatically
}
```

---

## Module Code Reference

| Module | Code |
|--------|------|
| Accounts Payable | AP |
| Accounts Receivable | AR |
| General Ledger | GL |
| Fixed Assets | FA |
| Procurement / Purchase Order | PO |
| Inventory Management | INV |
| Sales / Order Management | SOM |
| Human Resources | HRMS |
| Payroll | PAY |
| CRM | CRM |
| Projects | PROJ |
| Costing | COST |
| Budget | BUDG |
| Security / Access Control | SEC |
| Visitor Pass | VIS |
| Recruitment | RECR |
| Leave Management | LEAVE |
| Training | TRAIN |
| Performance | PERF |
| Asset Tracking | ASSET |
| Maintenance | MAINT |

> For any unlisted module, use a short uppercase code that matches your backend module identifier.

---

## Excluding Fields From Sync

Some fields must never sync across participants (passwords, PINs, audit notes, computed read-only fields):

```html
<!-- data-collab-exclude prevents this field from being included in form diffs -->
<input formControlName="password"  data-collab-exclude />
<input formControlName="auditNote" data-collab-exclude />
<input formControlName="totalAmt"  data-collab-exclude readonly />
```

The `CoBrowseService` deep-diff utility skips paths whose form control element has this attribute.

---

## Troubleshooting

| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Session doesn't connect | `hubUrl` unreachable | Check `environment.collabHubUrl`; confirm backend SignalR hub is running |
| Form fields not syncing | `registerForm` called before form is built | Move `registerForm` to after `FormBuilder.group()` completes |
| Cursor dots not visible | `enableCursorBroadcast` off | Enable in Collaboration Config (`/collaboration/config`) |
| Can't grant control | User is not Host | Only the session Host can transfer control |
| "Admin access required" toast | Missing `CollabAdmin` role | Assign role via Keycloak realm or the project's role management |
| Invite token shows expired | Past `inviteLinkExpiryMinutes` | Regenerate link from the Invite modal |
| Detail panel won't open | `<collab-detail-panel />` not mounted in host layout | Add to `gblayout.component.ts` template and imports |
| `@Input() token` is undefined on join page | `withComponentInputBinding()` missing | Add to `provideRouter()` in `app.config.ts` |
