tas-uell-sdk 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,7 @@ export declare class TasButtonComponent implements OnInit, OnDestroy {
15
15
  currentUser: TasCurrentUser;
16
16
  variant: 'default' | 'teal';
17
17
  buttonLabel: string;
18
+ skipStatusCheck: boolean;
18
19
  isLoading: boolean;
19
20
  isCheckingStatus: boolean;
20
21
  isStatusError: boolean;
@@ -47,9 +48,10 @@ export declare class TasButtonComponent implements OnInit, OnDestroy {
47
48
  * Check status endpoint to determine if button should be enabled
48
49
  */
49
50
  private checkStatus;
51
+ private handleStatusError;
50
52
  onClick(): void;
51
53
  private openWaitingRoomModal;
52
54
  private openVideoCallModal;
53
55
  static ɵfac: i0.ɵɵFactoryDeclaration<TasButtonComponent, never>;
54
- static ɵcmp: i0.ɵɵComponentDeclaration<TasButtonComponent, "tas-btn", never, { "roomType": "roomType"; "entityId": "entityId"; "tenant": "tenant"; "businessRole": "businessRole"; "currentUser": "currentUser"; "variant": "variant"; "buttonLabel": "buttonLabel"; }, {}, never, never>;
56
+ static ɵcmp: i0.ɵɵComponentDeclaration<TasButtonComponent, "tas-btn", never, { "roomType": "roomType"; "entityId": "entityId"; "tenant": "tenant"; "businessRole": "businessRole"; "currentUser": "currentUser"; "variant": "variant"; "buttonLabel": "buttonLabel"; "skipStatusCheck": "skipStatusCheck"; }, {}, never, never>;
55
57
  }
@@ -9,8 +9,10 @@ export declare class TasIncomingAppointmentComponent implements OnInit, OnDestro
9
9
  tenant: string;
10
10
  businessRole: TasBusinessRole;
11
11
  currentUser: TasCurrentUser;
12
+ fromDate: string;
13
+ toDate: string;
12
14
  enterCall: EventEmitter<TasAppointment>;
13
- appointment: TasAppointment | null;
15
+ appointments: TasAppointment[];
14
16
  isLoading: boolean;
15
17
  hasError: boolean;
16
18
  private subscriptions;
@@ -18,16 +20,23 @@ export declare class TasIncomingAppointmentComponent implements OnInit, OnDestro
18
20
  ngOnInit(): void;
19
21
  ngOnDestroy(): void;
20
22
  private loadAppointments;
21
- onEnterCall(): void;
23
+ onEnterCall(appointment: TasAppointment): void;
24
+ /**
25
+ * Check if tas-btn should be shown for an appointment (CONFIRMED or ACTIVE status)
26
+ */
27
+ shouldShowTasBtn(appointment: TasAppointment): boolean;
22
28
  /**
23
29
  * Format date to Spanish format: "Lunes 8 de diciembre"
24
30
  */
25
- get formattedDate(): string;
31
+ formatAppointmentDate(appointment: TasAppointment): string;
26
32
  /**
27
33
  * Format time range: "9:00 - 9:30"
28
34
  */
29
- get formattedTimeRange(): string;
30
- private formatDate;
35
+ formatTimeRange(appointment: TasAppointment): string;
36
+ /**
37
+ * Get the other participant in the call (not the current user)
38
+ */
39
+ getOtherParticipant(appointment: TasAppointment): string;
31
40
  static ɵfac: i0.ɵɵFactoryDeclaration<TasIncomingAppointmentComponent, never>;
32
- static ɵcmp: i0.ɵɵComponentDeclaration<TasIncomingAppointmentComponent, "tas-incoming-appointment", never, { "roomType": "roomType"; "entityId": "entityId"; "tenant": "tenant"; "businessRole": "businessRole"; "currentUser": "currentUser"; }, { "enterCall": "enterCall"; }, never, never>;
41
+ static ɵcmp: i0.ɵɵComponentDeclaration<TasIncomingAppointmentComponent, "tas-incoming-appointment", never, { "roomType": "roomType"; "entityId": "entityId"; "tenant": "tenant"; "businessRole": "businessRole"; "currentUser": "currentUser"; "fromDate": "fromDate"; "toDate": "toDate"; }, { "enterCall": "enterCall"; }, never, never>;
33
42
  }
@@ -1,13 +1,23 @@
1
1
  import { OnInit, OnDestroy, ElementRef, AfterViewInit } from '@angular/core';
2
+ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
2
3
  import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
3
4
  import { TasService } from '../../services/tas.service';
4
5
  import { GeolocationService } from '../../services/geolocation.service';
5
- import { CallState, TasBusinessRole, WaitingRoomUser } from '../../interfaces/tas.interfaces';
6
+ import { CallState, TasBusinessRole, WaitingRoomUser, UserGeoInfo } from '../../interfaces/tas.interfaces';
6
7
  import * as i0 from "@angular/core";
8
+ /** User geolocation panel view states */
9
+ export declare enum UserGeoViewState {
10
+ HIDDEN = "HIDDEN",
11
+ INITIAL = "INITIAL",
12
+ VERIFYING = "VERIFYING",
13
+ VERIFIED = "VERIFIED",
14
+ DENIED = "DENIED"
15
+ }
7
16
  export declare class TasVideocallComponent implements OnInit, OnDestroy, AfterViewInit {
8
17
  activeModal: NgbActiveModal;
9
18
  private tasService;
10
19
  private geolocationService;
20
+ private sanitizer;
11
21
  sessionId: string;
12
22
  token: string;
13
23
  appointmentId?: number;
@@ -31,8 +41,14 @@ export declare class TasVideocallComponent implements OnInit, OnDestroy, AfterVi
31
41
  geoLocationStatus: 'unknown' | 'active' | 'denied';
32
42
  geoRequestActive: boolean;
33
43
  allGeoGranted: boolean;
44
+ userGeoInfo: UserGeoInfo[];
45
+ userGeoViewState: UserGeoViewState;
46
+ UserGeoViewState: typeof UserGeoViewState;
47
+ homeIcon: SafeHtml;
48
+ devModeEnabled: boolean;
49
+ private geoPanelDismissed;
34
50
  private subscriptions;
35
- constructor(activeModal: NgbActiveModal, tasService: TasService, geolocationService: GeolocationService);
51
+ constructor(activeModal: NgbActiveModal, tasService: TasService, geolocationService: GeolocationService, sanitizer: DomSanitizer);
36
52
  ngOnInit(): void;
37
53
  ngAfterViewInit(): void;
38
54
  ngOnDestroy(): void;
@@ -45,6 +61,20 @@ export declare class TasVideocallComponent implements OnInit, OnDestroy, AfterVi
45
61
  * Check if current user can admit others (OWNER, BACKOFFICE, or MODERATOR)
46
62
  */
47
63
  get canAdmitUsers(): boolean;
64
+ /** Users with pending geo status */
65
+ get pendingUsers(): UserGeoInfo[];
66
+ /** Users who granted geo */
67
+ get grantedUsers(): UserGeoInfo[];
68
+ /** Users who denied geo */
69
+ get deniedUsers(): UserGeoInfo[];
70
+ /** Show location panel only if owner and there are users who haven't granted */
71
+ get shouldShowLocationPanel(): boolean;
72
+ /** Check if any user has denied geo */
73
+ get hasAnyDenied(): boolean;
74
+ /** Show user geo panel for owners when not hidden */
75
+ get shouldShowUserGeoPanel(): boolean;
76
+ /** Set user geo view state (for dev controls or close button) */
77
+ setUserGeoViewState(state: UserGeoViewState): void;
48
78
  /**
49
79
  * Admit a user from the waiting room
50
80
  */
@@ -58,19 +88,26 @@ export declare class TasVideocallComponent implements OnInit, OnDestroy, AfterVi
58
88
  */
59
89
  closeLocationPanel(): void;
60
90
  /**
61
- * Request the user to share their location
91
+ * Request the user to share their location (called by owner)
62
92
  */
63
93
  requestUserLocation(): void;
64
94
  private setupSubscriptions;
65
95
  /**
66
96
  * Handle activateGeo request from backend (for non-owner users).
67
- * If geo is already active, report it. If not, prompt user.
97
+ * Directly prompts browser for geolocation - no panel for users.
68
98
  */
69
99
  private handleActivateGeo;
100
+ /** Start geolocation verification (called from template button) */
101
+ startGeoVerification(): Promise<void>;
70
102
  /**
71
- * Report geolocation status to backend.
103
+ * Report granted geolocation to backend.
104
+ * IMPORTANT: Only call with valid coordinates.
72
105
  */
73
106
  private reportGeoStatus;
107
+ /**
108
+ * Report denied geolocation to backend.
109
+ */
110
+ private denyGeoLocation;
74
111
  private initializeCall;
75
112
  private resetVideoPositions;
76
113
  private initInteract;
@@ -34,39 +34,61 @@ export declare class TasWaitingRoomComponent implements OnInit, OnDestroy {
34
34
  private subscriptions;
35
35
  private videoCallModalRef;
36
36
  private geoPosition;
37
+ private geoWasDenied;
38
+ private geoResultSent;
39
+ private mediaPermissionsGranted;
40
+ private geoPermissionsResolved;
41
+ private retryCount;
42
+ private readonly MAX_RETRIES;
37
43
  /** Whether current user is an owner */
38
44
  get isOwner(): boolean;
45
+ /** Whether we're still requesting permissions (for template) */
46
+ get isRequestingPermissions(): boolean;
47
+ /** Whether we're waiting for admission after permissions granted (for template) */
48
+ get isWaitingForAdmission(): boolean;
39
49
  constructor(activeModal: NgbActiveModal, tasService: TasService, geolocationService: GeolocationService, modalService: NgbModal, cdr: ChangeDetectorRef);
40
50
  ngOnInit(): void;
41
51
  /**
42
52
  * Request camera and microphone permissions.
53
+ * Triggers auto-join when permissions are resolved.
43
54
  */
44
55
  private requestMediaPermissions;
45
56
  /**
46
- * Request geolocation immediately on init.
57
+ * Request geolocation permission and cache result.
47
58
  * Only for regular users (not owners/backoffice).
48
- * If user allows, store position and send to backend.
59
+ * Actual send to backend happens after we have videoCallId.
60
+ * Triggers auto-join when geolocation is resolved.
49
61
  */
50
62
  private requestGeolocation;
51
63
  /**
52
- * Send geolocation to backend via modify user endpoint.
53
- * NOTE: Endpoint call is prepared but may not be active yet.
64
+ * Send geo result to backend (granted or denied).
65
+ * Only sends if videoCallId is available and not already sent.
66
+ */
67
+ private sendGeoResultToBackend;
68
+ /**
69
+ * Send granted geolocation to backend via modify user endpoint.
54
70
  */
55
71
  private sendGeolocationToBackend;
72
+ /**
73
+ * Send geolocation denial to backend via modify user endpoint.
74
+ */
75
+ private sendGeoDenialToBackend;
56
76
  ngOnDestroy(): void;
57
77
  /**
58
78
  * Check status to get session info
59
79
  */
60
80
  private checkStatus;
61
81
  /**
62
- * Handle changes to joinable status
82
+ * Handle changes to joinable status.
83
+ * Triggers auto-join when joinable becomes true.
63
84
  */
64
85
  private handleJoinableChange;
65
86
  /**
66
- * Called when user clicks the join button.
67
- * Calls /start to get token then auto-joins.
87
+ * Attempt to auto-join the session when all conditions are met.
88
+ * - For owners: just need joinable + sessionId
89
+ * - For users: need media permissions + geo resolved + joinable + sessionId
68
90
  */
69
- joinSession(): void;
91
+ private tryAutoJoin;
70
92
  /**
71
93
  * Check if user has owner/backoffice role
72
94
  */
@@ -76,11 +98,8 @@ export declare class TasWaitingRoomComponent implements OnInit, OnDestroy {
76
98
  */
77
99
  private startSessionAndJoin;
78
100
  /**
79
- * Closes the waiting room
80
- */
81
- cancel(): void;
82
- /**
83
- * Retry after an error
101
+ * Retry after an error.
102
+ * Resets retry count and restarts the flow.
84
103
  */
85
104
  retry(): void;
86
105
  private openVideoCallModal;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * SVG icons used internally by TAS SDK components.
3
+ * Icons are stored as strings to avoid asset bundling complexity.
4
+ */
5
+ export declare const TAS_ICONS: {
6
+ readonly home: "<svg width=\"120\" height=\"100\" viewBox=\"0 0 120 100\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect x=\"10\" width=\"100\" height=\"100\" rx=\"50\" fill=\"#44D8E8\" fill-opacity=\"0.2\"/>\n<path d=\"M45 70C43.625 70 42.4479 69.5104 41.4688 68.5313C40.4896 67.5521 40 66.375 40 65V46.5625L37.5 48.5C36.9583 48.9167 36.3438 49.0833 35.6562 49C34.9688 48.9167 34.4167 48.5833 34 48C33.5833 47.4583 33.4271 46.8542 33.5312 46.1875C33.6354 45.5208 33.9583 44.9792 34.5 44.5625L56.9375 27.3125C57.3958 26.9792 57.8854 26.7292 58.4062 26.5625C58.9271 26.3958 59.4583 26.3125 60 26.3125C60.5417 26.3125 61.0729 26.3958 61.5938 26.5625C62.1146 26.7292 62.6042 26.9792 63.0625 27.3125L85.5 44.5C86.0417 44.9167 86.3646 45.4583 86.4688 46.125C86.5729 46.7917 86.4167 47.4167 86 48C85.5833 48.5833 85.0417 48.9063 84.375 48.9688C83.7083 49.0313 83.0833 48.8542 82.5 48.4375L60 31.25L45 42.75V65H50.0625C50.7708 65 51.3542 65.2396 51.8125 65.7188C52.2708 66.1979 52.5 66.7917 52.5 67.5C52.5 68.2083 52.2604 68.8021 51.7812 69.2813C51.3021 69.7604 50.7083 70 50 70H45ZM67.3125 73.9375C66.9792 73.9375 66.6667 73.875 66.375 73.75C66.0833 73.625 65.8125 73.4375 65.5625 73.1875L58.5 66.125C58 65.625 57.75 65.0417 57.75 64.375C57.75 63.7083 58 63.125 58.5 62.625C59 62.125 59.5833 61.875 60.25 61.875C60.9167 61.875 61.5 62.125 62 62.625L67.3125 67.875L79.6875 55.5C80.1875 55 80.7812 54.7604 81.4688 54.7813C82.1562 54.8021 82.75 55.0625 83.25 55.5625C83.75 56.0625 84 56.6458 84 57.3125C84 57.9792 83.75 58.5625 83.25 59.0625L69.0625 73.1875C68.8125 73.4375 68.5417 73.625 68.25 73.75C67.9583 73.875 67.6458 73.9375 67.3125 73.9375Z\" fill=\"white\"/>\n<path d=\"M110 5L106.575 3.425L105 0L103.425 3.425L100 5L103.425 6.575L105 10L106.575 6.575L110 5Z\" fill=\"#44D8E8\"/>\n<path d=\"M10 51L6.575 49.425L5 46L3.425 49.425L0 51L3.425 52.575L5 56L6.575 52.575L10 51Z\" fill=\"#44D8E8\"/>\n<path d=\"M95 43.5L93.2875 42.7125L92.5 41L91.7125 42.7125L90 43.5L91.7125 44.2875L92.5 46L93.2875 44.2875L95 43.5Z\" fill=\"#44D8E8\"/>\n<path d=\"M42 4.5L40.2875 3.7125L39.5 2L38.7125 3.7125L37 4.5L38.7125 5.2875L39.5 7L40.2875 5.2875L42 4.5Z\" fill=\"#44D8E8\"/>\n<path d=\"M88 83.5L86.2875 82.7125L85.5 81L84.7125 82.7125L83 83.5L84.7125 84.2875L85.5 86L86.2875 84.2875L88 83.5Z\" fill=\"#44D8E8\"/>\n<path d=\"M120 97.5L118.287 96.7125L117.5 95L116.713 96.7125L115 97.5L116.713 98.2875L117.5 100L118.287 98.2875L120 97.5Z\" fill=\"#44D8E8\"/>\n</svg>";
7
+ };
8
+ export declare type TasIconName = keyof typeof TAS_ICONS;
@@ -36,7 +36,13 @@ export declare enum UserCallAction {
36
36
  BAN = "BAN",
37
37
  CHANGE_STATUS = "CHANGE_STATUS",
38
38
  REQUEST_GEOLOCALIZATION = "REQUEST_GEOLOCALIZATION",
39
- ACTIVATE_GEOLOCATION = "ACTIVATE_GEOLOCATION"
39
+ ACTIVATE_GEOLOCATION = "ACTIVATE_GEOLOCATION",
40
+ DENY_GEOLOCATION = "DENY_GEOLOCATION"
41
+ }
42
+ export declare enum GeoStatus {
43
+ PENDING = "PENDING",
44
+ GRANTED = "GRANTED",
45
+ DENIED = "DENIED"
40
46
  }
41
47
  export declare enum RoomUserStatus {
42
48
  ASSIGNED = "ASSIGNED",
@@ -99,6 +105,9 @@ export interface ProxyVideoStatusUser {
99
105
  userId: number;
100
106
  rol: TasUserRole;
101
107
  status: UserStatus;
108
+ geoStatus: GeoStatus;
109
+ latitude: number | null;
110
+ longitude: number | null;
102
111
  }
103
112
  export interface ProxyVideoStatusResponse {
104
113
  content: {
@@ -109,8 +118,8 @@ export interface ProxyVideoStatusResponse {
109
118
  waitingRoomAvailable: boolean;
110
119
  appointmentId: number;
111
120
  activateGeo: boolean;
112
- geoRequestActive?: boolean;
113
- allGeoGranted?: boolean;
121
+ geoRequestActive: boolean | null;
122
+ allGeoGranted: boolean;
114
123
  users: ProxyVideoStatusUser[];
115
124
  };
116
125
  }
@@ -137,10 +146,22 @@ export interface WaitingRoomUser {
137
146
  name: string;
138
147
  status: RoomUserStatus;
139
148
  }
149
+ /** User geolocation info for owner panel */
150
+ export interface UserGeoInfo {
151
+ userId: number;
152
+ geoStatus: GeoStatus;
153
+ latitude: number | null;
154
+ longitude: number | null;
155
+ }
140
156
  export declare enum AppointmentStatus {
141
157
  CONFIRMED = "CONFIRMED",
142
158
  CANCELLED = "CANCELLED",
143
- RESCHEDULED = "RESCHEDULED"
159
+ RESCHEDULED = "RESCHEDULED",
160
+ ACTIVE = "ACTIVE"
161
+ }
162
+ export interface TasParticipant {
163
+ userId: number;
164
+ name: string;
144
165
  }
145
166
  export interface TasAppointment {
146
167
  id: number;
@@ -154,6 +175,7 @@ export interface TasAppointment {
154
175
  notes: string;
155
176
  entityId: number;
156
177
  roomType: TasRoomType;
178
+ participants: TasParticipant[];
157
179
  }
158
180
  export interface GetAppointmentsRequest {
159
181
  fromDate: string;
@@ -1,10 +1,10 @@
1
1
  import { TasUtilityService } from './tas-utility.service';
2
2
  import { Observable } from 'rxjs';
3
3
  import * as OT from '@opentok/client';
4
- import { GenerateTokenResponse, StartSessionRequest, FinishSessionResponse, ProxyVideoStatusRequest, ProxyVideoStatusResponse, ProxyVideoFinishRequest, ProxyVideoUserModifyRequest, UserCallAction, RoomUserStatus, TasBusinessRole, CallState, ViewMode, WaitingRoomUser, TasAppointment, GetAppointmentsRequest } from '../interfaces/tas.interfaces';
4
+ import { GenerateTokenResponse, StartSessionRequest, FinishSessionResponse, ProxyVideoStatusRequest, ProxyVideoStatusResponse, ProxyVideoFinishRequest, ProxyVideoUserModifyRequest, UserCallAction, RoomUserStatus, GeoStatus, TasBusinessRole, CallState, ViewMode, WaitingRoomUser, UserGeoInfo, TasAppointment, GetAppointmentsRequest } from '../interfaces/tas.interfaces';
5
5
  import { TasConfig, TasHttpClient } from '../config/tas.config';
6
6
  import * as i0 from "@angular/core";
7
- export { CallState, ViewMode, TasBusinessRole, UserCallAction, RoomUserStatus };
7
+ export { CallState, ViewMode, TasBusinessRole, UserCallAction, RoomUserStatus, GeoStatus };
8
8
  export declare class TasService {
9
9
  private httpClient;
10
10
  private config;
@@ -40,12 +40,16 @@ export declare class TasService {
40
40
  geoRequestActive$: Observable<boolean>;
41
41
  private allGeoGrantedSubject;
42
42
  allGeoGranted$: Observable<boolean>;
43
+ private userGeoInfoSubject;
44
+ userGeoInfo$: Observable<UserGeoInfo[]>;
43
45
  private statusPollingInterval;
44
46
  private readonly DEFAULT_POLL_INTERVAL_MS;
45
47
  private wasOwnerPresent;
46
48
  private currentAppointmentId;
47
49
  private currentVideoCallId;
48
50
  private currentTenant;
51
+ private readonly STATUS_CACHE_TTL_MS;
52
+ private statusCache;
49
53
  constructor(httpClient: TasHttpClient, config: TasConfig, tasUtilityService: TasUtilityService);
50
54
  /**
51
55
  * Start automatic status polling for the current session.
@@ -86,8 +90,15 @@ export declare class TasService {
86
90
  * PROXY circuit token: /v2/proxy/video/start
87
91
  */
88
92
  startProxyVideoSession(payload: StartSessionRequest): Observable<GenerateTokenResponse>;
93
+ /**
94
+ * Generate cache key from request payload
95
+ */
96
+ private getStatusCacheKey;
97
+ private inflightStatusRequests;
89
98
  /**
90
99
  * PROXY circuit status: /v2/proxy/video/status
100
+ * Uses a 1-second cache to avoid redundant API calls from multiple components
101
+ * Implements request deduplication for simultaneous calls
91
102
  */
92
103
  getProxyVideoStatus(payload: ProxyVideoStatusRequest): Observable<ProxyVideoStatusResponse>;
93
104
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tas-uell-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "TAS (Telemedicine Assistance Service) SDK for Angular applications - Video call functionality using TokBox/Vonage",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",