@serhii.olishevskyi/docs 1.1.3 → 1.1.5
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/dist/{UserPopoverComponent-64KAMDBO.mjs → UserPopoverComponent-5F4S3X4F.mjs} +1 -1
- package/dist/{chunk-BAFKGCJJ.mjs → chunk-UA57YNRJ.mjs} +141 -0
- package/dist/index.d.mts +41 -1
- package/dist/index.d.ts +41 -1
- package/dist/index.js +151 -1
- package/dist/index.mjs +25 -2
- package/package.json +1 -1
|
@@ -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,30 @@ var trackUserSession = async (supabase, userId) => {
|
|
|
222
300
|
{ onConflict: "user_id, device_id" }
|
|
223
301
|
);
|
|
224
302
|
};
|
|
303
|
+
var trackUserSessionByRoute = async (supabase, userId, currentUrl) => {
|
|
304
|
+
const deviceId = getDeviceId();
|
|
305
|
+
await supabase.from("user_sessions").upsert(
|
|
306
|
+
{
|
|
307
|
+
user_id: userId,
|
|
308
|
+
device_id: deviceId,
|
|
309
|
+
user_agent: navigator.userAgent,
|
|
310
|
+
url: currentUrl || window.location.href,
|
|
311
|
+
last_seen_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
312
|
+
},
|
|
313
|
+
{ onConflict: "user_id, device_id" }
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
var fetchUserSessions = async (supabase) => {
|
|
317
|
+
const currentDeviceId = getDeviceId();
|
|
318
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
319
|
+
if (!user) return [];
|
|
320
|
+
const { data, error } = await supabase.from("user_sessions").select("*").eq("user_id", user.id).order("last_seen_at", { ascending: false });
|
|
321
|
+
if (error) throw error;
|
|
322
|
+
return (data || []).map((s) => ({
|
|
323
|
+
...s,
|
|
324
|
+
is_current: s.device_id === currentDeviceId
|
|
325
|
+
}));
|
|
326
|
+
};
|
|
225
327
|
var isCurrentSessionValid = async (supabase, userId) => {
|
|
226
328
|
try {
|
|
227
329
|
const currentDeviceId = getDeviceId();
|
|
@@ -237,11 +339,42 @@ var isCurrentSessionValid = async (supabase, userId) => {
|
|
|
237
339
|
return true;
|
|
238
340
|
}
|
|
239
341
|
};
|
|
342
|
+
var groupSessionsByUser = (sessions) => {
|
|
343
|
+
const groupsMap = /* @__PURE__ */ new Map();
|
|
344
|
+
sessions.forEach((session) => {
|
|
345
|
+
const userId = session.user_id;
|
|
346
|
+
if (!groupsMap.has(userId)) {
|
|
347
|
+
groupsMap.set(userId, {
|
|
348
|
+
userId,
|
|
349
|
+
userName: session.user_name || "\u041D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447",
|
|
350
|
+
employeePost: session.employee_post || "\u041F\u043E\u0441\u0430\u0434\u0430 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u043D\u0430",
|
|
351
|
+
role: session.role || "user",
|
|
352
|
+
sessions: []
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
groupsMap.get(userId).sessions.push(session);
|
|
356
|
+
});
|
|
357
|
+
return Array.from(groupsMap.values()).sort(
|
|
358
|
+
(a, b) => a.userName.localeCompare(b.userName, "uk")
|
|
359
|
+
);
|
|
360
|
+
};
|
|
240
361
|
var getUserLastSeenAt = async (supabase, userId) => {
|
|
241
362
|
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
363
|
if (error || !data) return null;
|
|
243
364
|
return data.last_seen_at;
|
|
244
365
|
};
|
|
366
|
+
var getUsersLastSeenMap = async (supabase, userIds) => {
|
|
367
|
+
if (!userIds.length) return {};
|
|
368
|
+
const { data, error } = await supabase.from("user_sessions").select("user_id, last_seen_at").in("user_id", userIds).order("last_seen_at", { ascending: false });
|
|
369
|
+
if (error || !data) return {};
|
|
370
|
+
const resultMap = {};
|
|
371
|
+
for (const session of data) {
|
|
372
|
+
if (!resultMap[session.user_id]) {
|
|
373
|
+
resultMap[session.user_id] = session.last_seen_at;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return resultMap;
|
|
377
|
+
};
|
|
245
378
|
|
|
246
379
|
// src/context/AuthProvider.tsx
|
|
247
380
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
@@ -2413,6 +2546,14 @@ var UserPopoverComponent = ({
|
|
|
2413
2546
|
var UserPopoverComponent_default = UserPopoverComponent;
|
|
2414
2547
|
|
|
2415
2548
|
export {
|
|
2549
|
+
fetchCompanySessions,
|
|
2550
|
+
trackUserSession,
|
|
2551
|
+
trackUserSessionByRoute,
|
|
2552
|
+
fetchUserSessions,
|
|
2553
|
+
isCurrentSessionValid,
|
|
2554
|
+
groupSessionsByUser,
|
|
2555
|
+
getUserLastSeenAt,
|
|
2556
|
+
getUsersLastSeenMap,
|
|
2416
2557
|
SupabaseProvider,
|
|
2417
2558
|
useSupabase,
|
|
2418
2559
|
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,43 @@ declare const Providers: {
|
|
|
488
489
|
readonly AIProvider: React$1.FC<AIProviderProps>;
|
|
489
490
|
};
|
|
490
491
|
|
|
491
|
-
|
|
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 trackUserSessionByRoute: (supabase: _supabase_supabase_js.SupabaseClient, userId: string, currentUrl?: string) => Promise<void>;
|
|
523
|
+
readonly fetchUserSessions: (supabase: _supabase_supabase_js.SupabaseClient) => Promise<UserSessionItem[]>;
|
|
524
|
+
readonly isCurrentSessionValid: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<boolean>;
|
|
525
|
+
readonly groupSessionsByUser: (sessions: UserSessionItem[]) => UserGroupedSessions[];
|
|
526
|
+
readonly getUserLastSeenAt: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<string | null>;
|
|
527
|
+
readonly getUsersLastSeenMap: (supabase: _supabase_supabase_js.SupabaseClient, userIds: string[]) => Promise<Record<string, string>>;
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
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,43 @@ declare const Providers: {
|
|
|
488
489
|
readonly AIProvider: React$1.FC<AIProviderProps>;
|
|
489
490
|
};
|
|
490
491
|
|
|
491
|
-
|
|
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 trackUserSessionByRoute: (supabase: _supabase_supabase_js.SupabaseClient, userId: string, currentUrl?: string) => Promise<void>;
|
|
523
|
+
readonly fetchUserSessions: (supabase: _supabase_supabase_js.SupabaseClient) => Promise<UserSessionItem[]>;
|
|
524
|
+
readonly isCurrentSessionValid: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<boolean>;
|
|
525
|
+
readonly groupSessionsByUser: (sessions: UserSessionItem[]) => UserGroupedSessions[];
|
|
526
|
+
readonly getUserLastSeenAt: (supabase: _supabase_supabase_js.SupabaseClient, userId: string) => Promise<string | null>;
|
|
527
|
+
readonly getUsersLastSeenMap: (supabase: _supabase_supabase_js.SupabaseClient, userIds: string[]) => Promise<Record<string, string>>;
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
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, trackUserSessionByRoute, 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,30 @@ var init_session_service = __esm({
|
|
|
196
274
|
{ onConflict: "user_id, device_id" }
|
|
197
275
|
);
|
|
198
276
|
};
|
|
277
|
+
trackUserSessionByRoute = async (supabase, userId, currentUrl) => {
|
|
278
|
+
const deviceId = getDeviceId();
|
|
279
|
+
await supabase.from("user_sessions").upsert(
|
|
280
|
+
{
|
|
281
|
+
user_id: userId,
|
|
282
|
+
device_id: deviceId,
|
|
283
|
+
user_agent: navigator.userAgent,
|
|
284
|
+
url: currentUrl || window.location.href,
|
|
285
|
+
last_seen_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
286
|
+
},
|
|
287
|
+
{ onConflict: "user_id, device_id" }
|
|
288
|
+
);
|
|
289
|
+
};
|
|
290
|
+
fetchUserSessions = async (supabase) => {
|
|
291
|
+
const currentDeviceId = getDeviceId();
|
|
292
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
293
|
+
if (!user) return [];
|
|
294
|
+
const { data, error } = await supabase.from("user_sessions").select("*").eq("user_id", user.id).order("last_seen_at", { ascending: false });
|
|
295
|
+
if (error) throw error;
|
|
296
|
+
return (data || []).map((s) => ({
|
|
297
|
+
...s,
|
|
298
|
+
is_current: s.device_id === currentDeviceId
|
|
299
|
+
}));
|
|
300
|
+
};
|
|
199
301
|
isCurrentSessionValid = async (supabase, userId) => {
|
|
200
302
|
try {
|
|
201
303
|
const currentDeviceId = getDeviceId();
|
|
@@ -211,11 +313,42 @@ var init_session_service = __esm({
|
|
|
211
313
|
return true;
|
|
212
314
|
}
|
|
213
315
|
};
|
|
316
|
+
groupSessionsByUser = (sessions) => {
|
|
317
|
+
const groupsMap = /* @__PURE__ */ new Map();
|
|
318
|
+
sessions.forEach((session) => {
|
|
319
|
+
const userId = session.user_id;
|
|
320
|
+
if (!groupsMap.has(userId)) {
|
|
321
|
+
groupsMap.set(userId, {
|
|
322
|
+
userId,
|
|
323
|
+
userName: session.user_name || "\u041D\u0435\u0432\u0456\u0434\u043E\u043C\u0438\u0439 \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447",
|
|
324
|
+
employeePost: session.employee_post || "\u041F\u043E\u0441\u0430\u0434\u0430 \u043D\u0435 \u0432\u043A\u0430\u0437\u0430\u043D\u0430",
|
|
325
|
+
role: session.role || "user",
|
|
326
|
+
sessions: []
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
groupsMap.get(userId).sessions.push(session);
|
|
330
|
+
});
|
|
331
|
+
return Array.from(groupsMap.values()).sort(
|
|
332
|
+
(a, b) => a.userName.localeCompare(b.userName, "uk")
|
|
333
|
+
);
|
|
334
|
+
};
|
|
214
335
|
getUserLastSeenAt = async (supabase, userId) => {
|
|
215
336
|
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
337
|
if (error || !data) return null;
|
|
217
338
|
return data.last_seen_at;
|
|
218
339
|
};
|
|
340
|
+
getUsersLastSeenMap = async (supabase, userIds) => {
|
|
341
|
+
if (!userIds.length) return {};
|
|
342
|
+
const { data, error } = await supabase.from("user_sessions").select("user_id, last_seen_at").in("user_id", userIds).order("last_seen_at", { ascending: false });
|
|
343
|
+
if (error || !data) return {};
|
|
344
|
+
const resultMap = {};
|
|
345
|
+
for (const session of data) {
|
|
346
|
+
if (!resultMap[session.user_id]) {
|
|
347
|
+
resultMap[session.user_id] = session.last_seen_at;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return resultMap;
|
|
351
|
+
};
|
|
219
352
|
}
|
|
220
353
|
});
|
|
221
354
|
|
|
@@ -2617,6 +2750,7 @@ __export(index_exports, {
|
|
|
2617
2750
|
ConfirmationProvider: () => ConfirmationProvider,
|
|
2618
2751
|
LocalizationProvider: () => LocalizationProvider,
|
|
2619
2752
|
Providers: () => Providers,
|
|
2753
|
+
Services: () => Services,
|
|
2620
2754
|
SupabaseProvider: () => SupabaseProvider,
|
|
2621
2755
|
ThemeProvider: () => ThemeProvider,
|
|
2622
2756
|
ToastProvider: () => ToastProvider,
|
|
@@ -3875,6 +4009,21 @@ var UI = {
|
|
|
3875
4009
|
// src/index.ts
|
|
3876
4010
|
init_context();
|
|
3877
4011
|
init_context();
|
|
4012
|
+
|
|
4013
|
+
// src/services/index.ts
|
|
4014
|
+
init_session_service();
|
|
4015
|
+
var Services = {
|
|
4016
|
+
SessionService: {
|
|
4017
|
+
fetchCompanySessions,
|
|
4018
|
+
trackUserSession,
|
|
4019
|
+
trackUserSessionByRoute,
|
|
4020
|
+
fetchUserSessions,
|
|
4021
|
+
isCurrentSessionValid,
|
|
4022
|
+
groupSessionsByUser,
|
|
4023
|
+
getUserLastSeenAt,
|
|
4024
|
+
getUsersLastSeenMap
|
|
4025
|
+
}
|
|
4026
|
+
};
|
|
3878
4027
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3879
4028
|
0 && (module.exports = {
|
|
3880
4029
|
AIProvider,
|
|
@@ -3884,6 +4033,7 @@ init_context();
|
|
|
3884
4033
|
ConfirmationProvider,
|
|
3885
4034
|
LocalizationProvider,
|
|
3886
4035
|
Providers,
|
|
4036
|
+
Services,
|
|
3887
4037
|
SupabaseProvider,
|
|
3888
4038
|
ThemeProvider,
|
|
3889
4039
|
ToastProvider,
|
package/dist/index.mjs
CHANGED
|
@@ -19,6 +19,14 @@ import {
|
|
|
19
19
|
UserRoleChipComponent,
|
|
20
20
|
UserSettingsProvider,
|
|
21
21
|
UserStatusChipComponent,
|
|
22
|
+
fetchCompanySessions,
|
|
23
|
+
fetchUserSessions,
|
|
24
|
+
getUserLastSeenAt,
|
|
25
|
+
getUsersLastSeenMap,
|
|
26
|
+
groupSessionsByUser,
|
|
27
|
+
isCurrentSessionValid,
|
|
28
|
+
trackUserSession,
|
|
29
|
+
trackUserSessionByRoute,
|
|
22
30
|
translationService,
|
|
23
31
|
useAI,
|
|
24
32
|
useAppSettings,
|
|
@@ -32,7 +40,7 @@ import {
|
|
|
32
40
|
useTranslationEdit,
|
|
33
41
|
useTranslationNamespace,
|
|
34
42
|
useUserSettings
|
|
35
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-UA57YNRJ.mjs";
|
|
36
44
|
|
|
37
45
|
// src/UI/components/inputs/TextFieldComponent.tsx
|
|
38
46
|
import { useState } from "react";
|
|
@@ -730,7 +738,7 @@ var EmailInputComponent = ({
|
|
|
730
738
|
import { useState as useState5, useEffect as useEffect2, lazy, Suspense } from "react";
|
|
731
739
|
import { Avatar, Tooltip as Tooltip3, Typography as Typography2 } from "@mui/material";
|
|
732
740
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
733
|
-
var LazyUserPopover = lazy(() => import("./UserPopoverComponent-
|
|
741
|
+
var LazyUserPopover = lazy(() => import("./UserPopoverComponent-5F4S3X4F.mjs"));
|
|
734
742
|
var getInitials = (name) => {
|
|
735
743
|
if (!name) return "?";
|
|
736
744
|
const parts = name.trim().split(" ");
|
|
@@ -1266,6 +1274,20 @@ var UI = {
|
|
|
1266
1274
|
PublicLanguageSelector
|
|
1267
1275
|
}
|
|
1268
1276
|
};
|
|
1277
|
+
|
|
1278
|
+
// src/services/index.ts
|
|
1279
|
+
var Services = {
|
|
1280
|
+
SessionService: {
|
|
1281
|
+
fetchCompanySessions,
|
|
1282
|
+
trackUserSession,
|
|
1283
|
+
trackUserSessionByRoute,
|
|
1284
|
+
fetchUserSessions,
|
|
1285
|
+
isCurrentSessionValid,
|
|
1286
|
+
groupSessionsByUser,
|
|
1287
|
+
getUserLastSeenAt,
|
|
1288
|
+
getUsersLastSeenMap
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1269
1291
|
export {
|
|
1270
1292
|
AIProvider,
|
|
1271
1293
|
AppSettingsProvider,
|
|
@@ -1274,6 +1296,7 @@ export {
|
|
|
1274
1296
|
ConfirmationProvider,
|
|
1275
1297
|
LocalizationProvider,
|
|
1276
1298
|
Providers,
|
|
1299
|
+
Services,
|
|
1277
1300
|
SupabaseProvider,
|
|
1278
1301
|
ThemeProvider,
|
|
1279
1302
|
ToastProvider,
|