@serhii.olishevskyi/docs 1.1.3 → 1.1.4

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,7 +1,7 @@
1
1
  import {
2
2
  UserPopoverComponent,
3
3
  UserPopoverComponent_default
4
- } from "./chunk-BAFKGCJJ.mjs";
4
+ } from "./chunk-ERXHWCQ5.mjs";
5
5
  export {
6
6
  UserPopoverComponent,
7
7
  UserPopoverComponent_default as default
@@ -200,6 +200,84 @@ var getDeviceId = () => {
200
200
  };
201
201
 
202
202
  // src/services/session.service.ts
203
+ var fetchCompanySessions = async (supabase, companyId, page = 1, pageSize = 5, searchQuery = "") => {
204
+ const currentDeviceId = getDeviceId();
205
+ let query = supabase.from("memberships").select(`
206
+ user_id,
207
+ role,
208
+ employee_post,
209
+ profiles (
210
+ id,
211
+ display_name,
212
+ first_name,
213
+ last_name
214
+ )
215
+ `, { count: "exact" }).eq("company_id", companyId);
216
+ if (searchQuery.trim()) {
217
+ const q = `%${searchQuery.trim()}%`;
218
+ const { data: matchedProfiles } = await supabase.from("profiles").select("id").or(`display_name.ilike.${q},first_name.ilike.${q},last_name.ilike.${q}`);
219
+ const matchedUserIds = (matchedProfiles || []).map((p) => p.id);
220
+ if (matchedUserIds.length > 0) {
221
+ query = query.or(`employee_post.ilike.${q},user_id.in.(${matchedUserIds.join(",")})`);
222
+ } else {
223
+ query = query.ilike("employee_post", q);
224
+ }
225
+ }
226
+ const { data: members, error: membersError } = await query;
227
+ if (membersError) throw membersError;
228
+ if (!members || members.length === 0) {
229
+ return { data: [], totalCount: 0 };
230
+ }
231
+ const userMap = /* @__PURE__ */ new Map();
232
+ members.forEach((m) => {
233
+ const profile = Array.isArray(m.profiles) ? m.profiles[0] : m.profiles;
234
+ const name = profile?.display_name || [profile?.first_name, profile?.last_name].filter(Boolean).join(" ") || "\u041D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447";
235
+ userMap.set(m.user_id, {
236
+ name,
237
+ post: m.employee_post || "\u041F\u043E\u0441\u0430\u0434\u0430 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u043D\u0430",
238
+ role: m.role || "user"
239
+ });
240
+ });
241
+ const userIds = Array.from(userMap.keys());
242
+ const { data: sessions, error: sessionsError } = await supabase.from("user_sessions").select("*").in("user_id", userIds).order("last_seen_at", { ascending: false });
243
+ if (sessionsError) throw sessionsError;
244
+ const groupsMap = /* @__PURE__ */ new Map();
245
+ (sessions || []).forEach((s) => {
246
+ const userInfo = userMap.get(s.user_id);
247
+ if (!userInfo) return;
248
+ if (!groupsMap.has(s.user_id)) {
249
+ groupsMap.set(s.user_id, {
250
+ userId: s.user_id,
251
+ userName: userInfo.name,
252
+ employeePost: userInfo.post,
253
+ role: userInfo.role,
254
+ sessions: []
255
+ });
256
+ }
257
+ groupsMap.get(s.user_id).sessions.push({
258
+ id: s.id,
259
+ user_id: s.user_id,
260
+ user_agent: s.user_agent,
261
+ ip_address: s.ip_address,
262
+ created_at: s.created_at,
263
+ last_seen_at: s.last_seen_at,
264
+ device_id: s.device_id,
265
+ is_current: s.device_id === currentDeviceId,
266
+ user_name: userInfo.name,
267
+ employee_post: userInfo.post,
268
+ role: userInfo.role,
269
+ url: s.url
270
+ });
271
+ });
272
+ const allGroupedWithSessions = Array.from(groupsMap.values()).filter((group) => group.sessions.length > 0).sort((a, b) => a.userName.localeCompare(b.userName, "uk"));
273
+ const totalCount = allGroupedWithSessions.length;
274
+ const from = (page - 1) * pageSize;
275
+ const paginatedData = allGroupedWithSessions.slice(from, from + pageSize);
276
+ return {
277
+ data: paginatedData,
278
+ totalCount
279
+ };
280
+ };
203
281
  var trackUserSession = async (supabase, userId) => {
204
282
  const deviceId = getDeviceId();
205
283
  let ip = "";
@@ -222,6 +300,17 @@ var trackUserSession = async (supabase, userId) => {
222
300
  { onConflict: "user_id, device_id" }
223
301
  );
224
302
  };
303
+ var fetchUserSessions = async (supabase) => {
304
+ const currentDeviceId = getDeviceId();
305
+ const { data: { user } } = await supabase.auth.getUser();
306
+ if (!user) return [];
307
+ const { data, error } = await supabase.from("user_sessions").select("*").eq("user_id", user.id).order("last_seen_at", { ascending: false });
308
+ if (error) throw error;
309
+ return (data || []).map((s) => ({
310
+ ...s,
311
+ is_current: s.device_id === currentDeviceId
312
+ }));
313
+ };
225
314
  var isCurrentSessionValid = async (supabase, userId) => {
226
315
  try {
227
316
  const currentDeviceId = getDeviceId();
@@ -237,11 +326,42 @@ var isCurrentSessionValid = async (supabase, userId) => {
237
326
  return true;
238
327
  }
239
328
  };
329
+ var groupSessionsByUser = (sessions) => {
330
+ const groupsMap = /* @__PURE__ */ new Map();
331
+ sessions.forEach((session) => {
332
+ const userId = session.user_id;
333
+ if (!groupsMap.has(userId)) {
334
+ groupsMap.set(userId, {
335
+ userId,
336
+ userName: session.user_name || "\u041D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447",
337
+ employeePost: session.employee_post || "\u041F\u043E\u0441\u0430\u0434\u0430 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u043D\u0430",
338
+ role: session.role || "user",
339
+ sessions: []
340
+ });
341
+ }
342
+ groupsMap.get(userId).sessions.push(session);
343
+ });
344
+ return Array.from(groupsMap.values()).sort(
345
+ (a, b) => a.userName.localeCompare(b.userName, "uk")
346
+ );
347
+ };
240
348
  var getUserLastSeenAt = async (supabase, userId) => {
241
349
  const { data, error } = await supabase.from("user_sessions").select("last_seen_at").eq("user_id", userId).order("last_seen_at", { ascending: false }).limit(1).maybeSingle();
242
350
  if (error || !data) return null;
243
351
  return data.last_seen_at;
244
352
  };
353
+ var getUsersLastSeenMap = async (supabase, userIds) => {
354
+ if (!userIds.length) return {};
355
+ const { data, error } = await supabase.from("user_sessions").select("user_id, last_seen_at").in("user_id", userIds).order("last_seen_at", { ascending: false });
356
+ if (error || !data) return {};
357
+ const resultMap = {};
358
+ for (const session of data) {
359
+ if (!resultMap[session.user_id]) {
360
+ resultMap[session.user_id] = session.last_seen_at;
361
+ }
362
+ }
363
+ return resultMap;
364
+ };
245
365
 
246
366
  // src/context/AuthProvider.tsx
247
367
  import { jsx as jsx3 } from "react/jsx-runtime";
@@ -2413,6 +2533,13 @@ var UserPopoverComponent = ({
2413
2533
  var UserPopoverComponent_default = UserPopoverComponent;
2414
2534
 
2415
2535
  export {
2536
+ fetchCompanySessions,
2537
+ trackUserSession,
2538
+ fetchUserSessions,
2539
+ isCurrentSessionValid,
2540
+ groupSessionsByUser,
2541
+ getUserLastSeenAt,
2542
+ getUsersLastSeenMap,
2416
2543
  SupabaseProvider,
2417
2544
  useSupabase,
2418
2545
  AuthProvider,
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React$1 from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
3
  import { TextFieldProps, IconButtonProps, TooltipProps, SxProps, Theme } from '@mui/material';
4
+ import * as _supabase_supabase_js from '@supabase/supabase-js';
4
5
  import { SupabaseClient, User, Session } from '@supabase/supabase-js';
5
6
 
6
7
  interface UrlValue {
@@ -488,4 +489,42 @@ declare const Providers: {
488
489
  readonly AIProvider: React$1.FC<AIProviderProps>;
489
490
  };
490
491
 
491
- export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type MyAvatarComponentProps as MyAvatarProps, type NumberInputComponentProps as NumberInputProps, Providers, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
492
+ interface UserSessionItem {
493
+ id: string;
494
+ user_id: string;
495
+ user_agent: string | null;
496
+ ip_address: string | null;
497
+ created_at: string;
498
+ last_seen_at: string;
499
+ device_id: string;
500
+ url?: string | null;
501
+ is_current?: boolean;
502
+ user_name?: string;
503
+ employee_post?: string;
504
+ role?: string;
505
+ }
506
+ interface UserGroupedSessions {
507
+ userId: string;
508
+ userName: string;
509
+ employeePost: string;
510
+ role: string;
511
+ sessions: UserSessionItem[];
512
+ }
513
+ interface FetchCompanySessionsResponse {
514
+ data: UserGroupedSessions[];
515
+ totalCount: number;
516
+ }
517
+
518
+ declare const Services: {
519
+ readonly SessionService: {
520
+ readonly fetchCompanySessions: (supabase: _supabase_supabase_js.SupabaseClient, companyId: string, page?: number, pageSize?: number, searchQuery?: string) => Promise<FetchCompanySessionsResponse>;
521
+ readonly trackUserSession: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<void>;
522
+ readonly fetchUserSessions: (supabase: _supabase_supabase_js.SupabaseClient) => Promise<UserSessionItem[]>;
523
+ readonly isCurrentSessionValid: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<boolean>;
524
+ readonly groupSessionsByUser: (sessions: UserSessionItem[]) => UserGroupedSessions[];
525
+ readonly getUserLastSeenAt: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<string | null>;
526
+ readonly getUsersLastSeenMap: (supabase: _supabase_supabase_js.SupabaseClient, userIds: string[]) => Promise<Record<string, string>>;
527
+ };
528
+ };
529
+
530
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type MyAvatarComponentProps as MyAvatarProps, type NumberInputComponentProps as NumberInputProps, Providers, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserGroupedSessions, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, type UserSessionItem, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React$1 from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
3
  import { TextFieldProps, IconButtonProps, TooltipProps, SxProps, Theme } from '@mui/material';
4
+ import * as _supabase_supabase_js from '@supabase/supabase-js';
4
5
  import { SupabaseClient, User, Session } from '@supabase/supabase-js';
5
6
 
6
7
  interface UrlValue {
@@ -488,4 +489,42 @@ declare const Providers: {
488
489
  readonly AIProvider: React$1.FC<AIProviderProps>;
489
490
  };
490
491
 
491
- export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type MyAvatarComponentProps as MyAvatarProps, type NumberInputComponentProps as NumberInputProps, Providers, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
492
+ interface UserSessionItem {
493
+ id: string;
494
+ user_id: string;
495
+ user_agent: string | null;
496
+ ip_address: string | null;
497
+ created_at: string;
498
+ last_seen_at: string;
499
+ device_id: string;
500
+ url?: string | null;
501
+ is_current?: boolean;
502
+ user_name?: string;
503
+ employee_post?: string;
504
+ role?: string;
505
+ }
506
+ interface UserGroupedSessions {
507
+ userId: string;
508
+ userName: string;
509
+ employeePost: string;
510
+ role: string;
511
+ sessions: UserSessionItem[];
512
+ }
513
+ interface FetchCompanySessionsResponse {
514
+ data: UserGroupedSessions[];
515
+ totalCount: number;
516
+ }
517
+
518
+ declare const Services: {
519
+ readonly SessionService: {
520
+ readonly fetchCompanySessions: (supabase: _supabase_supabase_js.SupabaseClient, companyId: string, page?: number, pageSize?: number, searchQuery?: string) => Promise<FetchCompanySessionsResponse>;
521
+ readonly trackUserSession: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<void>;
522
+ readonly fetchUserSessions: (supabase: _supabase_supabase_js.SupabaseClient) => Promise<UserSessionItem[]>;
523
+ readonly isCurrentSessionValid: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<boolean>;
524
+ readonly groupSessionsByUser: (sessions: UserSessionItem[]) => UserGroupedSessions[];
525
+ readonly getUserLastSeenAt: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<string | null>;
526
+ readonly getUsersLastSeenMap: (supabase: _supabase_supabase_js.SupabaseClient, userIds: string[]) => Promise<Record<string, string>>;
527
+ };
528
+ };
529
+
530
+ export { type AIConfig, type AIGenerateOptions, AIProvider, type AIProviderProps, type AIProviderType, AppSettingsProvider, AuthProvider, BreadcrumbProvider, type ConfirmOptions, type ConfirmResult, ConfirmationProvider, type ConfirmationProviderProps, type CopyClipboardButtonComponentProps as CopyClipboardButtonProps, type DialogSeverity, type DisplayState, type EmailInputComponentProps as EmailInputProps, type Language, LocalizationProvider, type MyAvatarComponentProps as MyAvatarProps, type NumberInputComponentProps as NumberInputProps, Providers, Services, type SupabaseConfig, SupabaseProvider, type SupabaseProviderProps, type TextFieldComponentProps as TextFieldProps, type ThemeColors, type ThemeConfig, type ThemeMode, ThemeProvider, ToastProvider, type ToastType, type TranslatableTooltipComponentProps as TranslatableTooltipProps, TranslateProvider, TranslateText, TranslationEditProvider, type TypographyConfig, UI, type UrlComponentProps as UrlProps, type UserAvatarComponentProps as UserAvatarProps, type UserGroupedSessions, type UserPopoverComponentProps as UserPopoverProps, type UserRoleChipComponentProps as UserRoleChipProps, type UserSessionItem, UserSettingsProvider, type UserStatusChipComponentProps as UserStatusChipProps, useAI, useAppSettings, useAppTheme, useAuth, useBreadcrumb, useConfirmation, useLocalization, useSupabase, useToast, useTranslationEdit, useTranslationNamespace, useUserSettings };
package/dist/index.js CHANGED
@@ -169,11 +169,89 @@ var init_deviceId = __esm({
169
169
  });
170
170
 
171
171
  // src/services/session.service.ts
172
- var trackUserSession, isCurrentSessionValid, getUserLastSeenAt;
172
+ var fetchCompanySessions, trackUserSession, fetchUserSessions, isCurrentSessionValid, groupSessionsByUser, getUserLastSeenAt, getUsersLastSeenMap;
173
173
  var init_session_service = __esm({
174
174
  "src/services/session.service.ts"() {
175
175
  "use strict";
176
176
  init_deviceId();
177
+ fetchCompanySessions = async (supabase, companyId, page = 1, pageSize = 5, searchQuery = "") => {
178
+ const currentDeviceId = getDeviceId();
179
+ let query = supabase.from("memberships").select(`
180
+ user_id,
181
+ role,
182
+ employee_post,
183
+ profiles (
184
+ id,
185
+ display_name,
186
+ first_name,
187
+ last_name
188
+ )
189
+ `, { count: "exact" }).eq("company_id", companyId);
190
+ if (searchQuery.trim()) {
191
+ const q = `%${searchQuery.trim()}%`;
192
+ const { data: matchedProfiles } = await supabase.from("profiles").select("id").or(`display_name.ilike.${q},first_name.ilike.${q},last_name.ilike.${q}`);
193
+ const matchedUserIds = (matchedProfiles || []).map((p) => p.id);
194
+ if (matchedUserIds.length > 0) {
195
+ query = query.or(`employee_post.ilike.${q},user_id.in.(${matchedUserIds.join(",")})`);
196
+ } else {
197
+ query = query.ilike("employee_post", q);
198
+ }
199
+ }
200
+ const { data: members, error: membersError } = await query;
201
+ if (membersError) throw membersError;
202
+ if (!members || members.length === 0) {
203
+ return { data: [], totalCount: 0 };
204
+ }
205
+ const userMap = /* @__PURE__ */ new Map();
206
+ members.forEach((m) => {
207
+ const profile = Array.isArray(m.profiles) ? m.profiles[0] : m.profiles;
208
+ const name = profile?.display_name || [profile?.first_name, profile?.last_name].filter(Boolean).join(" ") || "\u041D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447";
209
+ userMap.set(m.user_id, {
210
+ name,
211
+ post: m.employee_post || "\u041F\u043E\u0441\u0430\u0434\u0430 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u043D\u0430",
212
+ role: m.role || "user"
213
+ });
214
+ });
215
+ const userIds = Array.from(userMap.keys());
216
+ const { data: sessions, error: sessionsError } = await supabase.from("user_sessions").select("*").in("user_id", userIds).order("last_seen_at", { ascending: false });
217
+ if (sessionsError) throw sessionsError;
218
+ const groupsMap = /* @__PURE__ */ new Map();
219
+ (sessions || []).forEach((s) => {
220
+ const userInfo = userMap.get(s.user_id);
221
+ if (!userInfo) return;
222
+ if (!groupsMap.has(s.user_id)) {
223
+ groupsMap.set(s.user_id, {
224
+ userId: s.user_id,
225
+ userName: userInfo.name,
226
+ employeePost: userInfo.post,
227
+ role: userInfo.role,
228
+ sessions: []
229
+ });
230
+ }
231
+ groupsMap.get(s.user_id).sessions.push({
232
+ id: s.id,
233
+ user_id: s.user_id,
234
+ user_agent: s.user_agent,
235
+ ip_address: s.ip_address,
236
+ created_at: s.created_at,
237
+ last_seen_at: s.last_seen_at,
238
+ device_id: s.device_id,
239
+ is_current: s.device_id === currentDeviceId,
240
+ user_name: userInfo.name,
241
+ employee_post: userInfo.post,
242
+ role: userInfo.role,
243
+ url: s.url
244
+ });
245
+ });
246
+ const allGroupedWithSessions = Array.from(groupsMap.values()).filter((group) => group.sessions.length > 0).sort((a, b) => a.userName.localeCompare(b.userName, "uk"));
247
+ const totalCount = allGroupedWithSessions.length;
248
+ const from = (page - 1) * pageSize;
249
+ const paginatedData = allGroupedWithSessions.slice(from, from + pageSize);
250
+ return {
251
+ data: paginatedData,
252
+ totalCount
253
+ };
254
+ };
177
255
  trackUserSession = async (supabase, userId) => {
178
256
  const deviceId = getDeviceId();
179
257
  let ip = "";
@@ -196,6 +274,17 @@ var init_session_service = __esm({
196
274
  { onConflict: "user_id, device_id" }
197
275
  );
198
276
  };
277
+ fetchUserSessions = async (supabase) => {
278
+ const currentDeviceId = getDeviceId();
279
+ const { data: { user } } = await supabase.auth.getUser();
280
+ if (!user) return [];
281
+ const { data, error } = await supabase.from("user_sessions").select("*").eq("user_id", user.id).order("last_seen_at", { ascending: false });
282
+ if (error) throw error;
283
+ return (data || []).map((s) => ({
284
+ ...s,
285
+ is_current: s.device_id === currentDeviceId
286
+ }));
287
+ };
199
288
  isCurrentSessionValid = async (supabase, userId) => {
200
289
  try {
201
290
  const currentDeviceId = getDeviceId();
@@ -211,11 +300,42 @@ var init_session_service = __esm({
211
300
  return true;
212
301
  }
213
302
  };
303
+ groupSessionsByUser = (sessions) => {
304
+ const groupsMap = /* @__PURE__ */ new Map();
305
+ sessions.forEach((session) => {
306
+ const userId = session.user_id;
307
+ if (!groupsMap.has(userId)) {
308
+ groupsMap.set(userId, {
309
+ userId,
310
+ userName: session.user_name || "\u041D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447",
311
+ employeePost: session.employee_post || "\u041F\u043E\u0441\u0430\u0434\u0430 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u043D\u0430",
312
+ role: session.role || "user",
313
+ sessions: []
314
+ });
315
+ }
316
+ groupsMap.get(userId).sessions.push(session);
317
+ });
318
+ return Array.from(groupsMap.values()).sort(
319
+ (a, b) => a.userName.localeCompare(b.userName, "uk")
320
+ );
321
+ };
214
322
  getUserLastSeenAt = async (supabase, userId) => {
215
323
  const { data, error } = await supabase.from("user_sessions").select("last_seen_at").eq("user_id", userId).order("last_seen_at", { ascending: false }).limit(1).maybeSingle();
216
324
  if (error || !data) return null;
217
325
  return data.last_seen_at;
218
326
  };
327
+ getUsersLastSeenMap = async (supabase, userIds) => {
328
+ if (!userIds.length) return {};
329
+ const { data, error } = await supabase.from("user_sessions").select("user_id, last_seen_at").in("user_id", userIds).order("last_seen_at", { ascending: false });
330
+ if (error || !data) return {};
331
+ const resultMap = {};
332
+ for (const session of data) {
333
+ if (!resultMap[session.user_id]) {
334
+ resultMap[session.user_id] = session.last_seen_at;
335
+ }
336
+ }
337
+ return resultMap;
338
+ };
219
339
  }
220
340
  });
221
341
 
@@ -2617,6 +2737,7 @@ __export(index_exports, {
2617
2737
  ConfirmationProvider: () => ConfirmationProvider,
2618
2738
  LocalizationProvider: () => LocalizationProvider,
2619
2739
  Providers: () => Providers,
2740
+ Services: () => Services,
2620
2741
  SupabaseProvider: () => SupabaseProvider,
2621
2742
  ThemeProvider: () => ThemeProvider,
2622
2743
  ToastProvider: () => ToastProvider,
@@ -3875,6 +3996,20 @@ var UI = {
3875
3996
  // src/index.ts
3876
3997
  init_context();
3877
3998
  init_context();
3999
+
4000
+ // src/services/index.ts
4001
+ init_session_service();
4002
+ var Services = {
4003
+ SessionService: {
4004
+ fetchCompanySessions,
4005
+ trackUserSession,
4006
+ fetchUserSessions,
4007
+ isCurrentSessionValid,
4008
+ groupSessionsByUser,
4009
+ getUserLastSeenAt,
4010
+ getUsersLastSeenMap
4011
+ }
4012
+ };
3878
4013
  // Annotate the CommonJS export names for ESM import in node:
3879
4014
  0 && (module.exports = {
3880
4015
  AIProvider,
@@ -3884,6 +4019,7 @@ init_context();
3884
4019
  ConfirmationProvider,
3885
4020
  LocalizationProvider,
3886
4021
  Providers,
4022
+ Services,
3887
4023
  SupabaseProvider,
3888
4024
  ThemeProvider,
3889
4025
  ToastProvider,
package/dist/index.mjs CHANGED
@@ -19,6 +19,13 @@ import {
19
19
  UserRoleChipComponent,
20
20
  UserSettingsProvider,
21
21
  UserStatusChipComponent,
22
+ fetchCompanySessions,
23
+ fetchUserSessions,
24
+ getUserLastSeenAt,
25
+ getUsersLastSeenMap,
26
+ groupSessionsByUser,
27
+ isCurrentSessionValid,
28
+ trackUserSession,
22
29
  translationService,
23
30
  useAI,
24
31
  useAppSettings,
@@ -32,7 +39,7 @@ import {
32
39
  useTranslationEdit,
33
40
  useTranslationNamespace,
34
41
  useUserSettings
35
- } from "./chunk-BAFKGCJJ.mjs";
42
+ } from "./chunk-ERXHWCQ5.mjs";
36
43
 
37
44
  // src/UI/components/inputs/TextFieldComponent.tsx
38
45
  import { useState } from "react";
@@ -730,7 +737,7 @@ var EmailInputComponent = ({
730
737
  import { useState as useState5, useEffect as useEffect2, lazy, Suspense } from "react";
731
738
  import { Avatar, Tooltip as Tooltip3, Typography as Typography2 } from "@mui/material";
732
739
  import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
733
- var LazyUserPopover = lazy(() => import("./UserPopoverComponent-64KAMDBO.mjs"));
740
+ var LazyUserPopover = lazy(() => import("./UserPopoverComponent-ZG4UFBGJ.mjs"));
734
741
  var getInitials = (name) => {
735
742
  if (!name) return "?";
736
743
  const parts = name.trim().split(" ");
@@ -1266,6 +1273,19 @@ var UI = {
1266
1273
  PublicLanguageSelector
1267
1274
  }
1268
1275
  };
1276
+
1277
+ // src/services/index.ts
1278
+ var Services = {
1279
+ SessionService: {
1280
+ fetchCompanySessions,
1281
+ trackUserSession,
1282
+ fetchUserSessions,
1283
+ isCurrentSessionValid,
1284
+ groupSessionsByUser,
1285
+ getUserLastSeenAt,
1286
+ getUsersLastSeenMap
1287
+ }
1288
+ };
1269
1289
  export {
1270
1290
  AIProvider,
1271
1291
  AppSettingsProvider,
@@ -1274,6 +1294,7 @@ export {
1274
1294
  ConfirmationProvider,
1275
1295
  LocalizationProvider,
1276
1296
  Providers,
1297
+ Services,
1277
1298
  SupabaseProvider,
1278
1299
  ThemeProvider,
1279
1300
  ToastProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serhii.olishevskyi/docs",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "DOCS.UA react packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",