npm package: @spektra-cloudevents/zoho-asap-widget
Description: Standalone Angular library for Zoho Desk ASAP widget with JWT token authentication and session management
Author: CloudLabs
Latest version: 2.0.0

# Zoho Desk ASAP Widget - Standalone Angular Library

A **completely standalone Angular library** (10+) that wraps the [Zoho Desk ASAP 2.0 widget](https://desk.zoho.com/). This library handles **everything** internally: authentication, session management, and storage clearing. Your app only needs to pass a JWT token - no Zoho-specific code required.

## 🎯 Key Features

- ✅ **Truly Standalone**: Accepts JWT token, handles authentication internally
- ✅ **Zero Zoho Dependencies in App**: No AsapService, no additional authentication code needed
- ✅ **Multi-User Safe**: Automatically detects user changes and clears sessions
- ✅ **Smart Token Refresh**: Distinguishes token refresh (same user) from user switch (different user)
- ✅ **Automatic Session Management**: Clears localStorage, sessionStorage, and cookies on logout
- ✅ **Prefill Support**: Automatic form prefilling with dynamic values
- ✅ **Angular 10-18+ Compatible**: Works with all modern Angular versions
- ✅ **Production Ready**: Clean code, no console logs, robust error handling

## 📦 Installation

```bash
npm install @cloudlabs/zoho-asap-widget
```

Import the module in your app:

```typescript
import { ZohoAsapWidgetModule } from '@cloudlabs/zoho-asap-widget';

@NgModule({
  imports: [ZohoAsapWidgetModule],
  // ...
})
export class AppModule { }
```

## 🚀 Basic Usage

**Prerequisites:** Your app must have a JWT token for authenticating users with Zoho Desk ASAP.

```html
<!-- Simply pass your JWT token to the widget -->
<zoho-asap-widget
  *ngIf="jwtToken"
  [appId]="'YOUR_ZOHO_APP_ID'"
  [orgId]="'YOUR_ZOHO_ORG_ID'"
  [jwtToken]="jwtToken"
  [enabled]="true">
</zoho-asap-widget>
```

**That's it!** The widget handles:
- User authentication with Zoho
- Session management
- Storage cleanup on logout
- User switching detection

### With Form Prefill

```html
<zoho-asap-widget
  *ngIf="jwtToken"
  [appId]="'YOUR_ZOHO_APP_ID'"
  [orgId]="'YOUR_ZOHO_ORG_ID'"
  [jwtToken]="jwtToken"
  [enabled]="true"
  [prefillFields]="prefillData"
  [departmentId]="'YOUR_DEPT_ID'"
  [layoutId]="'YOUR_LAYOUT_ID'">
</zoho-asap-widget>
```

Where `prefillData` is an object in your component:
```typescript
prefillData: any = {
  subject: 'Support Request',              // Standard Zoho field
  cf_event_request_id: 'MS012345678910',  // Custom field
};
```

**Dynamic prefill example:**
```typescript
// Set subject dynamically with user name
this.prefillData.subject = `Support Request from ${this.userDisplayName}`;

// Update prefill based on user context
if (isPremiumUser) {
  this.prefillData = {
    subject: `Priority Support - ${userName}`,
    cf_priority: 'High',
    cf_user_type: 'Premium'
  };
}
```

## 🔧 Component Inputs

```typescript
interface ZohoAsapWidgetInputs {
  // Required
  appId: string;              // Zoho ASAP application ID
  orgId: string;              // Zoho organization ID
  jwtToken: string;           // JWT token for Zoho authentication
  
  // Optional
  enabled?: boolean;          // Enable/disable widget (default: true)
  nonce?: string;             // Security nonce for script loading
  
  // Prefill (optional)
  prefillFields?: {           // Object with field names and values
    [fieldName: string]: any; // e.g., { cf_event_request_id: '12345', cf_priority: 'High' }
  };
  departmentId?: string;      // Department ID (required for prefill)
  layoutId?: string;          // Form layout ID (required for prefill)
}
```

## 🔐 How It Works

### Architecture

```
┌─────────────────┐
│   Your App      │
│  (JWT Token)    │
└────────┬────────┘
         │ JWT Token
         ▼
┌─────────────────┐
│  Zoho Widget    │ ──► Authenticate with JWT
│   (Library)     │
└────────┬────────┘
         │
         ▼ Login with JWT
┌─────────────────┐
│  Zoho Desk      │
│   ASAP API      │
└─────────────────┘
```

### Flow

1. **App authenticates user** and obtains JWT token
2. **App passes JWT token** to widget via `[jwtToken]` input
3. **Widget handles everything:**
   - Logs into Zoho Desk ASAP with JWT token
   - Manages session lifecycle
4. **On logout:** Widget clears all Zoho storage/cookies automatically

## 🧠 Smart User Detection

The widget intelligently handles two scenarios:

### Scenario 1: Same User, Token Refresh
```typescript
// User's JWT token expires and gets refreshed
// Old: eyJhbGc...user@example.com...abc
// New: eyJhbGc...user@example.com...xyz

// Widget: Extracts user identity (email/oid)
// → Same user detected
// → Updates JWT without logout
// → User keeps their session state
```

### Scenario 2: Different User Login
```typescript
// User A logs out, User B logs in
// Old: eyJhbGc...userA@example.com...
// New: eyJhbGc...userB@example.com...

// Widget: Extracts user identity
// → Different user detected
// → Fully logs out User A
// → Clears all storage/cookies
// → Logs in User B with clean session
```

This prevents unnecessary session disruption while ensuring complete isolation between users.
```

## 🧹 Session Management (Automatic)

The widget handles **everything automatically** when component lifecycle changes:

```typescript
// When widget is destroyed (*ngIf becomes false):
// - Detects ngOnDestroy
// - Calls Zoho logout
// - Clears ALL Zoho storage (localStorage, sessionStorage, cookies)
// - Waits for logout confirmation event

// When widget is created fresh (*ngIf becomes true):
// - Clears any residual Zoho storage (safety check)
// - Loads Zoho script
// - Logs into Zoho with JWT token
// - Establishes fresh session
```

**Your app just controls the `*ngIf` condition - widget does the rest!**

## 🔄 Multi-User Scenarios

Perfect for applications where users switch accounts:

```typescript
// User A logged in
this.jwtToken = 'eyJhbGc...userA@example.com...';
// Widget: Logs in User A

// User A logs out
this.jwtToken = null;
// Widget: Detects token removal, logs out, clears all Zoho data

// User B logs in (component recreated)
this.jwtToken = 'eyJhbGc...userB@example.com...';
// Widget: Fresh component → clears storage → new session
// User B sees ONLY their data, never User A's
```

## 📝 Form Prefilling

To prefill ticket fields when opening the widget, pass an object with field names and values:

```html
<zoho-asap-widget
  [appId]="'202588000344171472'"
  [orgId]="'649984843'"
  [jwtToken]="jwtToken"
  [prefillFields]="{
    subject: 'Technical Support Request',        // Standard field
    description: 'Issue details here',           // Standard field
    cf_event_request_id: 'EVENT-12345',         // Custom field
    cf_priority: 'High',                         // Custom field
    cf_category: 'Technical Issue',              // Custom field
    cf_user_email: userEmail,                    // Custom field with dynamic value
    cf_location: 'Building A'                    // Custom field
  }"
  [departmentId]="'202588000019741045'"
  [layoutId]="'202588000019743298'">
</zoho-asap-widget>
```

**Field Types:**

**Standard Zoho Fields** (no prefix needed):
- `subject` - Ticket subject/title
- `description` - Ticket description/body
- `email` - Contact email
- `phone` - Contact phone

**Custom Fields** (require `cf_` prefix):
- `cf_event_request_id` - Your custom event ID field
- `cf_priority` - Your custom priority field
- `cf_category` - Your custom category field
- `cf_*` - Any other custom field you've created

**Requirements:**
- `prefillFields`: Object where keys are field names (standard or custom)
- `departmentId`: Zoho department ID (required for prefill)
- `layoutId`: Zoho form layout ID (required for prefill)

**How to find field API names:**
1. Go to Zoho Desk > Setup > Channels > ASAP
2. Click on your department's form layout
3. Standard fields: Use lowercase names (`subject`, `description`, `email`)
4. Custom fields: Will have API names like `cf_event_request_id`, `cf_priority`

**Dynamic prefill with user context:**
```typescript
// Construct prefill data with user information
prefillData = {
  subject: `Support Request from ${userName}`,
  email: userEmail,
  cf_event_request_id: ticketId,
  cf_user_type: userType,
  cf_priority: priority
};

// Update specific fields dynamically
this.prefillData.cf_event_request_id = newTicketId;
this.prefillData.subject = `Updated Request - ${newTicketId}`;

// Widget automatically reloads when prefillFields object reference changes
this.prefillData = { ...this.prefillData };
```

The widget automatically:
- Configures prefill before loading the Zoho script
- Reapplies prefill after user login
- Reloads widget when `prefillFields` object changes

##  Important: What Your App Needs vs. Doesn't Need

### ✅ Your App Needs to Provide

1. **JWT Token** - For Zoho Desk ASAP authentication
   - Get this from your authentication service/backend
   - Pass it to the widget: `[jwtToken]="yourToken"`

2. **Zoho Configuration** - Your Zoho Desk ASAP credentials
   - `appId`: From Zoho Desk ASAP setup
   - `orgId`: Your Zoho organization ID

3. **Optional: Prefill Data** - Custom field values for ticket forms
   - `prefillFields`: Object with field names and values
   - `departmentId` & `layoutId`: Required if using prefill

### ❌ Your App Does NOT Need

- ~~Any Zoho-specific services or logic~~
- ~~Additional authentication code (widget handles JWT authentication)~~
- ~~Session management code~~
- ~~Storage/cookie clearing logic~~
- ~~Logout/login event handlers~~
- ~~User switching detection~~
- ~~Import anything except `ZohoAsapWidgetModule`~~

**Summary:** Just provide the JWT token and Zoho config - the widget handles all authentication and session management internally!

## 🏗️ Building & Publishing

```bash
# Build the library for production
ng build zoho-asap-widget --configuration production

# Navigate to dist folder
cd dist/zoho-asap-widget

# Publish to npm (one-time login: npm login)
npm publish --access public

# For updates, increment version first:
# npm version patch  # 1.0.0 -> 1.0.1
# npm version minor  # 1.0.0 -> 1.1.0
# npm version major  # 1.0.0 -> 2.0.0
```

## 🐛 Troubleshooting

### Widget shows old user's data after login
- **Cause:** Browser cache not cleared
- **Solution:** Widget now clears storage on `ngOnInit()` automatically
- **Manual fix:** Clear browser cache (Ctrl+Shift+Delete → All time)

### Widget not loading
- **Check:** `appId` and `orgId` are correct
- **Check:** JWT token is valid (not null/undefined)
- **Check:** Network connectivity to Zoho services

### Token refresh causes logout
- **Cause:** Should not happen - widget detects same user
- **Debug:** Check console for user identity extraction logs
- **Verify:** JWT payload has `oid`, `sub`, or `email` claim

## 🔗 Resources

- [Zoho Desk ASAP 2.0 Documentation](https://desk.zoho.com/portal/en/kb/articles/asap-widgets)

## 📄 License

MIT