@iblai/iblai-js 1.0.16 → 1.0.17

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.
package/README.md CHANGED
@@ -63,6 +63,58 @@ Add this to your `package.json` scripts:
63
63
 
64
64
  No special configuration required. Install and import directly.
65
65
 
66
+ ## Styling Setup (Tailwind CSS)
67
+
68
+ The SDK's React components use Tailwind CSS utility classes. To ensure these classes are included in your app's CSS output, you need two things in your global CSS file:
69
+
70
+ 1. **Import the SDK's base styles** (CSS variables for the IBL design system)
71
+ 2. **Add a `@source` directive** so your Tailwind instance scans the SDK's compiled components for class names
72
+
73
+ ### Tailwind CSS v4 (recommended)
74
+
75
+ In your global CSS file (e.g., `app/globals.css`):
76
+
77
+ ```css
78
+ @import 'tailwindcss';
79
+
80
+ /* SDK: custom base styles (CSS variables, no Tailwind utilities) */
81
+ @import '@iblai/iblai-js/web-containers/styles';
82
+
83
+ /* SDK: scan compiled components so Tailwind generates their utility classes */
84
+ @source "../node_modules/@iblai/iblai-js/dist/web-containers/source";
85
+ ```
86
+
87
+ The `@source` directive tells your Tailwind instance to scan the SDK's compiled JavaScript for class names like `lg:grid-cols-2`, `md:flex`, `hidden`, etc. Without this, responsive and utility classes used only in SDK components won't be generated.
88
+
89
+ ### Tailwind CSS v3
90
+
91
+ In your `tailwind.config.js`, add the SDK's compiled output to the `content` array:
92
+
93
+ ```js
94
+ module.exports = {
95
+ content: [
96
+ './app/**/*.{js,ts,jsx,tsx}',
97
+ './components/**/*.{js,ts,jsx,tsx}',
98
+ './node_modules/@iblai/iblai-js/dist/web-containers/source/**/*.js',
99
+ ],
100
+ };
101
+ ```
102
+
103
+ Then import the base styles in your CSS:
104
+
105
+ ```css
106
+ @import '@iblai/iblai-js/web-containers/styles';
107
+ ```
108
+
109
+ ### Why this is needed
110
+
111
+ The SDK does **not** ship pre-built Tailwind utility CSS. Instead, it ships:
112
+
113
+ - **Base styles** (`styles.css`) — CSS variables and custom component classes that don't depend on Tailwind
114
+ - **Compiled component source** (`source/index.esm.js`) — the bundled JavaScript containing all Tailwind class name strings
115
+
116
+ Your app's single Tailwind instance scans both your own code and the SDK's compiled source, then generates one unified set of utility classes. This avoids CSS cascade conflicts that would occur if two separate Tailwind builds produced overlapping `@layer utilities` blocks.
117
+
66
118
  ## Quick Start
67
119
 
68
120
  ### 1. Initialize the data layer
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { useRef, useEffect, useState, useCallback, useLayoutEffect, forwardRef, createElement as createElement$1, useMemo, createContext, useReducer, useImperativeHandle, useContext, useDebugValue, isValidElement, Children, PureComponent, cloneElement, Component } from 'react';
3
3
  import { LOCAL_STORAGE_KEYS, TimeTracker, getInitials, useTenantMetadata, isAlphaNumeric32, checkRbacPermission, getTimeAgo, formatRelativeTime as formatRelativeTime$2, ANONYMOUS_USERNAME, combineCSVData, redirectToAuthSpaJoinTenant, redirectToAuthSpa, getAuthSpaJoinUrl } from '@iblai/web-utils';
4
- import { useTimeTrackingMutation, useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useGetNotificationsCountQuery, useLazyGetNotificationsQuery, useMarkAllAsReadMutation, useCreateNotificationPreviewMutation, useSendNotificationMutation, useGetMentorsQuery, useGetRbacPoliciesQuery, usePlatformUsersQuery, usePlatformUserGroupsQuery, useUpdateTemplateMutation, useGetTemplateDetailsQuery, useGetTemplatesQuery, useLazyGetTemplateDetailsQuery, useToggleTemplateMutation, useGetTopicsStatsQuery, useGetUsersStatsQuery, useGetSessionStatsQuery, useGetTopicsDetailsStatsQuery, useGetAccessTimeHeatmapQuery, useGetUserDetailsStatsQuery, useGetTranscriptsConversationHeadlineQuery, useGetAverageRatingQuery, useGetFinancialStatsQuery, useGetDetailedFinancialStatsQuery, useGetTranscriptsMessagesDetailsQuery, useGetTranscriptsMessagesQuery, useGetReportsQuery, useGetReportDetailQuery, useCreateReportMutation, useGetMentorPublicSettingsQuery, useGetContentAnalyticsQuery, useGetContentAnalyticsDetailsQuery } from '@iblai/data-layer';
4
+ import { useTimeTrackingMutation, useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useGetNotificationsCountQuery, useLazyGetNotificationsQuery, useMarkAllAsReadMutation, useCreateNotificationPreviewMutation, useSendNotificationMutation, useGetMentorsQuery, useGetRbacPoliciesQuery, usePlatformUsersQuery, usePlatformUserGroupsQuery, useUpdateTemplateMutation, useGetTemplateDetailsQuery, useGetTemplatesQuery, useLazyGetTemplateDetailsQuery, useToggleTemplateMutation, useGetTopicsStatsQuery, useGetUsersStatsQuery, useGetSessionStatsQuery, useGetTopicsDetailsStatsQuery, useGetAccessTimeHeatmapQuery, useGetUserDetailsStatsQuery, useGetTranscriptsConversationHeadlineQuery, useGetAverageRatingQuery, useGetFinancialStatsQuery, useGetDetailedFinancialStatsQuery, useGetTranscriptsMessagesDetailsQuery, useGetTranscriptsMessagesQuery, useGetReportsQuery, useGetReportDetailQuery, useCreateReportMutation, useLazyGetDownloadReportFromURLQuery, useGetMentorPublicSettingsQuery, useGetContentAnalyticsQuery, useGetContentAnalyticsDetailsQuery } from '@iblai/data-layer';
5
5
  import * as ReactDOM from 'react-dom';
6
6
  import ReactDOM__default from 'react-dom';
7
7
  import { jsx, Fragment as Fragment$1, jsxs } from 'react/jsx-runtime';
@@ -150156,7 +150156,7 @@ const defaultTabs = [
150156
150156
  },
150157
150157
  ];
150158
150158
  const className = 'inline-flex items-center justify-center whitespace-nowrap rounded-[5px] px-2 sm:px-2.5 pt-1 pb-0.5 sm:text-sm text-gray-500 ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-blue-600 data-[state=active]:font-semibold text-sm font-medium data-[state=active]:bg-[#f5f7fb] data-[state=active]:shadow-none cursor-pointer';
150159
- function AnalyticsLayout({ children, currentPath, basePath, onTabChange, tabs = defaultTabs, activeTabClassName = '', beforeDataReports, }) {
150159
+ function AnalyticsLayout({ children, currentPath, basePath, onTabChange, tabs = defaultTabs, activeTabClassName = '', beforeDataReports, excludeTabs = [], }) {
150160
150160
  useEffect(() => {
150161
150161
  const mainContentContainer = document.getElementById('main-content-container');
150162
150162
  if (mainContentContainer) {
@@ -150189,7 +150189,9 @@ function AnalyticsLayout({ children, currentPath, basePath, onTabChange, tabs =
150189
150189
  const handleTabClick = (tabValue) => {
150190
150190
  onTabChange(tabValue);
150191
150191
  };
150192
- return (jsxs("div", { className: "flex flex-col flex-1 min-h-0 h-full overflow-hidden overscroll-none bg-[#f5f7fb]", children: [jsx("div", { className: "p-6 pb-0", children: jsx("div", { className: "flex items-center justify-between mb-6", children: jsxs("div", { className: "flex items-center justify-between w-full overflow-x-auto", children: [jsx(Tabs, { className: "mb-6 bg-[#f5f7fb]", value: getTabValue(currentPath), children: jsx(TabsList, { className: "bg-[#f5f7fb]", children: tabs.map((tab) => {
150192
+ return (jsxs("div", { className: "flex flex-col flex-1 min-h-0 h-full overflow-hidden overscroll-none bg-[#f5f7fb]", children: [jsx("div", { className: "p-6 pb-0", children: jsx("div", { className: "flex items-center justify-between mb-6", children: jsxs("div", { className: "flex items-center justify-between w-full overflow-x-auto", children: [jsx(Tabs, { className: "mb-6 bg-[#f5f7fb]", value: getTabValue(currentPath), children: jsx(TabsList, { className: "bg-[#f5f7fb]", children: tabs
150193
+ .filter((tab) => !excludeTabs.includes(tab.value))
150194
+ .map((tab) => {
150193
150195
  const isActive = getTabValue(currentPath) === tab.value;
150194
150196
  return (jsx(TabsTrigger, { value: tab.value, className: cn(className, isActive && activeTabClassName), onClick: () => handleTabClick(tab.value), children: tab.label }, tab.value));
150195
150197
  }) }) }), jsxs("div", { className: "flex items-center gap-4", children: [beforeDataReports, jsx(Tabs, { className: "mb-6 bg-[#f5f7fb]", value: getTabValue(currentPath), children: jsx(TabsList, { className: "bg-[#f5f7fb]", children: jsx(TabsTrigger, { value: "reports", className: cn(className, getTabValue(currentPath) === 'reports' && activeTabClassName), onClick: () => handleTabClick('reports'), children: "Data Reports" }) }) })] })] }) }) }), jsx("div", { className: "flex-1 overflow-auto overscroll-contain scrollbar-none px-6 pb-6 pt-0", children: children })] }));
@@ -185631,6 +185633,26 @@ const useReports = ({ tenantKey, selectedMentorId, usergroupIds, }) => {
185631
185633
  refetchOnMountOrArgChange: false,
185632
185634
  });
185633
185635
  const [createReport, { isLoading: isCreating }] = useCreateReportMutation();
185636
+ const [fetchReportBlob] = useLazyGetDownloadReportFromURLQuery();
185637
+ const downloadReportFromUrl = useCallback(async (url) => {
185638
+ try {
185639
+ const result = await fetchReportBlob({ url }).unwrap();
185640
+ const blob = result instanceof Blob ? result : new Blob([result]);
185641
+ const blobUrl = URL.createObjectURL(blob);
185642
+ const link = document.createElement('a');
185643
+ link.href = blobUrl;
185644
+ link.download = 'report.csv';
185645
+ document.body.appendChild(link);
185646
+ link.click();
185647
+ document.body.removeChild(link);
185648
+ URL.revokeObjectURL(blobUrl);
185649
+ return true;
185650
+ }
185651
+ catch (error) {
185652
+ console.error('Failed to download report:', error);
185653
+ return false;
185654
+ }
185655
+ }, [fetchReportBlob]);
185634
185656
  const clearPollingTimeout = useCallback(() => {
185635
185657
  if (pollTimeoutRef.current) {
185636
185658
  clearTimeout(pollTimeoutRef.current);
@@ -185699,14 +185721,13 @@ const useReports = ({ tenantKey, selectedMentorId, usergroupIds, }) => {
185699
185721
  onCompletionRef.current(url);
185700
185722
  }
185701
185723
  if (autoDownloadRef.current) {
185702
- const link = document.createElement('a');
185703
- link.href = url;
185704
- link.target = '_blank';
185705
- link.rel = 'noopener noreferrer';
185706
- document.body.appendChild(link);
185707
- link.click();
185708
- document.body.removeChild(link);
185709
- toast.success('Report downloaded successfully');
185724
+ const success = await downloadReportFromUrl(url);
185725
+ if (success) {
185726
+ toast.success('Report downloaded successfully');
185727
+ }
185728
+ else {
185729
+ toast.error('Failed to download report');
185730
+ }
185710
185731
  }
185711
185732
  shouldContinue = false;
185712
185733
  stopPolling();
@@ -185748,7 +185769,15 @@ const useReports = ({ tenantKey, selectedMentorId, usergroupIds, }) => {
185748
185769
  void ((_a = pollReportStatusRef.current) === null || _a === void 0 ? void 0 : _a.call(pollReportStatusRef));
185749
185770
  }, nextInterval);
185750
185771
  }
185751
- }, [activeReportName, clearPollingTimeout, fetchReportDetail, pollArgs, refetch, stopPolling]);
185772
+ }, [
185773
+ activeReportName,
185774
+ clearPollingTimeout,
185775
+ downloadReportFromUrl,
185776
+ fetchReportDetail,
185777
+ pollArgs,
185778
+ refetch,
185779
+ stopPolling,
185780
+ ]);
185752
185781
  useEffect(() => {
185753
185782
  pollReportStatusRef.current = pollReportStatus;
185754
185783
  }, [pollReportStatus]);
@@ -185805,14 +185834,13 @@ const useReports = ({ tenantKey, selectedMentorId, usergroupIds, }) => {
185805
185834
  onCompletion(url);
185806
185835
  }
185807
185836
  if (autoDownload) {
185808
- const link = document.createElement('a');
185809
- link.href = url;
185810
- link.target = '_blank';
185811
- link.rel = 'noopener noreferrer';
185812
- document.body.appendChild(link);
185813
- link.click();
185814
- document.body.removeChild(link);
185815
- toast.success('Report downloaded successfully');
185837
+ const success = await downloadReportFromUrl(url);
185838
+ if (success) {
185839
+ toast.success('Report downloaded successfully');
185840
+ }
185841
+ else {
185842
+ toast.error('Failed to download report');
185843
+ }
185816
185844
  }
185817
185845
  stopPolling();
185818
185846
  refetch();
@@ -185834,6 +185862,7 @@ const useReports = ({ tenantKey, selectedMentorId, usergroupIds, }) => {
185834
185862
  }, [
185835
185863
  activeReportName,
185836
185864
  createReport,
185865
+ downloadReportFromUrl,
185837
185866
  selectedMentorId,
185838
185867
  usergroupIds,
185839
185868
  isGenerating,
@@ -185880,6 +185909,30 @@ function AnalyticsReports({ tenantKey, selectedMentorId, disabledReports = [], u
185880
185909
  selectedMentorId,
185881
185910
  usergroupIds,
185882
185911
  });
185912
+ const [fetchReportBlob] = useLazyGetDownloadReportFromURLQuery();
185913
+ const fetchAuthenticatedContent = useCallback(async (url) => {
185914
+ const result = await fetchReportBlob({ url }).unwrap();
185915
+ const blob = result instanceof Blob ? result : new Blob([result]);
185916
+ return blob.text();
185917
+ }, [fetchReportBlob]);
185918
+ const downloadAuthenticatedReport = useCallback(async (url, filename = 'report.csv') => {
185919
+ try {
185920
+ const result = await fetchReportBlob({ url }).unwrap();
185921
+ const blob = result instanceof Blob ? result : new Blob([result]);
185922
+ const blobUrl = URL.createObjectURL(blob);
185923
+ const link = document.createElement('a');
185924
+ link.href = blobUrl;
185925
+ link.download = filename;
185926
+ document.body.appendChild(link);
185927
+ link.click();
185928
+ document.body.removeChild(link);
185929
+ URL.revokeObjectURL(blobUrl);
185930
+ }
185931
+ catch (error) {
185932
+ console.error('Failed to download report:', error);
185933
+ throw error;
185934
+ }
185935
+ }, [fetchReportBlob]);
185883
185936
  const [csvEditorData, setCsvEditorData] = useState({
185884
185937
  isOpen: false,
185885
185938
  headers: [],
@@ -185926,11 +185979,7 @@ function AnalyticsReports({ tenantKey, selectedMentorId, disabledReports = [], u
185926
185979
  const handleReportCompletion = useCallback(async (url) => {
185927
185980
  const TIMEOUT_MS = 30000;
185928
185981
  const fetchAndParse = async () => {
185929
- const response = await fetch(url);
185930
- if (!response.ok) {
185931
- throw new Error('Failed to fetch report');
185932
- }
185933
- const csvText = await response.text();
185982
+ const csvText = await fetchAuthenticatedContent(url);
185934
185983
  return parseCSV(csvText);
185935
185984
  };
185936
185985
  const timeoutPromise = new Promise((_, reject) => {
@@ -185950,16 +185999,16 @@ function AnalyticsReports({ tenantKey, selectedMentorId, disabledReports = [], u
185950
185999
  catch (error) {
185951
186000
  console.error('Failed to load CSV for editing:', error);
185952
186001
  toast.error('Failed to load report for editing. Opening download instead.');
185953
- // Fallback to direct download
185954
- const link = document.createElement('a');
185955
- link.href = url;
185956
- link.target = '_blank';
185957
- link.rel = 'noopener noreferrer';
185958
- document.body.appendChild(link);
185959
- link.click();
185960
- document.body.removeChild(link);
186002
+ // Fallback to authenticated download
186003
+ try {
186004
+ await downloadAuthenticatedReport(url);
186005
+ }
186006
+ catch (downloadError) {
186007
+ console.error('Failed to download report:', downloadError);
186008
+ toast.error('Failed to download report');
186009
+ }
185961
186010
  }
185962
- }, [parseCSV]);
186011
+ }, [parseCSV, fetchAuthenticatedContent, downloadAuthenticatedReport]);
185963
186012
  const handleCsvEditorSave = useCallback((data) => {
185964
186013
  // Convert data back to CSV and trigger download
185965
186014
  const csvContent = [
@@ -185989,15 +186038,15 @@ function AnalyticsReports({ tenantKey, selectedMentorId, disabledReports = [], u
185989
186038
  const handleCsvEditorCancel = useCallback(() => {
185990
186039
  setCsvEditorData({ isOpen: false, headers: [], rows: [], reportUrl: null });
185991
186040
  }, []);
185992
- const handleAutoDownload = useCallback((url) => {
185993
- const link = document.createElement('a');
185994
- link.href = url;
185995
- link.target = '_blank';
185996
- link.rel = 'noopener noreferrer';
185997
- document.body.appendChild(link);
185998
- link.click();
185999
- document.body.removeChild(link);
186000
- }, []);
186041
+ const handleAutoDownload = useCallback(async (url) => {
186042
+ try {
186043
+ await downloadAuthenticatedReport(url);
186044
+ }
186045
+ catch (error) {
186046
+ console.error('Failed to auto-download report:', error);
186047
+ toast.error('Failed to download report');
186048
+ }
186049
+ }, [downloadAuthenticatedReport]);
186001
186050
  // Check if a report is one of the recommendation reports to combine
186002
186051
  const isRecommendationReport = useCallback((reportDisplayName) => {
186003
186052
  return combinedRecommendationReports.some((name) => reportDisplayName.toLowerCase() === name.toLowerCase());
@@ -186050,12 +186099,13 @@ function AnalyticsReports({ tenantKey, selectedMentorId, disabledReports = [], u
186050
186099
  if (cancelCombiningRef.current) {
186051
186100
  throw new Error('Cancelled');
186052
186101
  }
186053
- const response = await fetch(url);
186054
- if (!response.ok) {
186102
+ try {
186103
+ const csvText = await fetchAuthenticatedContent(url);
186104
+ csvDataArray.push(parseCSV(csvText));
186105
+ }
186106
+ catch (error) {
186055
186107
  throw new Error(`Failed to fetch ${name}`);
186056
186108
  }
186057
- const csvText = await response.text();
186058
- csvDataArray.push(parseCSV(csvText));
186059
186109
  }
186060
186110
  // Check for cancellation before combining
186061
186111
  if (cancelCombiningRef.current) {
@@ -186090,6 +186140,7 @@ function AnalyticsReports({ tenantKey, selectedMentorId, disabledReports = [], u
186090
186140
  isRecommendationReport,
186091
186141
  initializeReportDownload,
186092
186142
  parseCSV,
186143
+ fetchAuthenticatedContent,
186093
186144
  ]);
186094
186145
  const handleReportClick = useCallback((report) => {
186095
186146
  var _a, _b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -49,8 +49,8 @@
49
49
  "author": "iblai",
50
50
  "license": "ISC",
51
51
  "dependencies": {
52
- "@iblai/data-layer": "1.1.7",
53
- "@iblai/web-containers": "1.1.14",
52
+ "@iblai/data-layer": "1.1.8",
53
+ "@iblai/web-containers": "1.1.15",
54
54
  "@iblai/web-utils": "1.1.9"
55
55
  },
56
56
  "peerDependencies": {