> ## Documentation Index
> Fetch the complete documentation index at: https://docs.talview.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scheduler

> Developer guide for integrating scheduler with the Proview SDK, including initialization, authentication, UI rendering, events, and full API reference.

## What is Scheduler module

The Scheduler module is part of the Proview SDK, exposed as `Proview.scheduler`. It provides a candidate-facing interface for managing interview scheduler workflows with integrated authentication.

Once initialized, it lets your application:

* **Fetch meetings** — retrieve a candidate's scheduled interviews and their current state.
* **Respond to interviews** — accept, decline, propose a new time, or submit availability on behalf of the candidate.
* **Render scheduler UI** — mount a prebuilt scheduler interface in `inline`, `sidebar`, or `modal` layout.
* **React to state changes** — subscribe to events for phase transitions, route changes, and errors.

Both Promise-based and callback-based async patterns are supported. All UI text is localizable via the `locale` option.

***

## Quickstart

> All async methods support both Promises and an optional callback. This walkthrough uses Promises. See [Callback Signature](#callback-signature) for the callback form.

### 1. Initialize credential

The SDK supports Firebase as a credentialing provider for authentication.

```ts theme={null}
const firebaseCredential = Proview.auth.FirebaseAuthStrategy({
  token: 'your-firebase-auth-token',
});
```

### 2. Initialize scheduler

Call `Proview.scheduler.init()` with the Firebase credential. The SDK authenticates the user and makes the user object available globally at `Proview.user`.

```ts theme={null}
const scheduler = Proview.scheduler.init({
  dsn: 'project-dsn',
  projectId: 'project-123',
  credential: firebaseCredential,
});

// Access user object globally
const user = Proview.user;
console.log(user.uid, user.email, user.displayName);

// Get ID token when needed
const idToken = await user.getIdToken();
```

### 3. Fetch meetings

```ts theme={null}
const meetings = await scheduler.fetchMeetings();
// => MeetingSummary[]
```

### 4. Fetch state for a specific meeting

```ts theme={null}
const state = await scheduler.fetchMeeting('meeting_123');
// => SchedulerState | null
```

### 5. Respond to an interview

**Accept:**

```ts theme={null}
const updated = await scheduler.acceptMeeting({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  reason: 'Confirmed',
});
```

**Decline:**

```ts theme={null}
const updated = await scheduler.declineMeeting({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  reason: 'Not available',
});
```

**Propose a new time** (when the candidate wants to reschedule):

```ts theme={null}
const updated = await scheduler.proposeNewTime({
  meetingId: 'meeting_123',
  interviewId: 'interview_456',
  slotId: 'slot_789',
  note: 'Prefer morning slots',
});
```

**Set availability** (saves the candidate's general availability; not scoped to a single meeting):

```ts theme={null}
const updated = await scheduler.setAvailability({
  meetingId: 'meeting_123',
  weekly: {
    monday: [{ start: '09:00', end: '17:00' }],
    wednesday: [{ start: '10:00', end: '15:00' }],
  },
  blockedDates: ['2026-04-01'],
});
```

### 6. Render the scheduler UI

Mount the scheduler UI into any container element. Three layout modes are supported: `inline`, `sidebar`, and `modal`.

```ts theme={null}
const mounted = scheduler.mount({
  initialView: 'scheduled_interviews',
  mountMode: {
    mode: 'inline',
    containerId: 'proview-scheduler-root',
  },
});
```

### 7. Navigate programmatically

Use `scheduler.navigate()` to trigger a view change from outside the UI:

```ts theme={null}
scheduler.navigate('interviews', { meetingId: 'meeting_123' });
```

Use `mounted.setView()` to change the view from the mounted instance directly:

```ts theme={null}
mounted.setView('scheduled_interviews', { meetingId: 'meeting_123' });
```

### 8. Listen to events

`on()` returns an unsubscribe function, useful for cleanup in component lifecycles:

```ts theme={null}
const unsubscribe = scheduler.on('state.changed', ({ previous, current }) => {
  console.log('Phase changed:', previous.phase, '->', current.phase);
});

// Later, to stop listening:
unsubscribe();
```

### 9. Teardown

When done, unmount the UI and destroy the session:

```ts theme={null}
mounted.unmount();
scheduler.destroy();
```

***

## API Reference

### `Proview.scheduler.init(input)`

Creates a scheduler session.

**Input**

```ts theme={null}
type SchedulerInitInput = {
  dsn: string;               // Required: Project DSN
  projectId: string;         // Required: Project ID
  credential: Credential;    // Required: Authentication credential (e.g., FirebaseAuthStrategy)
};
```

**Returns** — `SchedulerSession`

```ts theme={null}
type SchedulerSession = {
  fetchMeeting(meetingId: string, cb?: MeetingStateCallback): Promise<SchedulerState | null>;
  fetchMeetings(cb?: MeetingsCallback): Promise<MeetingSummary[]>;
  acceptMeeting(payload: AcceptMeetingInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  declineMeeting(payload: DeclineMeetingInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  proposeNewTime(payload: ProposeNewTimeInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  setAvailability(payload: SetAvailabilityInput, cb?: MeetingStateCallback): Promise<SchedulerState>;
  mount(options: MountOptions): MountedSchedulerUi;
  navigate(view: SchedulerView, options: { meetingId: string }): void;
  getCurrentView(): SchedulerView;
  on<E extends SchedulerEventName>(event: E, handler: (payload: SchedulerEventPayloadMap[E]) => void): () => void;
  off<E extends SchedulerEventName>(event: E, handler: (payload: SchedulerEventPayloadMap[E]) => void): void;
  destroy(): void;
};
```

The authenticated user is available globally at `Proview.user` after initialization.

***

## User Object

After initializing the scheduler module, `Proview.user` provides a Firebase-compatible user object accessible globally.

**User Interface**

```ts theme={null}
type User = {
  uid: string;                      // User's unique ID
  email: string | null;             // User's email address
  displayName: string | null;       // User's display name
  emailVerified: boolean;           // Email verification status
  isAnonymous?: boolean;            // Is anonymous user
  metadata?: UserMetadata;          // Creation and last sign-in time
  providerData?: ProviderData[];    // Provider-specific info

  // Methods
  getIdToken(forceRefresh?: boolean): Promise<string>;
  getIdTokenResult(forceRefresh?: boolean): Promise<IdTokenResult>;
};
```

**Example:**

```ts theme={null}
const firebaseCredential = Proview.auth.FirebaseAuthStrategy({
  apiKey: 'your-firebase-api-key',
  authDomain: 'your-project.firebaseapp.com',
  projectId: 'your-firebase-project-id',
});

const scheduler = Proview.scheduler.init({
  dsn: 'project-dsn',
  projectId: 'project-123',
  credential: firebaseCredential,
});

// Access user properties globally
const user = Proview.user;
console.log(user.uid);
console.log(user.email);
console.log(user.displayName);
console.log(user.emailVerified);

// Get ID token
const idToken = await user.getIdToken();

// Force refresh token
const freshToken = await user.getIdToken(true);
```

***

## Firebase Authentication

### `Proview.auth.FirebaseAuthStrategy(config)`

Creates a Firebase authentication credential for use with the Proview SDK modules.

**Input**

```ts theme={null}
type FirebaseAuthStrategyConfig = {
  token: string;  // Required: Firebase authentication token
};
```

**Returns** — `FirebaseAuthStrategy`

A credential object that can be passed to any Proview SDK module initialization.

**Example:**

```ts theme={null}
// Initialize Firebase credential with token
const firebaseCredential = Proview.auth.FirebaseAuthStrategy({
  token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
});

// Use with scheduler module
const scheduler = Proview.scheduler.init({
  dsn: 'project-dsn',
  projectId: 'project-123',
  credential: firebaseCredential,
});
```

***

### `fetchMeetings(cb?)`

Returns all meetings for the candidate.

|              |                                                                        |
| ------------ | ---------------------------------------------------------------------- |
| **Input**    | None                                                                   |
| **Promise**  | `Promise<MeetingSummary[]>`                                            |
| **Callback** | `(error: SchedulerError \| null, meetings?: MeetingSummary[]) => void` |

***

### `fetchMeeting(meetingId, cb?)`

Returns the current scheduler state for one meeting.

|              |                                                                           |
| ------------ | ------------------------------------------------------------------------- |
| **Input**    | `meetingId: string`                                                       |
| **Promise**  | `Promise<SchedulerState \| null>`                                         |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `acceptMeeting(payload, cb?)`

Accepts a specific interview within a meeting.

**Input**

```ts theme={null}
type AcceptMeetingInput = {
  meetingId: string;
  interviewId: string;
  reason?: string;
};
```

|              |                                                                           |
| ------------ | ------------------------------------------------------------------------- |
| **Promise**  | `Promise<SchedulerState>`                                                 |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `declineMeeting(payload, cb?)`

Declines a specific interview within a meeting.

**Input**

```ts theme={null}
type DeclineMeetingInput = {
  meetingId: string;
  interviewId: string;
  reason?: string;
};
```

|              |                                                                           |
| ------------ | ------------------------------------------------------------------------- |
| **Promise**  | `Promise<SchedulerState>`                                                 |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `proposeNewTime(payload, cb?)`

Submits a candidate's preferred alternative time slot.

**Input**

```ts theme={null}
type ProposeNewTimeInput = {
  meetingId: string;
  interviewId: string;
  slotId: string;  // ID of the available slot to propose
  note?: string;
};
```

|              |                                                                           |
| ------------ | ------------------------------------------------------------------------- |
| **Promise**  | `Promise<SchedulerState>`                                                 |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `setAvailability(payload, cb?)`

Saves the candidate's general availability for scheduler. Availability is candidate-scoped, not tied to a specific meeting. The `meetingId` provides session context for the operation.

**Input**

```ts theme={null}
type SetAvailabilityInput = {
  meetingId: string;
  // Recurring weekly time windows. start/end use HH:MM (24-hour) format.
  weekly: Partial<
    Record<
      'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday',
      Array<{ start: string; end: string }>
    >
  >;
  blockedDates: string[]; // Specific dates to exclude, in YYYY-MM-DD format
};
```

|              |                                                                           |
| ------------ | ------------------------------------------------------------------------- |
| **Promise**  | `Promise<SchedulerState>`                                                 |
| **Callback** | `(error: SchedulerError \| null, state?: SchedulerState \| null) => void` |

***

### `mount(options)`

Mounts the scheduler UI into a host element.

**Input**

```ts theme={null}
type SchedulerView = 'scheduled_interviews' | 'interviews';

type MountOptions = {
  // View to show on first render. Defaults to 'scheduled_interviews'.
  initialView?: SchedulerView;

  // BCP 47 locale tag (e.g. 'en-US', 'fr-FR'). Defaults to the browser locale.
  locale?: string;

  // 'sdk' — the SDK renders its own error UI.
  // 'host' — errors are surfaced via the 'session.error' event for the host app to handle.
  errorMode?: 'sdk' | 'host';

  mountMode:
    | { mode: 'inline'; containerId: string }
    | { mode: 'sidebar'; containerId: string; width?: number }
    | { mode: 'modal'; containerId: string; closeOnBackdrop?: boolean; title?: string };

  views?: {
    interviews?: {
      // Which tab to show first in the interviews view.
      initialTab?: 'upcoming' | 'completed';
    };
  };
};
```

**Returns** — `MountedSchedulerUi`

```ts theme={null}
type MountedSchedulerUi = {
  unmount(): void;
  setView(
    view: SchedulerView,
    options: {
      meetingId: string;
      // Arbitrary key-value data passed through to 'ui.route.changed' events.
      metadata?: Record<string, string | number | boolean | null>;
    },
  ): void;
};
```

***

### `navigate(view, options)`

Navigates the mounted UI to a target view. Equivalent to `mounted.setView()` but called on the session rather than the mounted instance.

|               |                         |
| ------------- | ----------------------- |
| **`view`**    | `SchedulerView`         |
| **`options`** | `{ meetingId: string }` |
| **Returns**   | `void`                  |

***

### `getCurrentView()`

Returns the currently active view name.

|             |                 |
| ----------- | --------------- |
| **Returns** | `SchedulerView` |

***

### `on(event, handler)` / `off(event, handler)`

Subscribes or unsubscribes from scheduler events. `on()` returns an unsubscribe function as a convenience alternative to calling `off()`.

**Event names and payloads**

| Event               | Payload                                                                                               |
| ------------------- | ----------------------------------------------------------------------------------------------------- |
| `state.changed`     | `{ previous: SchedulerState; current: SchedulerState; source: 'command' \| 'remote' }`                |
| `ui.route.changed`  | `{ from: SchedulerView \| null; to: SchedulerView; mode: 'memory' \| 'host_v8'; meetingId?: string }` |
| `ui.rendered`       | `{ view: SchedulerView; mode: UiMode; containerId?: string }`                                         |
| `session.error`     | `SchedulerError`                                                                                      |
| `session.destroyed` | `{ meetingId: string; participantId: string; destroyedAt: string }`                                   |

**Full type definitions**

```ts theme={null}
type SchedulerPhase =
  | 'loading'
  | 'invited'
  | 'confirmed'
  | 'declined'
  | 'proposed'
  | 'proposal_declined'
  | 'cancelled'
  | 'completed'
  | 'expired';

type UiMode = 'inline' | 'sidebar' | 'modal';

type SchedulerError = {
  code: string;
  message: string;
  recoverable: boolean;
  source: 'network' | 'auth' | 'validation' | 'internal';
};

type MeetingSummary = {
  meetingId: string;
  phase: SchedulerPhase;
  startAt: string; // ISO 8601
  endAt: string;   // ISO 8601
  title: string;
  round: string;
  mode: string;
};

type SchedulerState = {
  meetingId: string;
  participantId: string;
  phase: SchedulerPhase;
  interviews: Array<{
    interviewId: string;
    title: string;
    round: string;
    type: string;
    mode: string;
    jobId: string;
    startAt: string; // ISO 8601
    endAt: string;   // ISO 8601
    rsvpStatus: 'pending' | 'confirmed' | 'declined' | 'proposed';
    join: { enabled: boolean; url?: string };
  }>;
  availability: {
    weekly: Record<string, Array<{ start: string; end: string }>>;
    blockedDates: string[]; // YYYY-MM-DD
  };
  view: SchedulerView;
  tabs: {
    active: 'upcoming' | 'completed';
  };
  lastSyncedAt?: string; // ISO 8601
};

type SchedulerEventName =
  | 'state.changed'
  | 'ui.route.changed'
  | 'ui.rendered'
  | 'session.error'
  | 'session.destroyed';

type SchedulerEventPayloadMap = {
  'state.changed': {
    previous: SchedulerState;
    current: SchedulerState;
    source: 'command' | 'remote';
  };
  'ui.route.changed': {
    from: SchedulerView | null;
    to: SchedulerView;
    mode: 'memory' | 'host_v8';
    meetingId?: string;
  };
  'ui.rendered': {
    view: SchedulerView;
    mode: UiMode;
    containerId?: string;
  };
  'session.error': SchedulerError;
  'session.destroyed': {
    meetingId: string;
    participantId: string;
    destroyedAt: string;
  };
};
```

***

### `destroy()`

Cleans up the scheduler session and all event listeners. Call `mounted.unmount()` first if the UI is still mounted.

***

## Callback Signature

All async methods accept an optional callback as the last argument. The callback uses a Node.js-style error-first signature.

```ts theme={null}
type MeetingStateCallback = (error: SchedulerError | null, state?: SchedulerState | null) => void;
type MeetingsCallback = (error: SchedulerError | null, meetings?: MeetingSummary[]) => void;
```

Either style works:

```ts theme={null}
// Promise
const state = await scheduler.acceptMeeting(payload);

// Callback
scheduler.acceptMeeting(payload, (error, state) => {
  if (error) return;
  // handle updated state
});
```
