varminer-app-header 2.1.5 → 2.1.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AppHeader.d.ts","sourceRoot":"","sources":["../src/AppHeader.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,cAAc,EAAe,MAAM,SAAS,CAAC;AAKtD,OAAO,sBAAsB,CAAC;AAQ9B,QAAA,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAwwBvC,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"AppHeader.d.ts","sourceRoot":"","sources":["../src/AppHeader.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,cAAc,EAAe,MAAM,SAAS,CAAC;AAKtD,OAAO,sBAAsB,CAAC;AAQ9B,QAAA,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6wBvC,CAAC;AAEF,eAAe,SAAS,CAAC"}
package/dist/index.d.ts CHANGED
@@ -40,5 +40,88 @@ interface Translations {
40
40
  declare const translations: Record<SupportedLanguage, Translations>;
41
41
  declare const getTranslations: (language?: SupportedLanguage) => Translations;
42
42
 
43
- export { AppHeader, DrawerProvider, getTranslations, translations, useDrawer };
44
- export type { AppHeaderProps, SupportedLanguage, Translations, UserProfile };
43
+ /** localStorage key used by IAM app for user details (GET /v1/userManagement/userDetails response). */
44
+ declare const USER_DETAILS_STORAGE_KEY = "linn-i-am-userDetails";
45
+ /** Stored value is the full API response from userDetails. */
46
+ interface UserDetailsStoredResponse {
47
+ status: "SUCCESS" | "FAILURE";
48
+ statusinfo: {
49
+ code: string;
50
+ message: string;
51
+ };
52
+ data?: {
53
+ usersDetails: UserDetailsItem[];
54
+ };
55
+ errors?: Record<string, string> | null;
56
+ }
57
+ interface UserDetailsItem {
58
+ userId: number;
59
+ firstName: string;
60
+ lastName: string;
61
+ email: string;
62
+ status: string;
63
+ roles?: string[];
64
+ lastLogin?: string;
65
+ createdDate?: string;
66
+ contactNumber?: {
67
+ phoneType: string;
68
+ phoneNumber: string;
69
+ }[];
70
+ timeZone?: string;
71
+ workInfo?: {
72
+ jobTitle?: string;
73
+ department?: string;
74
+ supervisor?: string;
75
+ };
76
+ }
77
+ /**
78
+ * Read user details from IAM app localStorage (set after verify-OTP on same origin).
79
+ * Does not write or overwrite this key; IAM app is the source of truth.
80
+ */
81
+ declare function getStoredUserDetails(): UserDetailsItem | null;
82
+ declare const getUserDataFromStorage: () => {
83
+ name: any;
84
+ email: any;
85
+ role: any;
86
+ avatar: any;
87
+ initials: any;
88
+ } | null;
89
+ declare const getNotificationCountFromStorage: () => number;
90
+ declare const getMessageCountFromStorage: () => number | undefined;
91
+ declare const getI18nLocaleFromStorage: () => string | null;
92
+ declare const setI18nLocaleToStorage: (locale: string) => void;
93
+ /**
94
+ * Get all data from localStorage (persist:userdb) including decoded accessToken
95
+ * This function reads all available data without hardcoding and doesn't require any parameters
96
+ * @returns Comprehensive object containing all available data from localStorage and decoded JWT payload
97
+ */
98
+ declare const getAllDataFromStorage: () => any;
99
+ /**
100
+ * Get profile picture URL from object store API using tenant_id and user_id from JWT token
101
+ * @param baseUrl - Base URL for the object store API (default: http://objectstore.impact0mics.local:9012)
102
+ * @returns Profile picture URL or null if tenant_id or user_id is not available
103
+ */
104
+ declare const getProfilePictureUrl: (baseUrl?: string) => string | null;
105
+ /**
106
+ * Fetch profile picture from API with headers, get S3 URL, generate presigned URL, and return as blob URL
107
+ * This function:
108
+ * 1. Fetches the profile picture path from the API
109
+ * 2. Extracts the S3 filePath from the JSON response
110
+ * 3. Generates a presigned URL for the S3 object
111
+ * 4. Fetches the image using the presigned URL
112
+ * 5. Converts it to a blob URL that can be used in img src
113
+ * @param baseUrl - Base URL for the object store API (default: http://objectstore.impact0mics.local:9012)
114
+ * @param messageId - Optional message ID for X-Message-Id header (default: generated UUID)
115
+ * @param correlationId - Optional correlation ID for X-Correlation-Id header (default: generated UUID)
116
+ * @param awsConfig - AWS configuration (accessKeyId, secretAccessKey, region, bucket)
117
+ * @returns Promise that resolves to blob URL string or null if fetch fails
118
+ */
119
+ declare const fetchProfilePictureAsBlobUrl: (baseUrl?: string, messageId?: string, correlationId?: string, awsConfig?: {
120
+ accessKeyId: string;
121
+ secretAccessKey: string;
122
+ region?: string;
123
+ bucket?: string;
124
+ }) => Promise<string | null>;
125
+
126
+ export { AppHeader, DrawerProvider, USER_DETAILS_STORAGE_KEY, fetchProfilePictureAsBlobUrl, getAllDataFromStorage, getI18nLocaleFromStorage, getMessageCountFromStorage, getNotificationCountFromStorage, getProfilePictureUrl, getStoredUserDetails, getTranslations, getUserDataFromStorage, setI18nLocaleToStorage, translations, useDrawer };
127
+ export type { AppHeaderProps, SupportedLanguage, Translations, UserDetailsItem, UserDetailsStoredResponse, UserProfile };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EACtB,+BAA+B,EAC/B,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.esm.js CHANGED
@@ -35,7 +35,42 @@ const DrawerProvider = ({ children }) => {
35
35
  };
36
36
  const useDrawer = () => React.useContext(DrawerContext);
37
37
 
38
+ // --- IAM app user details (shared from IAM after verify-OTP, same origin) ---
39
+ /** localStorage key used by IAM app for user details (GET /v1/userManagement/userDetails response). */
40
+ const USER_DETAILS_STORAGE_KEY = "linn-i-am-userDetails";
41
+ /**
42
+ * Read user details from IAM app localStorage (set after verify-OTP on same origin).
43
+ * Does not write or overwrite this key; IAM app is the source of truth.
44
+ */
45
+ function getStoredUserDetails() {
46
+ try {
47
+ const raw = localStorage.getItem(USER_DETAILS_STORAGE_KEY);
48
+ if (!raw)
49
+ return null;
50
+ const parsed = JSON.parse(raw);
51
+ if (parsed.status !== "SUCCESS" || !parsed.data?.usersDetails?.length) {
52
+ return null;
53
+ }
54
+ return parsed.data.usersDetails[0];
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
38
60
  const getUserDataFromStorage = () => {
61
+ // Prefer IAM user details when available (same origin, after verify-OTP)
62
+ const iamUser = getStoredUserDetails();
63
+ if (iamUser) {
64
+ const name = [iamUser.firstName, iamUser.lastName].filter(Boolean).join(" ").trim();
65
+ const role = iamUser.roles?.length ? iamUser.roles[0] : iamUser.workInfo?.jobTitle ?? "";
66
+ return {
67
+ name: name || "",
68
+ email: iamUser.email || "",
69
+ role: role || "",
70
+ avatar: undefined,
71
+ initials: name ? name.split(/\s+/).map((s) => s[0]).join("").slice(0, 2).toUpperCase() : undefined,
72
+ };
73
+ }
39
74
  const userDbString = localStorage.getItem("persist:userdb");
40
75
  if (userDbString) {
41
76
  try {
@@ -354,45 +389,167 @@ const getProfilePictureUrl = (baseUrl = "http://objectstore.impact0mics.local:90
354
389
  }
355
390
  };
356
391
  /**
357
- * Fetch profile picture from API with headers and return as blob URL
358
- * This function fetches the image with required headers (X-Message-Id, X-Correlation-Id)
359
- * and converts it to a blob URL that can be used in img src
392
+ * Generate AWS S3 presigned URL for accessing S3 object
393
+ * @param s3Url - Full S3 URL (e.g., https://bucket.s3.region.amazonaws.com/key)
394
+ * @param accessKeyId - AWS Access Key ID
395
+ * @param secretAccessKey - AWS Secret Access Key
396
+ * @param region - AWS Region (default: ap-south-2)
397
+ * @param expiresIn - Expiration time in seconds (default: 3600 = 1 hour)
398
+ * @returns Presigned URL string
399
+ */
400
+ const generateS3PresignedUrl = async (s3Url, accessKeyId, secretAccessKey, region = "ap-south-2", expiresIn = 3600) => {
401
+ try {
402
+ // Parse S3 URL to extract bucket, region, and key
403
+ // Format: https://bucket.s3.region.amazonaws.com/key or https://bucket.s3-region.amazonaws.com/key
404
+ const url = new URL(s3Url);
405
+ const hostnameParts = url.hostname.split('.');
406
+ let bucket = hostnameParts[0];
407
+ let extractedRegion = region;
408
+ // Try to extract region from hostname (format: bucket.s3.region.amazonaws.com)
409
+ if (hostnameParts.length >= 3 && hostnameParts[1] === 's3') {
410
+ extractedRegion = hostnameParts[2] || region;
411
+ }
412
+ else if (hostnameParts.length >= 2 && hostnameParts[1].startsWith('s3-')) {
413
+ // Format: bucket.s3-region.amazonaws.com
414
+ extractedRegion = hostnameParts[1].substring(3) || region;
415
+ }
416
+ const key = url.pathname.substring(1); // Remove leading slash
417
+ // AWS credentials
418
+ const awsAccessKeyId = accessKeyId;
419
+ const awsSecretAccessKey = secretAccessKey;
420
+ const awsRegion = extractedRegion;
421
+ // Current timestamp
422
+ const now = new Date();
423
+ const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, '');
424
+ const amzDate = dateStamp + 'T' + now.toISOString().slice(11, 19).replace(/[:-]/g, '') + 'Z';
425
+ // Step 1: Create canonical request
426
+ const canonicalUri = '/' + encodeURIComponent(key).replace(/%2F/g, '/');
427
+ const canonicalQuerystring = `X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=${encodeURIComponent(awsAccessKeyId + '/' + dateStamp + '/' + awsRegion + '/s3/aws4_request')}&X-Amz-Date=${amzDate}&X-Amz-Expires=${expiresIn}&X-Amz-SignedHeaders=host`;
428
+ const canonicalHeaders = `host:${bucket}.s3.${awsRegion}.amazonaws.com\n`;
429
+ const signedHeaders = 'host';
430
+ const payloadHash = 'UNSIGNED-PAYLOAD';
431
+ const canonicalRequest = `GET\n${canonicalUri}\n${canonicalQuerystring}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHash}`;
432
+ // Step 2: Create string to sign
433
+ const algorithm = 'AWS4-HMAC-SHA256';
434
+ const credentialScope = `${dateStamp}/${awsRegion}/s3/aws4_request`;
435
+ const stringToSign = `${algorithm}\n${amzDate}\n${credentialScope}\n${await sha256(canonicalRequest)}`;
436
+ // Step 3: Calculate signature
437
+ const kDate = await hmacSha256('AWS4' + awsSecretAccessKey, dateStamp);
438
+ const kRegion = await hmacSha256(kDate, awsRegion);
439
+ const kService = await hmacSha256(kRegion, 's3');
440
+ const kSigning = await hmacSha256(kService, 'aws4_request');
441
+ const signature = await hmacSha256(kSigning, stringToSign);
442
+ // Convert signature to hex
443
+ const signatureHex = arrayBufferToHex(signature);
444
+ // Step 4: Construct presigned URL
445
+ const presignedUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com${canonicalUri}?${canonicalQuerystring}&X-Amz-Signature=${signatureHex}`;
446
+ return presignedUrl;
447
+ }
448
+ catch (err) {
449
+ console.error("Error generating S3 presigned URL:", err);
450
+ throw err;
451
+ }
452
+ };
453
+ // Helper function for SHA-256 hashing
454
+ const sha256 = async (message) => {
455
+ const msgBuffer = new TextEncoder().encode(message);
456
+ const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
457
+ return Array.from(new Uint8Array(hashBuffer))
458
+ .map(b => b.toString(16).padStart(2, '0'))
459
+ .join('');
460
+ };
461
+ // Helper function for HMAC-SHA256
462
+ const hmacSha256 = async (key, message) => {
463
+ const encoder = new TextEncoder();
464
+ let keyBuffer;
465
+ if (typeof key === 'string') {
466
+ keyBuffer = encoder.encode(key);
467
+ }
468
+ else {
469
+ keyBuffer = new Uint8Array(key);
470
+ }
471
+ const messageBuffer = encoder.encode(message);
472
+ const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
473
+ return await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);
474
+ };
475
+ // Helper function to convert ArrayBuffer to hex string
476
+ const arrayBufferToHex = (buffer) => {
477
+ return Array.from(new Uint8Array(buffer))
478
+ .map(b => b.toString(16).padStart(2, '0'))
479
+ .join('');
480
+ };
481
+ /**
482
+ * Fetch profile picture from API with headers, get S3 URL, generate presigned URL, and return as blob URL
483
+ * This function:
484
+ * 1. Fetches the profile picture path from the API
485
+ * 2. Extracts the S3 filePath from the JSON response
486
+ * 3. Generates a presigned URL for the S3 object
487
+ * 4. Fetches the image using the presigned URL
488
+ * 5. Converts it to a blob URL that can be used in img src
360
489
  * @param baseUrl - Base URL for the object store API (default: http://objectstore.impact0mics.local:9012)
361
490
  * @param messageId - Optional message ID for X-Message-Id header (default: generated UUID)
362
491
  * @param correlationId - Optional correlation ID for X-Correlation-Id header (default: generated UUID)
492
+ * @param awsConfig - AWS configuration (accessKeyId, secretAccessKey, region, bucket)
363
493
  * @returns Promise that resolves to blob URL string or null if fetch fails
364
494
  */
365
- const fetchProfilePictureAsBlobUrl = async (baseUrl = "http://objectstore.impact0mics.local:9012", messageId, correlationId) => {
495
+ const fetchProfilePictureAsBlobUrl = async (baseUrl = "http://objectstore.impact0mics.local:9012", messageId, correlationId, awsConfig) => {
366
496
  try {
367
497
  const profilePictureUrl = getProfilePictureUrl(baseUrl);
368
498
  if (!profilePictureUrl) {
369
499
  return null;
370
500
  }
501
+ // AWS credentials (default values provided by user)
502
+ const defaultAwsConfig = {
503
+ accessKeyId: "AKIAVRUVTJGLBCYZEI5L",
504
+ secretAccessKey: "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",
505
+ region: "ap-south-2",
506
+ bucket: "development-varminer-test"
507
+ };
508
+ const finalAwsConfig = awsConfig || defaultAwsConfig;
371
509
  // Generate message ID and correlation ID if not provided
372
510
  const msgId = messageId || `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
373
511
  const corrId = correlationId || `corr-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
374
- // Fetch the image with required headers
375
- const response = await fetch(profilePictureUrl, {
512
+ // Step 1: Fetch the profile picture path from API (returns JSON with filePath)
513
+ const apiResponse = await fetch(profilePictureUrl, {
376
514
  method: 'GET',
377
515
  headers: {
378
516
  'X-Message-Id': msgId,
379
517
  'X-Correlation-Id': corrId,
380
518
  },
381
519
  });
382
- // Check if the response is successful
383
- if (!response.ok) {
384
- console.warn(`Failed to fetch profile picture: ${response.status} ${response.statusText}`);
520
+ // Check if the API response is successful
521
+ if (!apiResponse.ok) {
522
+ console.warn(`Failed to fetch profile picture path: ${apiResponse.status} ${apiResponse.statusText}`);
523
+ return null;
524
+ }
525
+ // Parse JSON response
526
+ const apiData = await apiResponse.json();
527
+ // Extract filePath from response
528
+ const s3Url = apiData?.filePath;
529
+ if (!s3Url || typeof s3Url !== 'string') {
530
+ console.warn('Profile picture API response does not contain valid filePath');
531
+ return null;
532
+ }
533
+ // Step 2: Generate presigned URL for S3 object
534
+ const presignedUrl = await generateS3PresignedUrl(s3Url, finalAwsConfig.accessKeyId, finalAwsConfig.secretAccessKey, finalAwsConfig.region);
535
+ // Step 3: Fetch the image using presigned URL
536
+ const imageResponse = await fetch(presignedUrl, {
537
+ method: 'GET',
538
+ });
539
+ // Check if the image response is successful
540
+ if (!imageResponse.ok) {
541
+ console.warn(`Failed to fetch profile picture image: ${imageResponse.status} ${imageResponse.statusText}`);
385
542
  return null;
386
543
  }
387
544
  // Check if the response is an image
388
- const contentType = response.headers.get('content-type');
545
+ const contentType = imageResponse.headers.get('content-type');
389
546
  if (!contentType || !contentType.startsWith('image/')) {
390
547
  console.warn(`Profile picture response is not an image: ${contentType}`);
391
548
  return null;
392
549
  }
393
- // Convert response to blob
394
- const blob = await response.blob();
395
- // Create blob URL
550
+ // Step 4: Convert response to blob
551
+ const blob = await imageResponse.blob();
552
+ // Step 5: Create blob URL
396
553
  const blobUrl = URL.createObjectURL(blob);
397
554
  return blobUrl;
398
555
  }
@@ -438,7 +595,8 @@ const languages = [
438
595
  { code: "en", name: "English", flag: GBFlag },
439
596
  { code: "es", name: "Spanish", flag: ESFlag },
440
597
  ];
441
- const LanguageSelector = ({ currentLanguage, onLanguageChange, }) => {
598
+ const LanguageSelector = ({ currentLanguage, onLanguageChange: _onLanguageChange, // reserved for parent callback; we reload the page on select
599
+ }) => {
442
600
  const [anchorEl, setAnchorEl] = React__default.useState(null);
443
601
  const open = Boolean(anchorEl);
444
602
  const currentLang = languages.find((lang) => lang.code === currentLanguage) || languages[0];
@@ -799,7 +957,13 @@ const AppHeader = ({ language: languageProp }) => {
799
957
  };
800
958
  const handleSignOutClick = () => {
801
959
  handleClose();
802
- localStorage.clear();
960
+ try {
961
+ localStorage.clear();
962
+ sessionStorage.clear();
963
+ }
964
+ catch (e) {
965
+ console.warn("Clear storage on logout:", e);
966
+ }
803
967
  const path = finalRoutes.logout.startsWith('/')
804
968
  ? finalRoutes.logout
805
969
  : `/${finalRoutes.logout}`;
@@ -947,5 +1111,5 @@ const AppHeader = ({ language: languageProp }) => {
947
1111
  }, children: jsx(MoreIcon, {}) }) })] }) }), renderMobileMenu, renderMenu] }));
948
1112
  };
949
1113
 
950
- export { AppHeader, DrawerProvider, getTranslations, translations, useDrawer };
1114
+ export { AppHeader, DrawerProvider, USER_DETAILS_STORAGE_KEY, fetchProfilePictureAsBlobUrl, getAllDataFromStorage, getI18nLocaleFromStorage, getMessageCountFromStorage, getNotificationCountFromStorage, getProfilePictureUrl, getStoredUserDetails, getTranslations, getUserDataFromStorage, setI18nLocaleToStorage, translations, useDrawer };
951
1115
  //# sourceMappingURL=index.esm.js.map