@serhii.olishevskyi/docs 1.0.3 → 1.0.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.
- package/dist/UserPopoverComponent-J5CJMEVQ.mjs +8 -0
- package/dist/{chunk-CT3G3YSD.mjs → chunk-TCI2U35Q.mjs} +1437 -1045
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +2 -0
- package/dist/index.mjs +4 -2
- package/package.json +1 -1
- package/dist/UserPopoverComponent-BNJWFSLT.mjs +0 -399
|
@@ -1,9 +1,32 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// src/UI/components/data.display/UserPopoverComponent.tsx
|
|
2
|
+
import { useEffect as useEffect8, useState as useState12 } from "react";
|
|
3
|
+
import {
|
|
4
|
+
Popover,
|
|
5
|
+
Box as Box4,
|
|
6
|
+
Avatar,
|
|
7
|
+
Typography as Typography2,
|
|
8
|
+
Divider,
|
|
9
|
+
IconButton as IconButton4,
|
|
10
|
+
CircularProgress as CircularProgress2,
|
|
11
|
+
Stack,
|
|
12
|
+
Badge
|
|
13
|
+
} from "@mui/material";
|
|
14
|
+
import {
|
|
15
|
+
NotificationsNone as NotificationIcon,
|
|
16
|
+
AssignmentOutlined as TaskIcon,
|
|
17
|
+
ChatBubble as ChatIcon,
|
|
18
|
+
Work as WorkIcon,
|
|
19
|
+
CalendarToday as CalendarIcon,
|
|
20
|
+
Phone as PhoneIcon,
|
|
21
|
+
Close as CloseIcon,
|
|
22
|
+
Email as EmailIcon,
|
|
23
|
+
AccessTime as AccessTimeIcon,
|
|
24
|
+
Cake as CakeIcon
|
|
25
|
+
} from "@mui/icons-material";
|
|
26
|
+
|
|
27
|
+
// src/UI/components/data.display/UserStatusChipComponent.tsx
|
|
28
|
+
import { Chip, IconButton as IconButton2 } from "@mui/material";
|
|
29
|
+
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
|
7
30
|
|
|
8
31
|
// src/context/SupabaseProvider.tsx
|
|
9
32
|
import { createContext, useContext, useMemo } from "react";
|
|
@@ -34,20 +57,23 @@ var useSupabase = () => {
|
|
|
34
57
|
return context.supabase;
|
|
35
58
|
};
|
|
36
59
|
|
|
37
|
-
// src/context/
|
|
38
|
-
import { createContext as createContext2, useContext as useContext2
|
|
60
|
+
// src/context/TranslateProvider.tsx
|
|
61
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
39
62
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
63
|
+
var TranslationNamespaceContext = createContext2({
|
|
64
|
+
namespace: "common"
|
|
65
|
+
// значение по умолчанию
|
|
66
|
+
});
|
|
67
|
+
var TranslateProvider = ({ namespace, children }) => {
|
|
68
|
+
return /* @__PURE__ */ jsx2(TranslationNamespaceContext.Provider, { value: { namespace }, children });
|
|
69
|
+
};
|
|
70
|
+
var useTranslationNamespace = (overrideNamespace) => {
|
|
71
|
+
const context = useContext2(TranslationNamespaceContext);
|
|
72
|
+
return overrideNamespace || context.namespace;
|
|
47
73
|
};
|
|
48
74
|
|
|
49
75
|
// src/context/TranslationEditProvider.tsx
|
|
50
|
-
import { createContext as
|
|
76
|
+
import { createContext as createContext4, useContext as useContext4, useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
|
|
51
77
|
|
|
52
78
|
// node_modules/i18next/dist/esm/i18next.js
|
|
53
79
|
var isString = (obj) => typeof obj === "string";
|
|
@@ -2435,9 +2461,30 @@ var SupabaseCacheBackend = _SupabaseCacheBackend;
|
|
|
2435
2461
|
// src/i18n/config.ts
|
|
2436
2462
|
var config_default = instance;
|
|
2437
2463
|
|
|
2438
|
-
// src/context/
|
|
2464
|
+
// src/context/AuthProvider.tsx
|
|
2465
|
+
import { createContext as createContext3, useContext as useContext3, useEffect, useState, useCallback, useRef } from "react";
|
|
2466
|
+
|
|
2467
|
+
// src/services/session.service.ts
|
|
2468
|
+
var getUserLastSeenAt = async (supabase, userId) => {
|
|
2469
|
+
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();
|
|
2470
|
+
if (error || !data) return null;
|
|
2471
|
+
return data.last_seen_at;
|
|
2472
|
+
};
|
|
2473
|
+
|
|
2474
|
+
// src/context/AuthProvider.tsx
|
|
2439
2475
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
2440
|
-
var
|
|
2476
|
+
var AuthContext = createContext3(void 0);
|
|
2477
|
+
var useAuth = () => {
|
|
2478
|
+
const context = useContext3(AuthContext);
|
|
2479
|
+
if (!context) {
|
|
2480
|
+
throw new Error("useAuth should be used within the AuthProvider");
|
|
2481
|
+
}
|
|
2482
|
+
return context;
|
|
2483
|
+
};
|
|
2484
|
+
|
|
2485
|
+
// src/context/TranslationEditProvider.tsx
|
|
2486
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
2487
|
+
var TranslationEditContext = createContext4(void 0);
|
|
2441
2488
|
var TranslationEditProvider = ({ children }) => {
|
|
2442
2489
|
const supabase = useSupabase();
|
|
2443
2490
|
const { user } = useAuth();
|
|
@@ -2494,7 +2541,7 @@ var TranslationEditProvider = ({ children }) => {
|
|
|
2494
2541
|
setIsTranslationMode(val);
|
|
2495
2542
|
}
|
|
2496
2543
|
};
|
|
2497
|
-
return /* @__PURE__ */
|
|
2544
|
+
return /* @__PURE__ */ jsx4(
|
|
2498
2545
|
TranslationEditContext.Provider,
|
|
2499
2546
|
{
|
|
2500
2547
|
value: {
|
|
@@ -2509,1081 +2556,1035 @@ var TranslationEditProvider = ({ children }) => {
|
|
|
2509
2556
|
);
|
|
2510
2557
|
};
|
|
2511
2558
|
var useTranslationEdit = () => {
|
|
2512
|
-
const context =
|
|
2559
|
+
const context = useContext4(TranslationEditContext);
|
|
2513
2560
|
if (!context) throw new Error("useTranslationEdit must be used within TranslationEditProvider");
|
|
2514
2561
|
return context;
|
|
2515
2562
|
};
|
|
2516
2563
|
|
|
2517
|
-
// src/context/
|
|
2518
|
-
import { createContext as
|
|
2519
|
-
import {
|
|
2520
|
-
import
|
|
2521
|
-
|
|
2522
|
-
var
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2564
|
+
// src/context/LocalizationProvider.tsx
|
|
2565
|
+
import { createContext as createContext5, useContext as useContext5, useMemo as useMemo2, useEffect as useEffect3, useState as useState3 } from "react";
|
|
2566
|
+
import { useTranslation } from "react-i18next";
|
|
2567
|
+
import moment from "moment/min/moment-with-locales";
|
|
2568
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
2569
|
+
var DEFAULT_DATETIME_FORMATS = {
|
|
2570
|
+
uk: "DD.MM.YYYY, HH:mm",
|
|
2571
|
+
de: "DD.MM.YYYY, HH:mm",
|
|
2572
|
+
en: "MM/DD/YYYY, hh:mm A",
|
|
2573
|
+
es: "DD/MM/YYYY, HH:mm",
|
|
2574
|
+
fr: "DD/MM/YYYY, HH:mm",
|
|
2575
|
+
it: "DD/MM/YYYY, HH:mm"
|
|
2576
|
+
};
|
|
2577
|
+
var DEFAULT_DATE_FORMATS = {
|
|
2578
|
+
uk: "DD.MM.YYYY",
|
|
2579
|
+
de: "DD.MM.YYYY",
|
|
2580
|
+
en: "MM/DD/YYYY",
|
|
2581
|
+
es: "DD/MM/YYYY",
|
|
2582
|
+
fr: "DD/MM/YYYY",
|
|
2583
|
+
it: "DD/MM/YYYY"
|
|
2584
|
+
};
|
|
2585
|
+
var DEFAULT_TIME_FORMATS = {
|
|
2586
|
+
uk: "HH:mm:ss",
|
|
2587
|
+
de: "HH:mm:ss",
|
|
2588
|
+
en: "hh:mm:ss A",
|
|
2589
|
+
es: "HH:mm:ss",
|
|
2590
|
+
fr: "HH:mm:ss",
|
|
2591
|
+
it: "HH:mm:ss"
|
|
2592
|
+
};
|
|
2593
|
+
var LocalizationContext = createContext5(void 0);
|
|
2594
|
+
var LocalizationProvider = ({ children }) => {
|
|
2595
|
+
const supabase = useSupabase();
|
|
2596
|
+
const { i18n } = useTranslation();
|
|
2597
|
+
const [languages, setLanguages] = useState3([]);
|
|
2598
|
+
const [isLoadingLangs, setIsLoadingLangs] = useState3(true);
|
|
2599
|
+
const [languagesError, setLanguagesError] = useState3(null);
|
|
2600
|
+
useEffect3(() => {
|
|
2601
|
+
let isMounted = true;
|
|
2602
|
+
const fetchLanguages = async () => {
|
|
2603
|
+
try {
|
|
2604
|
+
setIsLoadingLangs(true);
|
|
2605
|
+
setLanguagesError(null);
|
|
2606
|
+
const { data: langs, error: langErr } = await supabase.from("languages").select("code, name, svg_flag, enabled, created_at").eq("enabled", true).order("code", { ascending: true });
|
|
2607
|
+
if (langErr) {
|
|
2608
|
+
throw langErr;
|
|
2544
2609
|
}
|
|
2545
|
-
if (
|
|
2546
|
-
|
|
2547
|
-
const ai = new GoogleGenAI({ apiKey });
|
|
2548
|
-
const response = await ai.models.generateContent({
|
|
2549
|
-
model: modelName,
|
|
2550
|
-
contents: prompt,
|
|
2551
|
-
config: {
|
|
2552
|
-
systemInstruction,
|
|
2553
|
-
temperature,
|
|
2554
|
-
responseMimeType: jsonMode ? "application/json" : "text/plain"
|
|
2555
|
-
}
|
|
2556
|
-
});
|
|
2557
|
-
const rawText = response.text?.trim() || "";
|
|
2558
|
-
if (jsonMode) {
|
|
2559
|
-
const cleanedText = rawText.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
2560
|
-
return JSON.parse(cleanedText);
|
|
2561
|
-
}
|
|
2562
|
-
return rawText;
|
|
2563
|
-
} catch (err) {
|
|
2564
|
-
console.error("[AIProvider Gemini Error]:", err);
|
|
2565
|
-
return null;
|
|
2566
|
-
}
|
|
2610
|
+
if (isMounted && langs) {
|
|
2611
|
+
setLanguages(langs);
|
|
2567
2612
|
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
method: "POST",
|
|
2577
|
-
headers: {
|
|
2578
|
-
"Content-Type": "application/json",
|
|
2579
|
-
Authorization: `Bearer ${apiKey}`
|
|
2580
|
-
},
|
|
2581
|
-
body: JSON.stringify({
|
|
2582
|
-
model: modelName,
|
|
2583
|
-
messages,
|
|
2584
|
-
temperature,
|
|
2585
|
-
...jsonMode ? { response_format: { type: "json_object" } } : {}
|
|
2586
|
-
})
|
|
2587
|
-
});
|
|
2588
|
-
const data = await res.json();
|
|
2589
|
-
const content = data.choices?.[0]?.message?.content?.trim() || "";
|
|
2590
|
-
if (jsonMode) {
|
|
2591
|
-
return JSON.parse(content);
|
|
2592
|
-
}
|
|
2593
|
-
return content;
|
|
2594
|
-
} catch (err) {
|
|
2595
|
-
console.error("[AIProvider OpenAI Error]:", err);
|
|
2596
|
-
return null;
|
|
2597
|
-
}
|
|
2613
|
+
} catch (err) {
|
|
2614
|
+
console.error("Failed to load languages from Supabase:", err);
|
|
2615
|
+
if (isMounted) {
|
|
2616
|
+
setLanguagesError(err.message || "Error loading languages");
|
|
2617
|
+
}
|
|
2618
|
+
} finally {
|
|
2619
|
+
if (isMounted) {
|
|
2620
|
+
setIsLoadingLangs(false);
|
|
2598
2621
|
}
|
|
2599
|
-
return null;
|
|
2600
2622
|
}
|
|
2601
2623
|
};
|
|
2602
|
-
|
|
2603
|
-
|
|
2624
|
+
fetchLanguages();
|
|
2625
|
+
return () => {
|
|
2626
|
+
isMounted = false;
|
|
2627
|
+
};
|
|
2628
|
+
}, [supabase]);
|
|
2629
|
+
const rawLang = i18n.language || "uk";
|
|
2630
|
+
const currentLang = rawLang.split("-")[0].toLowerCase();
|
|
2631
|
+
const getDateFormat = () => {
|
|
2632
|
+
return DEFAULT_DATE_FORMATS[currentLang] || "DD.MM.YYYY";
|
|
2633
|
+
};
|
|
2634
|
+
const getTimeFormat = () => {
|
|
2635
|
+
return DEFAULT_TIME_FORMATS[currentLang] || "HH:mm:ss";
|
|
2636
|
+
};
|
|
2637
|
+
const getDateTimeFormat = () => {
|
|
2638
|
+
return DEFAULT_DATETIME_FORMATS[currentLang] || "DD.MM.YYYY, HH:mm";
|
|
2639
|
+
};
|
|
2640
|
+
const formatDateTime = (date, customPattern) => {
|
|
2641
|
+
if (!date) return "\u2014";
|
|
2642
|
+
const pattern = customPattern || DEFAULT_DATETIME_FORMATS[currentLang] || "DD.MM.YYYY, HH:mm";
|
|
2643
|
+
return moment(date).locale(currentLang).format(pattern);
|
|
2644
|
+
};
|
|
2645
|
+
const formatDate = (date, customPattern) => {
|
|
2646
|
+
if (!date) return "\u2014";
|
|
2647
|
+
const pattern = customPattern || DEFAULT_DATE_FORMATS[currentLang] || "DD.MM.YYYY";
|
|
2648
|
+
return moment(date).locale(currentLang).format(pattern);
|
|
2649
|
+
};
|
|
2650
|
+
const formatTime = (date, customPattern) => {
|
|
2651
|
+
if (!date) return "\u2014";
|
|
2652
|
+
const pattern = customPattern || DEFAULT_TIME_FORMATS[currentLang] || "HH:mm:ss";
|
|
2653
|
+
return moment(date).locale(currentLang).format(pattern);
|
|
2654
|
+
};
|
|
2655
|
+
const formatRelativeTime = (date, withoutSuffix = false) => {
|
|
2656
|
+
if (!date) return "\u2014";
|
|
2657
|
+
return moment(date).locale(currentLang).fromNow(withoutSuffix);
|
|
2658
|
+
};
|
|
2659
|
+
const changeLanguage2 = async (lang) => {
|
|
2660
|
+
await i18n.changeLanguage(lang);
|
|
2661
|
+
};
|
|
2662
|
+
const value = useMemo2(
|
|
2663
|
+
() => ({
|
|
2664
|
+
currentLang,
|
|
2665
|
+
languages,
|
|
2666
|
+
isLoadingLangs,
|
|
2667
|
+
languagesError,
|
|
2668
|
+
changeLanguage: changeLanguage2,
|
|
2669
|
+
getDateFormat,
|
|
2670
|
+
getTimeFormat,
|
|
2671
|
+
getDateTimeFormat,
|
|
2672
|
+
formatDateTime,
|
|
2673
|
+
formatDate,
|
|
2674
|
+
formatTime,
|
|
2675
|
+
formatRelativeTime
|
|
2676
|
+
}),
|
|
2677
|
+
[currentLang, languages, isLoadingLangs, languagesError]
|
|
2678
|
+
);
|
|
2679
|
+
return /* @__PURE__ */ jsx5(LocalizationContext.Provider, { value, children });
|
|
2604
2680
|
};
|
|
2605
|
-
var
|
|
2606
|
-
const context =
|
|
2681
|
+
var useLocalization = () => {
|
|
2682
|
+
const context = useContext5(LocalizationContext);
|
|
2607
2683
|
if (!context) {
|
|
2608
|
-
throw new Error("
|
|
2684
|
+
throw new Error("useLocalization must be used within a LocalizationProvider");
|
|
2609
2685
|
}
|
|
2610
2686
|
return context;
|
|
2611
2687
|
};
|
|
2612
2688
|
|
|
2613
|
-
// src/context/
|
|
2614
|
-
import { createContext as
|
|
2615
|
-
import {
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2689
|
+
// src/context/ThemeProvider.tsx
|
|
2690
|
+
import { createContext as createContext6, useContext as useContext6, useMemo as useMemo3, useState as useState4, useEffect as useEffect4 } from "react";
|
|
2691
|
+
import { createTheme, ThemeProvider as MuiThemeProvider, CssBaseline, useMediaQuery } from "@mui/material";
|
|
2692
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
2693
|
+
var DEFAULT_LIGHT_COLORS = {
|
|
2694
|
+
primary: "#1976d2",
|
|
2695
|
+
secondary: "#9c27b0",
|
|
2696
|
+
backgroundDefault: "#f5f5f5",
|
|
2697
|
+
backgroundPaper: "#ffffff",
|
|
2698
|
+
textPrimary: "#0f172a",
|
|
2699
|
+
textSecondary: "#475569",
|
|
2700
|
+
success: "#2e7d32",
|
|
2701
|
+
error: "#d32f2f",
|
|
2702
|
+
warning: "#ed6c02",
|
|
2703
|
+
info: "#0288d1"
|
|
2622
2704
|
};
|
|
2623
|
-
var
|
|
2624
|
-
|
|
2625
|
-
|
|
2705
|
+
var DEFAULT_DARK_COLORS = {
|
|
2706
|
+
primary: "#90caf9",
|
|
2707
|
+
secondary: "#ce93d8",
|
|
2708
|
+
backgroundDefault: "#0f172a",
|
|
2709
|
+
backgroundPaper: "#1e293b",
|
|
2710
|
+
textPrimary: "#f8fafc",
|
|
2711
|
+
textSecondary: "#94a3b8",
|
|
2712
|
+
success: "#66bb6a",
|
|
2713
|
+
error: "#f44336",
|
|
2714
|
+
warning: "#ffa726",
|
|
2715
|
+
info: "#29b6f6"
|
|
2626
2716
|
};
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
// src/i18n/TranslationDialog.tsx
|
|
2636
|
-
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
2637
|
-
import {
|
|
2638
|
-
Dialog,
|
|
2639
|
-
DialogTitle,
|
|
2640
|
-
DialogContent,
|
|
2641
|
-
DialogActions,
|
|
2642
|
-
Button,
|
|
2643
|
-
TextField,
|
|
2644
|
-
Box,
|
|
2645
|
-
Typography,
|
|
2646
|
-
CircularProgress,
|
|
2647
|
-
IconButton,
|
|
2648
|
-
Tooltip,
|
|
2649
|
-
Paper,
|
|
2650
|
-
Alert
|
|
2651
|
-
} from "@mui/material";
|
|
2652
|
-
import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
|
|
2653
|
-
import TranslateIcon from "@mui/icons-material/Translate";
|
|
2654
|
-
import LightbulbIcon from "@mui/icons-material/Lightbulb";
|
|
2655
|
-
|
|
2656
|
-
// src/services/translation.service.ts
|
|
2657
|
-
var translationService = {
|
|
2658
|
-
// 1. Принимаем client первым аргументом
|
|
2659
|
-
incrementVersion: async (client, namespace, langCodes) => {
|
|
2660
|
-
try {
|
|
2661
|
-
const { data } = await client.from("translation_versions").select("version, lang_code").eq("namespace", namespace);
|
|
2662
|
-
const currentVersion = data && data.length > 0 ? Math.max(...data.map((d) => d.version || 0)) : 0;
|
|
2663
|
-
const nextVersion = currentVersion + 1;
|
|
2664
|
-
const languagesToUpdate = langCodes && langCodes.length > 0 ? langCodes : ["uk"];
|
|
2665
|
-
const updates = languagesToUpdate.map((lang_code) => ({
|
|
2666
|
-
namespace,
|
|
2667
|
-
lang_code,
|
|
2668
|
-
version: nextVersion,
|
|
2669
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2670
|
-
}));
|
|
2671
|
-
const { error } = await client.from("translation_versions").upsert(updates, { onConflict: "namespace,lang_code" });
|
|
2672
|
-
if (error) console.error("Error updating translation version:", error);
|
|
2673
|
-
return nextVersion;
|
|
2674
|
-
} catch (err) {
|
|
2675
|
-
console.error("Failed to increment version in Supabase:", err);
|
|
2676
|
-
return null;
|
|
2677
|
-
}
|
|
2717
|
+
var INITIAL_THEME_CONFIG = {
|
|
2718
|
+
name: "Base MUI Theme",
|
|
2719
|
+
mode: "light",
|
|
2720
|
+
borderRadius: 12,
|
|
2721
|
+
colors: {
|
|
2722
|
+
light: DEFAULT_LIGHT_COLORS,
|
|
2723
|
+
dark: DEFAULT_DARK_COLORS
|
|
2678
2724
|
},
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2725
|
+
typography: {
|
|
2726
|
+
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
|
|
2727
|
+
fontSize: 14,
|
|
2728
|
+
fontWeightLight: 300,
|
|
2729
|
+
fontWeightRegular: 400,
|
|
2730
|
+
fontWeightMedium: 600,
|
|
2731
|
+
fontWeightBold: 700
|
|
2732
|
+
}
|
|
2733
|
+
};
|
|
2734
|
+
var buildMuiTheme = (config) => {
|
|
2735
|
+
const currentMode = config.mode;
|
|
2736
|
+
const currentColors = config.colors?.[currentMode] || (currentMode === "light" ? DEFAULT_LIGHT_COLORS : DEFAULT_DARK_COLORS);
|
|
2737
|
+
return createTheme({
|
|
2738
|
+
palette: {
|
|
2739
|
+
mode: currentMode,
|
|
2740
|
+
primary: {
|
|
2741
|
+
main: currentColors.primary,
|
|
2742
|
+
light: currentMode === "light" ? "#63a4ff" : "#a1d2fa",
|
|
2743
|
+
dark: currentMode === "light" ? "#004ba0" : "#42a5f5"
|
|
2744
|
+
},
|
|
2745
|
+
secondary: {
|
|
2746
|
+
main: currentColors.secondary
|
|
2747
|
+
},
|
|
2748
|
+
background: {
|
|
2749
|
+
default: currentColors.backgroundDefault,
|
|
2750
|
+
paper: currentColors.backgroundPaper
|
|
2751
|
+
},
|
|
2752
|
+
text: {
|
|
2753
|
+
primary: currentColors.textPrimary,
|
|
2754
|
+
secondary: currentColors.textSecondary || (currentMode === "light" ? "#475569" : "#94a3b8")
|
|
2755
|
+
},
|
|
2756
|
+
success: { main: currentColors.success },
|
|
2757
|
+
error: { main: currentColors.error },
|
|
2758
|
+
warning: { main: currentColors.warning },
|
|
2759
|
+
info: { main: currentColors.info }
|
|
2760
|
+
},
|
|
2761
|
+
shape: {
|
|
2762
|
+
borderRadius: config.borderRadius
|
|
2763
|
+
},
|
|
2764
|
+
typography: {
|
|
2765
|
+
fontFamily: config.typography?.fontFamily || INITIAL_THEME_CONFIG.typography.fontFamily,
|
|
2766
|
+
fontSize: config.typography?.fontSize || INITIAL_THEME_CONFIG.typography.fontSize,
|
|
2767
|
+
fontWeightLight: config.typography?.fontWeightLight || INITIAL_THEME_CONFIG.typography.fontWeightLight,
|
|
2768
|
+
fontWeightRegular: config.typography?.fontWeightRegular || INITIAL_THEME_CONFIG.typography.fontWeightRegular,
|
|
2769
|
+
fontWeightMedium: config.typography?.fontWeightMedium || INITIAL_THEME_CONFIG.typography.fontWeightMedium,
|
|
2770
|
+
fontWeightBold: config.typography?.fontWeightBold || INITIAL_THEME_CONFIG.typography.fontWeightBold,
|
|
2771
|
+
h5: { fontWeight: 700, fontSize: "1.25rem" },
|
|
2772
|
+
subtitle1: { fontWeight: 600, fontSize: "0.95rem" },
|
|
2773
|
+
body2: { fontSize: "0.85rem" }
|
|
2774
|
+
},
|
|
2775
|
+
components: {
|
|
2776
|
+
MuiAutocomplete: {
|
|
2777
|
+
defaultProps: {
|
|
2778
|
+
noOptionsText: "\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E"
|
|
2779
|
+
}
|
|
2780
|
+
},
|
|
2781
|
+
MuiListItem: {
|
|
2782
|
+
styleOverrides: {
|
|
2783
|
+
root: {
|
|
2784
|
+
transition: "all 0.2s ease-in-out",
|
|
2785
|
+
"&:hover": {
|
|
2786
|
+
backgroundColor: currentMode === "light" ? "#f1f5f9" : "#334155"
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
},
|
|
2791
|
+
MuiListItemButton: {
|
|
2792
|
+
styleOverrides: {
|
|
2793
|
+
root: {
|
|
2794
|
+
transition: "all 0.2s ease-in-out",
|
|
2795
|
+
"&:hover": {
|
|
2796
|
+
backgroundColor: currentMode === "light" ? "#f1f5f9" : "#334155"
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
},
|
|
2801
|
+
MuiButton: {
|
|
2802
|
+
styleOverrides: {
|
|
2803
|
+
root: {
|
|
2804
|
+
textTransform: "none",
|
|
2805
|
+
fontWeight: 600,
|
|
2806
|
+
borderRadius: `${config.borderRadius}px`,
|
|
2807
|
+
padding: "10px 24px",
|
|
2808
|
+
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
},
|
|
2812
|
+
MuiPaper: {
|
|
2813
|
+
styleOverrides: {
|
|
2814
|
+
rounded: {
|
|
2815
|
+
borderRadius: `${config.borderRadius * 1.25}px`
|
|
2816
|
+
}
|
|
2699
2817
|
}
|
|
2700
|
-
return { translations };
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
if (params.targetLang) {
|
|
2704
|
-
const fallbackUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${params.targetLang}&dt=t&q=${encodeURIComponent(params.text)}`;
|
|
2705
|
-
const res = await fetch(fallbackUrl);
|
|
2706
|
-
if (res.ok) {
|
|
2707
|
-
const data = await res.json();
|
|
2708
|
-
return { translatedText: data[0].map((x) => x[0]).join("") };
|
|
2709
2818
|
}
|
|
2710
2819
|
}
|
|
2711
|
-
|
|
2712
|
-
}
|
|
2820
|
+
});
|
|
2713
2821
|
};
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
var
|
|
2718
|
-
open,
|
|
2719
|
-
onClose,
|
|
2720
|
-
translationKey,
|
|
2721
|
-
namespace,
|
|
2722
|
-
onSave
|
|
2723
|
-
}) => {
|
|
2822
|
+
var ThemeContext = createContext6(void 0);
|
|
2823
|
+
var THEME_STORAGE_KEY = "docsua_custom_theme_config";
|
|
2824
|
+
var THEME_MODE_STORAGE_KEY = "docsua_custom_theme_mode";
|
|
2825
|
+
var ThemeProvider = ({ children }) => {
|
|
2724
2826
|
const supabase = useSupabase();
|
|
2725
|
-
const {
|
|
2726
|
-
const
|
|
2727
|
-
const [
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
const [saving, setSaving] = useState3(false);
|
|
2731
|
-
const [translatingCode, setTranslatingCode] = useState3(null);
|
|
2732
|
-
const [isTranslatingAll, setIsTranslatingAll] = useState3(false);
|
|
2733
|
-
const [error, setError] = useState3(null);
|
|
2734
|
-
useEffect3(() => {
|
|
2735
|
-
if (!open) return;
|
|
2736
|
-
const fetchData = async () => {
|
|
2737
|
-
setLoading(true);
|
|
2738
|
-
setError(null);
|
|
2827
|
+
const { user, isAuthenticated, loading: isAuthLoading } = useAuth();
|
|
2828
|
+
const userId = user?.id;
|
|
2829
|
+
const [themeConfig, setThemeConfig] = useState4(() => {
|
|
2830
|
+
const saved = localStorage.getItem(THEME_STORAGE_KEY);
|
|
2831
|
+
if (saved) {
|
|
2739
2832
|
try {
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
setLanguages(langs || []);
|
|
2743
|
-
const { data: trans, error: transErr } = await supabase.from("translations").select("lang_code, value, context_hint").eq("namespace", namespace).eq("key", translationKey);
|
|
2744
|
-
if (transErr) throw transErr;
|
|
2745
|
-
const initialValues = {};
|
|
2746
|
-
let existingHint = "";
|
|
2747
|
-
langs?.forEach((l) => {
|
|
2748
|
-
const found = trans?.find((t2) => t2.lang_code === l.code);
|
|
2749
|
-
initialValues[l.code] = found ? found.value : "";
|
|
2750
|
-
if (found?.context_hint) {
|
|
2751
|
-
existingHint = found.context_hint;
|
|
2752
|
-
}
|
|
2753
|
-
});
|
|
2754
|
-
setValues(initialValues);
|
|
2755
|
-
setContextHint(existingHint);
|
|
2756
|
-
} catch (err) {
|
|
2757
|
-
console.error("Failed to load translations:", err);
|
|
2758
|
-
setError(err?.message || "Failed to load initial translation data.");
|
|
2759
|
-
} finally {
|
|
2760
|
-
setLoading(false);
|
|
2761
|
-
}
|
|
2762
|
-
};
|
|
2763
|
-
fetchData();
|
|
2764
|
-
}, [open, translationKey, namespace]);
|
|
2765
|
-
const handleAutoTranslate = async (e, targetLang) => {
|
|
2766
|
-
e.preventDefault();
|
|
2767
|
-
e.stopPropagation();
|
|
2768
|
-
setError(null);
|
|
2769
|
-
setTranslatingCode(targetLang);
|
|
2770
|
-
try {
|
|
2771
|
-
const res = await translationService.translateSmart(supabase, generate, {
|
|
2772
|
-
text: translationKey,
|
|
2773
|
-
targetLang,
|
|
2774
|
-
contextHint
|
|
2775
|
-
});
|
|
2776
|
-
if (res?.translatedText) {
|
|
2777
|
-
setValues((prev) => ({ ...prev, [targetLang]: res.translatedText }));
|
|
2833
|
+
return JSON.parse(saved);
|
|
2834
|
+
} catch (e) {
|
|
2778
2835
|
}
|
|
2779
|
-
} catch (err) {
|
|
2780
|
-
console.log(err);
|
|
2781
|
-
setError(`Failed to translate into ${targetLang}. ${err?.message || ""}`);
|
|
2782
|
-
} finally {
|
|
2783
|
-
setTranslatingCode(null);
|
|
2784
2836
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
if (
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2837
|
+
return INITIAL_THEME_CONFIG;
|
|
2838
|
+
});
|
|
2839
|
+
useEffect4(() => {
|
|
2840
|
+
if (isAuthLoading) return;
|
|
2841
|
+
if (isAuthenticated && userId) {
|
|
2842
|
+
const fetchActiveTheme = async () => {
|
|
2843
|
+
try {
|
|
2844
|
+
const { data, error } = await supabase.from("user_themes").select("*").eq("user_id", userId).eq("is_active", true).maybeSingle();
|
|
2845
|
+
if (error) {
|
|
2846
|
+
console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0435 \u0442\u0435\u043C\u044B \u0438\u0437 Supabase:", error.message);
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
if (data) {
|
|
2850
|
+
const activeTheme = data;
|
|
2851
|
+
const savedMode = localStorage.getItem(THEME_MODE_STORAGE_KEY);
|
|
2852
|
+
setThemeConfig({
|
|
2853
|
+
id: activeTheme.id,
|
|
2854
|
+
name: activeTheme.name || INITIAL_THEME_CONFIG.name,
|
|
2855
|
+
mode: savedMode || activeTheme.config?.mode || "light",
|
|
2856
|
+
borderRadius: activeTheme.config?.borderRadius ?? INITIAL_THEME_CONFIG.borderRadius,
|
|
2857
|
+
colors: activeTheme.config?.colors || INITIAL_THEME_CONFIG.colors,
|
|
2858
|
+
typography: activeTheme.config?.typography || INITIAL_THEME_CONFIG.typography,
|
|
2859
|
+
updatedAt: activeTheme.updated_at || void 0
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
} catch (err) {
|
|
2863
|
+
console.error("\u041D\u0435\u043F\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0437\u0430\u043F\u0440\u043E\u0441\u0435 \u0442\u0435\u043C\u044B:", err);
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
fetchActiveTheme();
|
|
2867
|
+
} else {
|
|
2868
|
+
setThemeConfig(INITIAL_THEME_CONFIG);
|
|
2869
|
+
localStorage.removeItem(THEME_STORAGE_KEY);
|
|
2870
|
+
localStorage.removeItem(THEME_MODE_STORAGE_KEY);
|
|
2807
2871
|
}
|
|
2808
|
-
};
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
setError(null);
|
|
2814
|
-
try {
|
|
2815
|
-
await onSave(values, contextHint);
|
|
2816
|
-
onClose();
|
|
2817
|
-
} catch (err) {
|
|
2818
|
-
console.error("Failed to save translations:", err);
|
|
2819
|
-
setError(err?.message || "Failed to save translations. Please try again.");
|
|
2820
|
-
} finally {
|
|
2821
|
-
setSaving(false);
|
|
2872
|
+
}, [isAuthenticated, isAuthLoading, userId]);
|
|
2873
|
+
useEffect4(() => {
|
|
2874
|
+
if (isAuthenticated) {
|
|
2875
|
+
localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(themeConfig));
|
|
2876
|
+
localStorage.setItem(THEME_MODE_STORAGE_KEY, themeConfig.mode);
|
|
2822
2877
|
}
|
|
2878
|
+
}, [themeConfig, isAuthenticated]);
|
|
2879
|
+
const mode = themeConfig.mode;
|
|
2880
|
+
const setMode = (newMode) => {
|
|
2881
|
+
setThemeConfig((prev) => ({ ...prev, mode: newMode }));
|
|
2823
2882
|
};
|
|
2824
|
-
const
|
|
2825
|
-
|
|
2826
|
-
setError(null);
|
|
2827
|
-
onClose();
|
|
2883
|
+
const toggleTheme = () => {
|
|
2884
|
+
setMode(mode === "light" ? "dark" : "light");
|
|
2828
2885
|
};
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
'"'
|
|
2849
|
-
] })
|
|
2850
|
-
] })
|
|
2851
|
-
] }),
|
|
2852
|
-
!loading && /* @__PURE__ */ jsx6(
|
|
2853
|
-
Button,
|
|
2854
|
-
{
|
|
2855
|
-
type: "button",
|
|
2856
|
-
size: "small",
|
|
2857
|
-
variant: "outlined",
|
|
2858
|
-
color: "secondary",
|
|
2859
|
-
startIcon: isTranslatingAll ? /* @__PURE__ */ jsx6(CircularProgress, { size: 16, color: "inherit" }) : /* @__PURE__ */ jsx6(TranslateIcon, { fontSize: "small" }),
|
|
2860
|
-
onClick: handleTranslateAll,
|
|
2861
|
-
disabled: isBusy,
|
|
2862
|
-
sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
|
|
2863
|
-
children: isTranslatingAll ? "Translating..." : "Translate All"
|
|
2864
|
-
}
|
|
2865
|
-
)
|
|
2866
|
-
] }),
|
|
2867
|
-
/* @__PURE__ */ jsxs(DialogContent, { dividers: true, sx: { pt: 2 }, children: [
|
|
2868
|
-
error && /* @__PURE__ */ jsx6(Alert, { severity: "error", onClose: () => setError(null), sx: { mb: 2 }, children: error }),
|
|
2869
|
-
loading ? /* @__PURE__ */ jsx6(Box, { sx: { display: "flex", justifyContent: "center", py: 4 }, children: /* @__PURE__ */ jsx6(CircularProgress, {}) }) : /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 2.5 }, children: [
|
|
2870
|
-
/* @__PURE__ */ jsxs(Paper, { variant: "outlined", sx: { p: 2, bgcolor: "action.hover", borderRadius: 2 }, children: [
|
|
2871
|
-
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
|
|
2872
|
-
/* @__PURE__ */ jsx6(LightbulbIcon, { color: "warning", fontSize: "small" }),
|
|
2873
|
-
/* @__PURE__ */ jsx6(Typography, { variant: "subtitle2", sx: { fontWeight: 600 }, children: "Context Hint / Clarification" })
|
|
2874
|
-
] }),
|
|
2875
|
-
/* @__PURE__ */ jsx6(
|
|
2876
|
-
TextField,
|
|
2877
|
-
{
|
|
2878
|
-
fullWidth: true,
|
|
2879
|
-
multiline: true,
|
|
2880
|
-
rows: 2,
|
|
2881
|
-
size: "small",
|
|
2882
|
-
value: contextHint,
|
|
2883
|
-
onChange: (e) => setContextHint(e.target.value),
|
|
2884
|
-
placeholder: "e.g. Website navigation menu link to the home page",
|
|
2885
|
-
helperText: "This hint explains the exact meaning to AI translation models.",
|
|
2886
|
-
disabled: isBusy
|
|
2887
|
-
}
|
|
2888
|
-
)
|
|
2889
|
-
] }),
|
|
2890
|
-
languages.map((lang) => /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2 }, children: [
|
|
2891
|
-
/* @__PURE__ */ jsx6(
|
|
2892
|
-
Box,
|
|
2893
|
-
{
|
|
2894
|
-
sx: {
|
|
2895
|
-
width: 32,
|
|
2896
|
-
height: 22,
|
|
2897
|
-
flexShrink: 0,
|
|
2898
|
-
borderRadius: "4px",
|
|
2899
|
-
overflow: "hidden",
|
|
2900
|
-
boxShadow: 1,
|
|
2901
|
-
"& svg": { width: "100%", height: "100%", display: "block" }
|
|
2902
|
-
},
|
|
2903
|
-
dangerouslySetInnerHTML: { __html: lang.svg_flag }
|
|
2904
|
-
}
|
|
2905
|
-
),
|
|
2906
|
-
/* @__PURE__ */ jsx6(
|
|
2907
|
-
TextField,
|
|
2908
|
-
{
|
|
2909
|
-
fullWidth: true,
|
|
2910
|
-
label: lang.name,
|
|
2911
|
-
size: "small",
|
|
2912
|
-
value: values[lang.code] || "",
|
|
2913
|
-
onChange: (e) => setValues((prev) => ({ ...prev, [lang.code]: e.target.value })),
|
|
2914
|
-
placeholder: "Enter manual translation...",
|
|
2915
|
-
disabled: isBusy
|
|
2916
|
-
}
|
|
2917
|
-
),
|
|
2918
|
-
/* @__PURE__ */ jsx6(Tooltip, { title: "Translate with context hint", children: /* @__PURE__ */ jsx6("span", { children: /* @__PURE__ */ jsx6(
|
|
2919
|
-
IconButton,
|
|
2920
|
-
{
|
|
2921
|
-
type: "button",
|
|
2922
|
-
color: "primary",
|
|
2923
|
-
disabled: isBusy,
|
|
2924
|
-
onClick: (e) => handleAutoTranslate(e, lang.code),
|
|
2925
|
-
children: translatingCode === lang.code ? /* @__PURE__ */ jsx6(CircularProgress, { size: 20, color: "inherit" }) : /* @__PURE__ */ jsx6(AutoFixHighIcon, { fontSize: "small" })
|
|
2926
|
-
}
|
|
2927
|
-
) }) })
|
|
2928
|
-
] }, lang.code))
|
|
2929
|
-
] })
|
|
2930
|
-
] }),
|
|
2931
|
-
/* @__PURE__ */ jsxs(DialogActions, { sx: { px: 3, py: 2 }, children: [
|
|
2932
|
-
/* @__PURE__ */ jsx6(Button, { onClick: onClose, disabled: isBusy, color: "inherit", type: "button", children: "Cancel" }),
|
|
2933
|
-
/* @__PURE__ */ jsx6(
|
|
2934
|
-
Button,
|
|
2935
|
-
{
|
|
2936
|
-
onClick: handleSaveAll,
|
|
2937
|
-
variant: "contained",
|
|
2938
|
-
disabled: isBusy,
|
|
2939
|
-
type: "button",
|
|
2940
|
-
children: saving ? "Saving..." : "Save All"
|
|
2941
|
-
}
|
|
2942
|
-
)
|
|
2943
|
-
] })
|
|
2944
|
-
]
|
|
2945
|
-
}
|
|
2946
|
-
);
|
|
2886
|
+
const theme = useMemo3(() => buildMuiTheme(themeConfig), [themeConfig]);
|
|
2887
|
+
const displayState = {
|
|
2888
|
+
isMobile: useMediaQuery(theme.breakpoints.down("sm")),
|
|
2889
|
+
isTablet: useMediaQuery(theme.breakpoints.between("sm", "md")),
|
|
2890
|
+
isDesktop: useMediaQuery(theme.breakpoints.between("md", "lg")),
|
|
2891
|
+
isWideDesktop: useMediaQuery(theme.breakpoints.between("lg", "xl")),
|
|
2892
|
+
isSuperWideDesktop: useMediaQuery(theme.breakpoints.up("xl"))
|
|
2893
|
+
};
|
|
2894
|
+
return /* @__PURE__ */ jsx6(ThemeContext.Provider, { value: { mode, toggleTheme, setMode, theme, themeConfig, setThemeConfig, displayState }, children: /* @__PURE__ */ jsxs(MuiThemeProvider, { theme, children: [
|
|
2895
|
+
/* @__PURE__ */ jsx6(CssBaseline, {}),
|
|
2896
|
+
children
|
|
2897
|
+
] }) });
|
|
2898
|
+
};
|
|
2899
|
+
var useAppTheme = () => {
|
|
2900
|
+
const context = useContext6(ThemeContext);
|
|
2901
|
+
if (!context) {
|
|
2902
|
+
throw new Error("useAppTheme should be used within the ThemeProvider");
|
|
2903
|
+
}
|
|
2904
|
+
return context;
|
|
2947
2905
|
};
|
|
2948
2906
|
|
|
2949
|
-
// src/
|
|
2950
|
-
import {
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
}) => {
|
|
2956
|
-
const namespace = useTranslationNamespace(overrideNamespace);
|
|
2957
|
-
const { t: t2, i18n } = useTranslation(namespace);
|
|
2958
|
-
const { isTranslationMode, canEdit } = useTranslationEdit();
|
|
2959
|
-
const supabase = useSupabase();
|
|
2960
|
-
const [anchorEl, setAnchorEl] = useState4(null);
|
|
2961
|
-
const [isDialogOpen, setIsDialogOpen] = useState4(false);
|
|
2962
|
-
const timeoutRef = useRef2(null);
|
|
2963
|
-
const translatedValue = t2(children, values);
|
|
2964
|
-
useEffect4(() => {
|
|
2965
|
-
return () => {
|
|
2966
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
2967
|
-
};
|
|
2968
|
-
}, []);
|
|
2969
|
-
if (!canEdit || !isTranslationMode) {
|
|
2970
|
-
return /* @__PURE__ */ jsx7(Fragment, { children: translatedValue });
|
|
2907
|
+
// src/context/BreadcrumbProvider.tsx
|
|
2908
|
+
import { createContext as createContext7, useContext as useContext7, useState as useState5 } from "react";
|
|
2909
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2910
|
+
var BreadcrumbContext = createContext7({
|
|
2911
|
+
breadcrumbTitles: {},
|
|
2912
|
+
setBreadcrumbTitle: () => {
|
|
2971
2913
|
}
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
}));
|
|
2980
|
-
const { error } = await supabase.from("translations").upsert(upsertData, { onConflict: "lang_code,namespace,key" });
|
|
2981
|
-
if (error) {
|
|
2982
|
-
alert("\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445");
|
|
2983
|
-
throw error;
|
|
2984
|
-
}
|
|
2985
|
-
await translationService.incrementVersion(supabase, namespace, Object.keys(newValues));
|
|
2986
|
-
Object.entries(newValues).forEach(([langCode, text]) => {
|
|
2987
|
-
i18n.addResource(langCode, namespace, children, text);
|
|
2914
|
+
});
|
|
2915
|
+
var BreadcrumbProvider = ({ children }) => {
|
|
2916
|
+
const [breadcrumbTitles, setBreadcrumbTitles] = useState5({});
|
|
2917
|
+
const setBreadcrumbTitle = (path, title) => {
|
|
2918
|
+
setBreadcrumbTitles((prev) => {
|
|
2919
|
+
if (prev[path] === title) return prev;
|
|
2920
|
+
return { ...prev, [path]: title };
|
|
2988
2921
|
});
|
|
2989
2922
|
};
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
};
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
/* @__PURE__ */
|
|
3014
|
-
|
|
3015
|
-
{
|
|
3016
|
-
component: "span",
|
|
3017
|
-
onMouseEnter: handleMouseEnter,
|
|
3018
|
-
onMouseLeave: handleMouseLeave,
|
|
3019
|
-
onClick: (e) => {
|
|
3020
|
-
e.preventDefault();
|
|
3021
|
-
e.stopPropagation();
|
|
3022
|
-
},
|
|
3023
|
-
sx: {
|
|
3024
|
-
display: "inline-block",
|
|
3025
|
-
position: "relative",
|
|
3026
|
-
borderRadius: "4px",
|
|
3027
|
-
padding: "0 3px",
|
|
3028
|
-
margin: "0 1px",
|
|
3029
|
-
border: "1px dashed #f59e0b",
|
|
3030
|
-
backgroundColor: "rgba(254, 243, 199, 0.35)",
|
|
3031
|
-
cursor: "pointer",
|
|
3032
|
-
transition: "all 0.2s ease-in-out",
|
|
3033
|
-
userSelect: "none",
|
|
3034
|
-
"&:hover": {
|
|
3035
|
-
backgroundColor: "#fef08a",
|
|
3036
|
-
borderColor: "#d97706",
|
|
3037
|
-
boxShadow: "0 0 0 2px rgba(245, 158, 11, 0.35)",
|
|
3038
|
-
transform: "scale(1.02)"
|
|
3039
|
-
}
|
|
3040
|
-
},
|
|
3041
|
-
children: translatedValue
|
|
3042
|
-
}
|
|
3043
|
-
),
|
|
3044
|
-
/* @__PURE__ */ jsx7(
|
|
3045
|
-
Popper,
|
|
2923
|
+
return /* @__PURE__ */ jsx7(BreadcrumbContext.Provider, { value: { breadcrumbTitles, setBreadcrumbTitle }, children });
|
|
2924
|
+
};
|
|
2925
|
+
var useBreadcrumb = () => useContext7(BreadcrumbContext);
|
|
2926
|
+
|
|
2927
|
+
// src/context/ToastProvider.tsx
|
|
2928
|
+
import { createContext as createContext8, useContext as useContext8, useState as useState6, useCallback as useCallback3, useMemo as useMemo4 } from "react";
|
|
2929
|
+
import { Snackbar, Alert } from "@mui/material";
|
|
2930
|
+
import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2931
|
+
var ToastContext = createContext8(void 0);
|
|
2932
|
+
var ToastProvider = ({ children }) => {
|
|
2933
|
+
const [toast, setToast] = useState6(null);
|
|
2934
|
+
const showToast = useCallback3((text, type = "info") => {
|
|
2935
|
+
setToast({ text, type });
|
|
2936
|
+
}, []);
|
|
2937
|
+
const hideToast = useCallback3(() => {
|
|
2938
|
+
setToast(null);
|
|
2939
|
+
}, []);
|
|
2940
|
+
const contextValue = useMemo4(() => ({
|
|
2941
|
+
showToast,
|
|
2942
|
+
hideToast
|
|
2943
|
+
}), [showToast, hideToast]);
|
|
2944
|
+
return /* @__PURE__ */ jsxs2(ToastContext.Provider, { value: contextValue, children: [
|
|
2945
|
+
children,
|
|
2946
|
+
/* @__PURE__ */ jsx8(
|
|
2947
|
+
Snackbar,
|
|
3046
2948
|
{
|
|
3047
|
-
open,
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
children: /* @__PURE__ */
|
|
3052
|
-
|
|
2949
|
+
open: Boolean(toast),
|
|
2950
|
+
autoHideDuration: 3500,
|
|
2951
|
+
onClose: hideToast,
|
|
2952
|
+
anchorOrigin: { vertical: "bottom", horizontal: "center" },
|
|
2953
|
+
children: /* @__PURE__ */ jsx8(
|
|
2954
|
+
Alert,
|
|
3053
2955
|
{
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
onMouseLeave: handleMouseLeave,
|
|
3059
|
-
sx: {
|
|
3060
|
-
mt: 0.5,
|
|
3061
|
-
borderRadius: 2,
|
|
3062
|
-
minWidth: 140,
|
|
3063
|
-
boxShadow: "0px 10px 15px -3px rgba(0,0,0,0.1)"
|
|
3064
|
-
},
|
|
3065
|
-
children: /* @__PURE__ */ jsxs2(MenuList, { dense: true, sx: { py: 0.5 }, children: [
|
|
3066
|
-
/* @__PURE__ */ jsxs2(MenuItem, { onClick: handleOpenDialog, sx: { py: 0.8, px: 1.5 }, children: [
|
|
3067
|
-
/* @__PURE__ */ jsx7(ListItemIcon, { sx: { minWidth: "28px !important" }, children: /* @__PURE__ */ jsx7(TranslateIcon2, { fontSize: "small", sx: { color: "#d97706" } }) }),
|
|
3068
|
-
/* @__PURE__ */ jsx7(
|
|
3069
|
-
ListItemText,
|
|
3070
|
-
{
|
|
3071
|
-
primary: "Localization",
|
|
3072
|
-
slotProps: {
|
|
3073
|
-
primary: {
|
|
3074
|
-
sx: {
|
|
3075
|
-
fontSize: "0.825rem",
|
|
3076
|
-
fontWeight: 600,
|
|
3077
|
-
color: "text.primary"
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
}
|
|
3081
|
-
}
|
|
3082
|
-
)
|
|
3083
|
-
] }),
|
|
3084
|
-
/* @__PURE__ */ jsxs2(MenuItem, { onClick: handleLaunchAction, sx: { py: 0.8, px: 1.5 }, children: [
|
|
3085
|
-
/* @__PURE__ */ jsx7(ListItemIcon, { sx: { minWidth: "28px !important" }, children: /* @__PURE__ */ jsx7(LaunchIcon, { fontSize: "small", color: "secondary" }) }),
|
|
3086
|
-
/* @__PURE__ */ jsx7(
|
|
3087
|
-
ListItemText,
|
|
3088
|
-
{
|
|
3089
|
-
primary: "Launch action",
|
|
3090
|
-
slotProps: {
|
|
3091
|
-
primary: {
|
|
3092
|
-
sx: {
|
|
3093
|
-
fontSize: "0.825rem",
|
|
3094
|
-
fontWeight: 600,
|
|
3095
|
-
color: "text.primary"
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
)
|
|
3101
|
-
] })
|
|
3102
|
-
] })
|
|
2956
|
+
severity: toast?.type || "info",
|
|
2957
|
+
variant: "filled",
|
|
2958
|
+
onClose: hideToast,
|
|
2959
|
+
children: toast?.text
|
|
3103
2960
|
}
|
|
3104
2961
|
)
|
|
3105
2962
|
}
|
|
3106
|
-
)
|
|
3107
|
-
/* @__PURE__ */ jsx7("span", { style: { display: "inline" }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx7(
|
|
3108
|
-
TranslationDialog,
|
|
3109
|
-
{
|
|
3110
|
-
open: isDialogOpen,
|
|
3111
|
-
onClose: () => setIsDialogOpen(false),
|
|
3112
|
-
translationKey: children,
|
|
3113
|
-
namespace,
|
|
3114
|
-
onSave: handleSaveTranslations
|
|
3115
|
-
}
|
|
3116
|
-
) })
|
|
2963
|
+
)
|
|
3117
2964
|
] });
|
|
3118
2965
|
};
|
|
2966
|
+
var useToast = () => {
|
|
2967
|
+
const context = useContext8(ToastContext);
|
|
2968
|
+
if (!context) {
|
|
2969
|
+
throw new Error("useToast must be used within a ToastProvider");
|
|
2970
|
+
}
|
|
2971
|
+
return context;
|
|
2972
|
+
};
|
|
3119
2973
|
|
|
3120
|
-
// src/
|
|
3121
|
-
import {
|
|
3122
|
-
import {
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
2974
|
+
// src/context/ConfirmationProvider.tsx
|
|
2975
|
+
import { createContext as createContext10, useContext as useContext10, useState as useState9, useCallback as useCallback4 } from "react";
|
|
2976
|
+
import {
|
|
2977
|
+
Button as Button2,
|
|
2978
|
+
Dialog as Dialog2,
|
|
2979
|
+
DialogActions as DialogActions2,
|
|
2980
|
+
DialogContent as DialogContent2,
|
|
2981
|
+
DialogContentText,
|
|
2982
|
+
DialogTitle as DialogTitle2,
|
|
2983
|
+
Box as Box3
|
|
2984
|
+
} from "@mui/material";
|
|
2985
|
+
import {
|
|
2986
|
+
Error as ErrorIcon,
|
|
2987
|
+
WarningAmberOutlined as WarningIcon,
|
|
2988
|
+
InfoOutlined as InfoIcon,
|
|
2989
|
+
CheckCircleOutlined as SuccessIcon
|
|
2990
|
+
} from "@mui/icons-material";
|
|
2991
|
+
|
|
2992
|
+
// src/i18n/TranslateText.tsx
|
|
2993
|
+
import { useState as useState8, useRef as useRef2, useEffect as useEffect6 } from "react";
|
|
2994
|
+
import { useTranslation as useTranslation2 } from "react-i18next";
|
|
2995
|
+
import { Box as Box2, Popper, Paper as Paper2, MenuList, MenuItem, ListItemIcon, ListItemText } from "@mui/material";
|
|
2996
|
+
import TranslateIcon2 from "@mui/icons-material/Translate";
|
|
2997
|
+
import LaunchIcon from "@mui/icons-material/Launch";
|
|
2998
|
+
|
|
2999
|
+
// src/i18n/TranslationDialog.tsx
|
|
3000
|
+
import { useEffect as useEffect5, useState as useState7 } from "react";
|
|
3001
|
+
import {
|
|
3002
|
+
Dialog,
|
|
3003
|
+
DialogTitle,
|
|
3004
|
+
DialogContent,
|
|
3005
|
+
DialogActions,
|
|
3006
|
+
Button,
|
|
3007
|
+
TextField,
|
|
3008
|
+
Box,
|
|
3009
|
+
Typography,
|
|
3010
|
+
CircularProgress,
|
|
3011
|
+
IconButton,
|
|
3012
|
+
Tooltip,
|
|
3013
|
+
Paper,
|
|
3014
|
+
Alert as Alert2
|
|
3015
|
+
} from "@mui/material";
|
|
3016
|
+
import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
|
|
3017
|
+
import TranslateIcon from "@mui/icons-material/Translate";
|
|
3018
|
+
import LightbulbIcon from "@mui/icons-material/Lightbulb";
|
|
3019
|
+
|
|
3020
|
+
// src/services/translation.service.ts
|
|
3021
|
+
var translationService = {
|
|
3022
|
+
// 1. Принимаем client первым аргументом
|
|
3023
|
+
incrementVersion: async (client, namespace, langCodes) => {
|
|
3024
|
+
try {
|
|
3025
|
+
const { data } = await client.from("translation_versions").select("version, lang_code").eq("namespace", namespace);
|
|
3026
|
+
const currentVersion = data && data.length > 0 ? Math.max(...data.map((d) => d.version || 0)) : 0;
|
|
3027
|
+
const nextVersion = currentVersion + 1;
|
|
3028
|
+
const languagesToUpdate = langCodes && langCodes.length > 0 ? langCodes : ["uk"];
|
|
3029
|
+
const updates = languagesToUpdate.map((lang_code) => ({
|
|
3030
|
+
namespace,
|
|
3031
|
+
lang_code,
|
|
3032
|
+
version: nextVersion,
|
|
3033
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3034
|
+
}));
|
|
3035
|
+
const { error } = await client.from("translation_versions").upsert(updates, { onConflict: "namespace,lang_code" });
|
|
3036
|
+
if (error) console.error("Error updating translation version:", error);
|
|
3037
|
+
return nextVersion;
|
|
3038
|
+
} catch (err) {
|
|
3039
|
+
console.error("Failed to increment version in Supabase:", err);
|
|
3040
|
+
return null;
|
|
3041
|
+
}
|
|
3042
|
+
},
|
|
3043
|
+
translateSmart: async (client, aiGenerateFn, params) => {
|
|
3044
|
+
const languagesToTranslate = params.targetLangs || (params.targetLang ? [params.targetLang] : []);
|
|
3045
|
+
try {
|
|
3046
|
+
const { data, error } = await client.functions.invoke("translate-smart", { body: params });
|
|
3047
|
+
if (!error && data) return data;
|
|
3048
|
+
} catch {
|
|
3049
|
+
console.warn("Edge Function translate-smart \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C AIProvider...");
|
|
3050
|
+
}
|
|
3051
|
+
if (aiGenerateFn) {
|
|
3052
|
+
const prompt = `Translate the source text into language codes: ${JSON.stringify(languagesToTranslate)}.
|
|
3053
|
+
Source Text: "${params.text}"
|
|
3054
|
+
${params.contextHint ? `Context Hint: "${params.contextHint}"` : ""}`;
|
|
3055
|
+
const translations = await aiGenerateFn({
|
|
3056
|
+
prompt,
|
|
3057
|
+
systemInstruction: 'Respond STRICTLY with a valid JSON mapping lang code to text. Example: {"en": "Hello"}',
|
|
3058
|
+
jsonMode: true
|
|
3059
|
+
});
|
|
3060
|
+
if (translations) {
|
|
3061
|
+
if (params.targetLang) {
|
|
3062
|
+
return { translatedText: translations[params.targetLang] || params.text };
|
|
3144
3063
|
}
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
...props,
|
|
3148
|
-
children: disableSpan ? children : /* @__PURE__ */ jsx8("span", { children })
|
|
3064
|
+
return { translations };
|
|
3065
|
+
}
|
|
3149
3066
|
}
|
|
3150
|
-
|
|
3067
|
+
if (params.targetLang) {
|
|
3068
|
+
const fallbackUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${params.targetLang}&dt=t&q=${encodeURIComponent(params.text)}`;
|
|
3069
|
+
const res = await fetch(fallbackUrl);
|
|
3070
|
+
if (res.ok) {
|
|
3071
|
+
const data = await res.json();
|
|
3072
|
+
return { translatedText: data[0].map((x) => x[0]).join("") };
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
throw new Error("All translation strategies failed.");
|
|
3076
|
+
}
|
|
3151
3077
|
};
|
|
3152
3078
|
|
|
3153
|
-
// src/context/
|
|
3154
|
-
import { createContext as
|
|
3155
|
-
import {
|
|
3156
|
-
import moment from "moment/min/moment-with-locales";
|
|
3079
|
+
// src/context/AIProvider.tsx
|
|
3080
|
+
import { createContext as createContext9, useContext as useContext9, useMemo as useMemo5 } from "react";
|
|
3081
|
+
import { GoogleGenAI } from "@google/genai";
|
|
3157
3082
|
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
3158
|
-
var
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3083
|
+
var AIContext = createContext9(null);
|
|
3084
|
+
var AIProvider = ({
|
|
3085
|
+
children,
|
|
3086
|
+
config,
|
|
3087
|
+
apiKey: directApiKey
|
|
3088
|
+
}) => {
|
|
3089
|
+
const apiKey = directApiKey || config?.apiKey;
|
|
3090
|
+
const provider = config?.provider || "gemini";
|
|
3091
|
+
const modelName = config?.modelName || (provider === "gemini" ? "gemini-2.5-flash" : "gpt-4o-mini");
|
|
3092
|
+
const contextValue = useMemo5(() => {
|
|
3093
|
+
return {
|
|
3094
|
+
apiKey,
|
|
3095
|
+
provider,
|
|
3096
|
+
modelName,
|
|
3097
|
+
generate: async ({
|
|
3098
|
+
prompt,
|
|
3099
|
+
systemInstruction,
|
|
3100
|
+
jsonMode = false,
|
|
3101
|
+
temperature
|
|
3102
|
+
}) => {
|
|
3103
|
+
if (!apiKey) {
|
|
3104
|
+
console.warn("[AIProvider]: API Key \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.");
|
|
3105
|
+
return null;
|
|
3106
|
+
}
|
|
3107
|
+
if (provider === "gemini") {
|
|
3108
|
+
try {
|
|
3109
|
+
const ai = new GoogleGenAI({ apiKey });
|
|
3110
|
+
const response = await ai.models.generateContent({
|
|
3111
|
+
model: modelName,
|
|
3112
|
+
contents: prompt,
|
|
3113
|
+
config: {
|
|
3114
|
+
systemInstruction,
|
|
3115
|
+
temperature,
|
|
3116
|
+
responseMimeType: jsonMode ? "application/json" : "text/plain"
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
const rawText = response.text?.trim() || "";
|
|
3120
|
+
if (jsonMode) {
|
|
3121
|
+
const cleanedText = rawText.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
3122
|
+
return JSON.parse(cleanedText);
|
|
3123
|
+
}
|
|
3124
|
+
return rawText;
|
|
3125
|
+
} catch (err) {
|
|
3126
|
+
console.error("[AIProvider Gemini Error]:", err);
|
|
3127
|
+
return null;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
if (provider === "openai") {
|
|
3131
|
+
try {
|
|
3132
|
+
const messages = [];
|
|
3133
|
+
if (systemInstruction) {
|
|
3134
|
+
messages.push({ role: "system", content: systemInstruction });
|
|
3135
|
+
}
|
|
3136
|
+
messages.push({ role: "user", content: prompt });
|
|
3137
|
+
const res = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
3138
|
+
method: "POST",
|
|
3139
|
+
headers: {
|
|
3140
|
+
"Content-Type": "application/json",
|
|
3141
|
+
Authorization: `Bearer ${apiKey}`
|
|
3142
|
+
},
|
|
3143
|
+
body: JSON.stringify({
|
|
3144
|
+
model: modelName,
|
|
3145
|
+
messages,
|
|
3146
|
+
temperature,
|
|
3147
|
+
...jsonMode ? { response_format: { type: "json_object" } } : {}
|
|
3148
|
+
})
|
|
3149
|
+
});
|
|
3150
|
+
const data = await res.json();
|
|
3151
|
+
const content = data.choices?.[0]?.message?.content?.trim() || "";
|
|
3152
|
+
if (jsonMode) {
|
|
3153
|
+
return JSON.parse(content);
|
|
3154
|
+
}
|
|
3155
|
+
return content;
|
|
3156
|
+
} catch (err) {
|
|
3157
|
+
console.error("[AIProvider OpenAI Error]:", err);
|
|
3158
|
+
return null;
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
return null;
|
|
3162
|
+
}
|
|
3163
|
+
};
|
|
3164
|
+
}, [apiKey, provider, modelName]);
|
|
3165
|
+
return /* @__PURE__ */ jsx9(AIContext.Provider, { value: contextValue, children });
|
|
3173
3166
|
};
|
|
3174
|
-
var
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
it: "HH:mm:ss"
|
|
3167
|
+
var useAI = () => {
|
|
3168
|
+
const context = useContext9(AIContext);
|
|
3169
|
+
if (!context) {
|
|
3170
|
+
throw new Error("useAI \u0434\u043E\u043B\u0436\u0435\u043D \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u0432\u043D\u0443\u0442\u0440\u0438 <AIProvider>");
|
|
3171
|
+
}
|
|
3172
|
+
return context;
|
|
3181
3173
|
};
|
|
3182
|
-
|
|
3183
|
-
|
|
3174
|
+
|
|
3175
|
+
// src/i18n/TranslationDialog.tsx
|
|
3176
|
+
import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3177
|
+
var TranslationDialog = ({
|
|
3178
|
+
open,
|
|
3179
|
+
onClose,
|
|
3180
|
+
translationKey,
|
|
3181
|
+
namespace,
|
|
3182
|
+
onSave
|
|
3183
|
+
}) => {
|
|
3184
3184
|
const supabase = useSupabase();
|
|
3185
|
-
const {
|
|
3186
|
-
const [languages, setLanguages] =
|
|
3187
|
-
const [
|
|
3188
|
-
const [
|
|
3185
|
+
const { generate } = useAI();
|
|
3186
|
+
const [languages, setLanguages] = useState7([]);
|
|
3187
|
+
const [values, setValues] = useState7({});
|
|
3188
|
+
const [contextHint, setContextHint] = useState7("");
|
|
3189
|
+
const [loading, setLoading] = useState7(false);
|
|
3190
|
+
const [saving, setSaving] = useState7(false);
|
|
3191
|
+
const [translatingCode, setTranslatingCode] = useState7(null);
|
|
3192
|
+
const [isTranslatingAll, setIsTranslatingAll] = useState7(false);
|
|
3193
|
+
const [error, setError] = useState7(null);
|
|
3189
3194
|
useEffect5(() => {
|
|
3190
|
-
|
|
3191
|
-
const
|
|
3195
|
+
if (!open) return;
|
|
3196
|
+
const fetchData = async () => {
|
|
3197
|
+
setLoading(true);
|
|
3198
|
+
setError(null);
|
|
3192
3199
|
try {
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
}
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3200
|
+
const { data: langs, error: langErr } = await supabase.from("languages").select("code, name, svg_flag").eq("enabled", true);
|
|
3201
|
+
if (langErr) throw langErr;
|
|
3202
|
+
setLanguages(langs || []);
|
|
3203
|
+
const { data: trans, error: transErr } = await supabase.from("translations").select("lang_code, value, context_hint").eq("namespace", namespace).eq("key", translationKey);
|
|
3204
|
+
if (transErr) throw transErr;
|
|
3205
|
+
const initialValues = {};
|
|
3206
|
+
let existingHint = "";
|
|
3207
|
+
langs?.forEach((l) => {
|
|
3208
|
+
const found = trans?.find((t2) => t2.lang_code === l.code);
|
|
3209
|
+
initialValues[l.code] = found ? found.value : "";
|
|
3210
|
+
if (found?.context_hint) {
|
|
3211
|
+
existingHint = found.context_hint;
|
|
3212
|
+
}
|
|
3213
|
+
});
|
|
3214
|
+
setValues(initialValues);
|
|
3215
|
+
setContextHint(existingHint);
|
|
3202
3216
|
} catch (err) {
|
|
3203
|
-
console.error("Failed to load
|
|
3204
|
-
|
|
3205
|
-
setLanguagesError(err.message || "Error loading languages");
|
|
3206
|
-
}
|
|
3217
|
+
console.error("Failed to load translations:", err);
|
|
3218
|
+
setError(err?.message || "Failed to load initial translation data.");
|
|
3207
3219
|
} finally {
|
|
3208
|
-
|
|
3209
|
-
setIsLoadingLangs(false);
|
|
3210
|
-
}
|
|
3220
|
+
setLoading(false);
|
|
3211
3221
|
}
|
|
3212
3222
|
};
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
if (!date) return "\u2014";
|
|
3236
|
-
const pattern = customPattern || DEFAULT_DATE_FORMATS[currentLang] || "DD.MM.YYYY";
|
|
3237
|
-
return moment(date).locale(currentLang).format(pattern);
|
|
3223
|
+
fetchData();
|
|
3224
|
+
}, [open, translationKey, namespace]);
|
|
3225
|
+
const handleAutoTranslate = async (e, targetLang) => {
|
|
3226
|
+
e.preventDefault();
|
|
3227
|
+
e.stopPropagation();
|
|
3228
|
+
setError(null);
|
|
3229
|
+
setTranslatingCode(targetLang);
|
|
3230
|
+
try {
|
|
3231
|
+
const res = await translationService.translateSmart(supabase, generate, {
|
|
3232
|
+
text: translationKey,
|
|
3233
|
+
targetLang,
|
|
3234
|
+
contextHint
|
|
3235
|
+
});
|
|
3236
|
+
if (res?.translatedText) {
|
|
3237
|
+
setValues((prev) => ({ ...prev, [targetLang]: res.translatedText }));
|
|
3238
|
+
}
|
|
3239
|
+
} catch (err) {
|
|
3240
|
+
console.log(err);
|
|
3241
|
+
setError(`Failed to translate into ${targetLang}. ${err?.message || ""}`);
|
|
3242
|
+
} finally {
|
|
3243
|
+
setTranslatingCode(null);
|
|
3244
|
+
}
|
|
3238
3245
|
};
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3246
|
+
const handleTranslateAll = async (e) => {
|
|
3247
|
+
e.preventDefault();
|
|
3248
|
+
e.stopPropagation();
|
|
3249
|
+
if (!translationKey || languages.length === 0) return;
|
|
3250
|
+
setIsTranslatingAll(true);
|
|
3251
|
+
setError(null);
|
|
3252
|
+
try {
|
|
3253
|
+
const targetLangs = languages.map((l) => l.code);
|
|
3254
|
+
const res = await translationService.translateSmart(supabase, generate, {
|
|
3255
|
+
text: translationKey,
|
|
3256
|
+
targetLangs,
|
|
3257
|
+
contextHint
|
|
3258
|
+
});
|
|
3259
|
+
if (res?.translations) {
|
|
3260
|
+
setValues((prev) => ({ ...prev, ...res.translations }));
|
|
3261
|
+
}
|
|
3262
|
+
} catch (err) {
|
|
3263
|
+
console.error("Batch translation error:", err);
|
|
3264
|
+
setError(err?.message || "Failed to translate all languages.");
|
|
3265
|
+
} finally {
|
|
3266
|
+
setIsTranslatingAll(false);
|
|
3267
|
+
}
|
|
3243
3268
|
};
|
|
3244
|
-
const
|
|
3245
|
-
|
|
3246
|
-
|
|
3269
|
+
const handleSaveAll = async (e) => {
|
|
3270
|
+
e.preventDefault();
|
|
3271
|
+
e.stopPropagation();
|
|
3272
|
+
setSaving(true);
|
|
3273
|
+
setError(null);
|
|
3274
|
+
try {
|
|
3275
|
+
await onSave(values, contextHint);
|
|
3276
|
+
onClose();
|
|
3277
|
+
} catch (err) {
|
|
3278
|
+
console.error("Failed to save translations:", err);
|
|
3279
|
+
setError(err?.message || "Failed to save translations. Please try again.");
|
|
3280
|
+
} finally {
|
|
3281
|
+
setSaving(false);
|
|
3282
|
+
}
|
|
3247
3283
|
};
|
|
3248
|
-
const
|
|
3249
|
-
|
|
3284
|
+
const handleClose = (_, reason) => {
|
|
3285
|
+
if (reason === "backdropClick" && isBusy) return;
|
|
3286
|
+
setError(null);
|
|
3287
|
+
onClose();
|
|
3250
3288
|
};
|
|
3251
|
-
const
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
backgroundPaper: "#ffffff",
|
|
3287
|
-
textPrimary: "#0f172a",
|
|
3288
|
-
textSecondary: "#475569",
|
|
3289
|
-
success: "#2e7d32",
|
|
3290
|
-
error: "#d32f2f",
|
|
3291
|
-
warning: "#ed6c02",
|
|
3292
|
-
info: "#0288d1"
|
|
3293
|
-
};
|
|
3294
|
-
var DEFAULT_DARK_COLORS = {
|
|
3295
|
-
primary: "#90caf9",
|
|
3296
|
-
secondary: "#ce93d8",
|
|
3297
|
-
backgroundDefault: "#0f172a",
|
|
3298
|
-
backgroundPaper: "#1e293b",
|
|
3299
|
-
textPrimary: "#f8fafc",
|
|
3300
|
-
textSecondary: "#94a3b8",
|
|
3301
|
-
success: "#66bb6a",
|
|
3302
|
-
error: "#f44336",
|
|
3303
|
-
warning: "#ffa726",
|
|
3304
|
-
info: "#29b6f6"
|
|
3305
|
-
};
|
|
3306
|
-
var INITIAL_THEME_CONFIG = {
|
|
3307
|
-
name: "Base MUI Theme",
|
|
3308
|
-
mode: "light",
|
|
3309
|
-
borderRadius: 12,
|
|
3310
|
-
colors: {
|
|
3311
|
-
light: DEFAULT_LIGHT_COLORS,
|
|
3312
|
-
dark: DEFAULT_DARK_COLORS
|
|
3313
|
-
},
|
|
3314
|
-
typography: {
|
|
3315
|
-
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
|
|
3316
|
-
fontSize: 14,
|
|
3317
|
-
fontWeightLight: 300,
|
|
3318
|
-
fontWeightRegular: 400,
|
|
3319
|
-
fontWeightMedium: 600,
|
|
3320
|
-
fontWeightBold: 700
|
|
3321
|
-
}
|
|
3322
|
-
};
|
|
3323
|
-
var buildMuiTheme = (config) => {
|
|
3324
|
-
const currentMode = config.mode;
|
|
3325
|
-
const currentColors = config.colors?.[currentMode] || (currentMode === "light" ? DEFAULT_LIGHT_COLORS : DEFAULT_DARK_COLORS);
|
|
3326
|
-
return createTheme({
|
|
3327
|
-
palette: {
|
|
3328
|
-
mode: currentMode,
|
|
3329
|
-
primary: {
|
|
3330
|
-
main: currentColors.primary,
|
|
3331
|
-
light: currentMode === "light" ? "#63a4ff" : "#a1d2fa",
|
|
3332
|
-
dark: currentMode === "light" ? "#004ba0" : "#42a5f5"
|
|
3333
|
-
},
|
|
3334
|
-
secondary: {
|
|
3335
|
-
main: currentColors.secondary
|
|
3336
|
-
},
|
|
3337
|
-
background: {
|
|
3338
|
-
default: currentColors.backgroundDefault,
|
|
3339
|
-
paper: currentColors.backgroundPaper
|
|
3340
|
-
},
|
|
3341
|
-
text: {
|
|
3342
|
-
primary: currentColors.textPrimary,
|
|
3343
|
-
secondary: currentColors.textSecondary || (currentMode === "light" ? "#475569" : "#94a3b8")
|
|
3344
|
-
},
|
|
3345
|
-
success: { main: currentColors.success },
|
|
3346
|
-
error: { main: currentColors.error },
|
|
3347
|
-
warning: { main: currentColors.warning },
|
|
3348
|
-
info: { main: currentColors.info }
|
|
3349
|
-
},
|
|
3350
|
-
shape: {
|
|
3351
|
-
borderRadius: config.borderRadius
|
|
3352
|
-
},
|
|
3353
|
-
typography: {
|
|
3354
|
-
fontFamily: config.typography?.fontFamily || INITIAL_THEME_CONFIG.typography.fontFamily,
|
|
3355
|
-
fontSize: config.typography?.fontSize || INITIAL_THEME_CONFIG.typography.fontSize,
|
|
3356
|
-
fontWeightLight: config.typography?.fontWeightLight || INITIAL_THEME_CONFIG.typography.fontWeightLight,
|
|
3357
|
-
fontWeightRegular: config.typography?.fontWeightRegular || INITIAL_THEME_CONFIG.typography.fontWeightRegular,
|
|
3358
|
-
fontWeightMedium: config.typography?.fontWeightMedium || INITIAL_THEME_CONFIG.typography.fontWeightMedium,
|
|
3359
|
-
fontWeightBold: config.typography?.fontWeightBold || INITIAL_THEME_CONFIG.typography.fontWeightBold,
|
|
3360
|
-
h5: { fontWeight: 700, fontSize: "1.25rem" },
|
|
3361
|
-
subtitle1: { fontWeight: 600, fontSize: "0.95rem" },
|
|
3362
|
-
body2: { fontSize: "0.85rem" }
|
|
3363
|
-
},
|
|
3364
|
-
components: {
|
|
3365
|
-
MuiAutocomplete: {
|
|
3366
|
-
defaultProps: {
|
|
3367
|
-
noOptionsText: "\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E"
|
|
3368
|
-
}
|
|
3369
|
-
},
|
|
3370
|
-
MuiListItem: {
|
|
3371
|
-
styleOverrides: {
|
|
3372
|
-
root: {
|
|
3373
|
-
transition: "all 0.2s ease-in-out",
|
|
3374
|
-
"&:hover": {
|
|
3375
|
-
backgroundColor: currentMode === "light" ? "#f1f5f9" : "#334155"
|
|
3289
|
+
const isBusy = loading || saving || isTranslatingAll || translatingCode !== null;
|
|
3290
|
+
return /* @__PURE__ */ jsxs3(
|
|
3291
|
+
Dialog,
|
|
3292
|
+
{
|
|
3293
|
+
open,
|
|
3294
|
+
onClose: handleClose,
|
|
3295
|
+
fullWidth: true,
|
|
3296
|
+
maxWidth: "sm",
|
|
3297
|
+
onClick: (e) => e.stopPropagation(),
|
|
3298
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
3299
|
+
children: [
|
|
3300
|
+
/* @__PURE__ */ jsxs3(DialogTitle, { sx: { pb: 1, display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
|
|
3301
|
+
/* @__PURE__ */ jsxs3(Box, { children: [
|
|
3302
|
+
"Context Translation",
|
|
3303
|
+
/* @__PURE__ */ jsxs3(Typography, { component: "div", variant: "subtitle2", color: "text.secondary", sx: { wordBreak: "break-all", mt: 0.5 }, children: [
|
|
3304
|
+
"Original: ",
|
|
3305
|
+
/* @__PURE__ */ jsxs3("strong", { children: [
|
|
3306
|
+
'"',
|
|
3307
|
+
translationKey,
|
|
3308
|
+
'"'
|
|
3309
|
+
] })
|
|
3310
|
+
] })
|
|
3311
|
+
] }),
|
|
3312
|
+
!loading && /* @__PURE__ */ jsx10(
|
|
3313
|
+
Button,
|
|
3314
|
+
{
|
|
3315
|
+
type: "button",
|
|
3316
|
+
size: "small",
|
|
3317
|
+
variant: "outlined",
|
|
3318
|
+
color: "secondary",
|
|
3319
|
+
startIcon: isTranslatingAll ? /* @__PURE__ */ jsx10(CircularProgress, { size: 16, color: "inherit" }) : /* @__PURE__ */ jsx10(TranslateIcon, { fontSize: "small" }),
|
|
3320
|
+
onClick: handleTranslateAll,
|
|
3321
|
+
disabled: isBusy,
|
|
3322
|
+
sx: { flexShrink: 0, ml: 2, whiteSpace: "nowrap" },
|
|
3323
|
+
children: isTranslatingAll ? "Translating..." : "Translate All"
|
|
3376
3324
|
}
|
|
3377
|
-
|
|
3378
|
-
}
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3325
|
+
)
|
|
3326
|
+
] }),
|
|
3327
|
+
/* @__PURE__ */ jsxs3(DialogContent, { dividers: true, sx: { pt: 2 }, children: [
|
|
3328
|
+
error && /* @__PURE__ */ jsx10(Alert2, { severity: "error", onClose: () => setError(null), sx: { mb: 2 }, children: error }),
|
|
3329
|
+
loading ? /* @__PURE__ */ jsx10(Box, { sx: { display: "flex", justifyContent: "center", py: 4 }, children: /* @__PURE__ */ jsx10(CircularProgress, {}) }) : /* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", flexDirection: "column", gap: 2.5 }, children: [
|
|
3330
|
+
/* @__PURE__ */ jsxs3(Paper, { variant: "outlined", sx: { p: 2, bgcolor: "action.hover", borderRadius: 2 }, children: [
|
|
3331
|
+
/* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
|
|
3332
|
+
/* @__PURE__ */ jsx10(LightbulbIcon, { color: "warning", fontSize: "small" }),
|
|
3333
|
+
/* @__PURE__ */ jsx10(Typography, { variant: "subtitle2", sx: { fontWeight: 600 }, children: "Context Hint / Clarification" })
|
|
3334
|
+
] }),
|
|
3335
|
+
/* @__PURE__ */ jsx10(
|
|
3336
|
+
TextField,
|
|
3337
|
+
{
|
|
3338
|
+
fullWidth: true,
|
|
3339
|
+
multiline: true,
|
|
3340
|
+
rows: 2,
|
|
3341
|
+
size: "small",
|
|
3342
|
+
value: contextHint,
|
|
3343
|
+
onChange: (e) => setContextHint(e.target.value),
|
|
3344
|
+
placeholder: "e.g. Website navigation menu link to the home page",
|
|
3345
|
+
helperText: "This hint explains the exact meaning to AI translation models.",
|
|
3346
|
+
disabled: isBusy
|
|
3347
|
+
}
|
|
3348
|
+
)
|
|
3349
|
+
] }),
|
|
3350
|
+
languages.map((lang) => /* @__PURE__ */ jsxs3(Box, { sx: { display: "flex", alignItems: "center", gap: 2 }, children: [
|
|
3351
|
+
/* @__PURE__ */ jsx10(
|
|
3352
|
+
Box,
|
|
3353
|
+
{
|
|
3354
|
+
sx: {
|
|
3355
|
+
width: 32,
|
|
3356
|
+
height: 22,
|
|
3357
|
+
flexShrink: 0,
|
|
3358
|
+
borderRadius: "4px",
|
|
3359
|
+
overflow: "hidden",
|
|
3360
|
+
boxShadow: 1,
|
|
3361
|
+
"& svg": { width: "100%", height: "100%", display: "block" }
|
|
3362
|
+
},
|
|
3363
|
+
dangerouslySetInnerHTML: { __html: lang.svg_flag }
|
|
3364
|
+
}
|
|
3365
|
+
),
|
|
3366
|
+
/* @__PURE__ */ jsx10(
|
|
3367
|
+
TextField,
|
|
3368
|
+
{
|
|
3369
|
+
fullWidth: true,
|
|
3370
|
+
label: lang.name,
|
|
3371
|
+
size: "small",
|
|
3372
|
+
value: values[lang.code] || "",
|
|
3373
|
+
onChange: (e) => setValues((prev) => ({ ...prev, [lang.code]: e.target.value })),
|
|
3374
|
+
placeholder: "Enter manual translation...",
|
|
3375
|
+
disabled: isBusy
|
|
3376
|
+
}
|
|
3377
|
+
),
|
|
3378
|
+
/* @__PURE__ */ jsx10(Tooltip, { title: "Translate with context hint", children: /* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10(
|
|
3379
|
+
IconButton,
|
|
3380
|
+
{
|
|
3381
|
+
type: "button",
|
|
3382
|
+
color: "primary",
|
|
3383
|
+
disabled: isBusy,
|
|
3384
|
+
onClick: (e) => handleAutoTranslate(e, lang.code),
|
|
3385
|
+
children: translatingCode === lang.code ? /* @__PURE__ */ jsx10(CircularProgress, { size: 20, color: "inherit" }) : /* @__PURE__ */ jsx10(AutoFixHighIcon, { fontSize: "small" })
|
|
3386
|
+
}
|
|
3387
|
+
) }) })
|
|
3388
|
+
] }, lang.code))
|
|
3389
|
+
] })
|
|
3390
|
+
] }),
|
|
3391
|
+
/* @__PURE__ */ jsxs3(DialogActions, { sx: { px: 3, py: 2 }, children: [
|
|
3392
|
+
/* @__PURE__ */ jsx10(Button, { onClick: onClose, disabled: isBusy, color: "inherit", type: "button", children: "Cancel" }),
|
|
3393
|
+
/* @__PURE__ */ jsx10(
|
|
3394
|
+
Button,
|
|
3395
|
+
{
|
|
3396
|
+
onClick: handleSaveAll,
|
|
3397
|
+
variant: "contained",
|
|
3398
|
+
disabled: isBusy,
|
|
3399
|
+
type: "button",
|
|
3400
|
+
children: saving ? "Saving..." : "Save All"
|
|
3386
3401
|
}
|
|
3387
|
-
|
|
3388
|
-
}
|
|
3389
|
-
|
|
3390
|
-
MuiButton: {
|
|
3391
|
-
styleOverrides: {
|
|
3392
|
-
root: {
|
|
3393
|
-
textTransform: "none",
|
|
3394
|
-
fontWeight: 600,
|
|
3395
|
-
borderRadius: `${config.borderRadius}px`,
|
|
3396
|
-
padding: "10px 24px",
|
|
3397
|
-
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
|
3398
|
-
}
|
|
3399
|
-
}
|
|
3400
|
-
},
|
|
3401
|
-
MuiPaper: {
|
|
3402
|
-
styleOverrides: {
|
|
3403
|
-
rounded: {
|
|
3404
|
-
borderRadius: `${config.borderRadius * 1.25}px`
|
|
3405
|
-
}
|
|
3406
|
-
}
|
|
3407
|
-
}
|
|
3402
|
+
)
|
|
3403
|
+
] })
|
|
3404
|
+
]
|
|
3408
3405
|
}
|
|
3409
|
-
|
|
3406
|
+
);
|
|
3410
3407
|
};
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
var
|
|
3408
|
+
|
|
3409
|
+
// src/i18n/TranslateText.tsx
|
|
3410
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3411
|
+
var TranslateText = ({
|
|
3412
|
+
overrideNamespace,
|
|
3413
|
+
children,
|
|
3414
|
+
values
|
|
3415
|
+
}) => {
|
|
3416
|
+
const namespace = useTranslationNamespace(overrideNamespace);
|
|
3417
|
+
const { t: t2, i18n } = useTranslation2(namespace);
|
|
3418
|
+
const { isTranslationMode, canEdit } = useTranslationEdit();
|
|
3415
3419
|
const supabase = useSupabase();
|
|
3416
|
-
const
|
|
3417
|
-
const
|
|
3418
|
-
const
|
|
3419
|
-
|
|
3420
|
-
if (saved) {
|
|
3421
|
-
try {
|
|
3422
|
-
return JSON.parse(saved);
|
|
3423
|
-
} catch (e) {
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
return INITIAL_THEME_CONFIG;
|
|
3427
|
-
});
|
|
3428
|
-
useEffect6(() => {
|
|
3429
|
-
if (isAuthLoading) return;
|
|
3430
|
-
if (isAuthenticated && userId) {
|
|
3431
|
-
const fetchActiveTheme = async () => {
|
|
3432
|
-
try {
|
|
3433
|
-
const { data, error } = await supabase.from("user_themes").select("*").eq("user_id", userId).eq("is_active", true).maybeSingle();
|
|
3434
|
-
if (error) {
|
|
3435
|
-
console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0435 \u0442\u0435\u043C\u044B \u0438\u0437 Supabase:", error.message);
|
|
3436
|
-
return;
|
|
3437
|
-
}
|
|
3438
|
-
if (data) {
|
|
3439
|
-
const activeTheme = data;
|
|
3440
|
-
const savedMode = localStorage.getItem(THEME_MODE_STORAGE_KEY);
|
|
3441
|
-
setThemeConfig({
|
|
3442
|
-
id: activeTheme.id,
|
|
3443
|
-
name: activeTheme.name || INITIAL_THEME_CONFIG.name,
|
|
3444
|
-
mode: savedMode || activeTheme.config?.mode || "light",
|
|
3445
|
-
borderRadius: activeTheme.config?.borderRadius ?? INITIAL_THEME_CONFIG.borderRadius,
|
|
3446
|
-
colors: activeTheme.config?.colors || INITIAL_THEME_CONFIG.colors,
|
|
3447
|
-
typography: activeTheme.config?.typography || INITIAL_THEME_CONFIG.typography,
|
|
3448
|
-
updatedAt: activeTheme.updated_at || void 0
|
|
3449
|
-
});
|
|
3450
|
-
}
|
|
3451
|
-
} catch (err) {
|
|
3452
|
-
console.error("\u041D\u0435\u043F\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0437\u0430\u043F\u0440\u043E\u0441\u0435 \u0442\u0435\u043C\u044B:", err);
|
|
3453
|
-
}
|
|
3454
|
-
};
|
|
3455
|
-
fetchActiveTheme();
|
|
3456
|
-
} else {
|
|
3457
|
-
setThemeConfig(INITIAL_THEME_CONFIG);
|
|
3458
|
-
localStorage.removeItem(THEME_STORAGE_KEY);
|
|
3459
|
-
localStorage.removeItem(THEME_MODE_STORAGE_KEY);
|
|
3460
|
-
}
|
|
3461
|
-
}, [isAuthenticated, isAuthLoading, userId]);
|
|
3420
|
+
const [anchorEl, setAnchorEl] = useState8(null);
|
|
3421
|
+
const [isDialogOpen, setIsDialogOpen] = useState8(false);
|
|
3422
|
+
const timeoutRef = useRef2(null);
|
|
3423
|
+
const translatedValue = t2(children, values);
|
|
3462
3424
|
useEffect6(() => {
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3425
|
+
return () => {
|
|
3426
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
3427
|
+
};
|
|
3428
|
+
}, []);
|
|
3429
|
+
if (!canEdit || !isTranslationMode) {
|
|
3430
|
+
return /* @__PURE__ */ jsx11(Fragment, { children: translatedValue });
|
|
3431
|
+
}
|
|
3432
|
+
const handleSaveTranslations = async (newValues, contextHint) => {
|
|
3433
|
+
const upsertData = Object.entries(newValues).map(([langCode, text]) => ({
|
|
3434
|
+
lang_code: langCode,
|
|
3435
|
+
namespace,
|
|
3436
|
+
key: children,
|
|
3437
|
+
value: text,
|
|
3438
|
+
context_hint: contextHint
|
|
3439
|
+
}));
|
|
3440
|
+
const { error } = await supabase.from("translations").upsert(upsertData, { onConflict: "lang_code,namespace,key" });
|
|
3441
|
+
if (error) {
|
|
3442
|
+
alert("\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445");
|
|
3443
|
+
throw error;
|
|
3466
3444
|
}
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3445
|
+
await translationService.incrementVersion(supabase, namespace, Object.keys(newValues));
|
|
3446
|
+
Object.entries(newValues).forEach(([langCode, text]) => {
|
|
3447
|
+
i18n.addResource(langCode, namespace, children, text);
|
|
3448
|
+
});
|
|
3471
3449
|
};
|
|
3472
|
-
const
|
|
3473
|
-
|
|
3450
|
+
const handleMouseEnter = (event) => {
|
|
3451
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
3452
|
+
setAnchorEl(event.currentTarget);
|
|
3474
3453
|
};
|
|
3475
|
-
const
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
isDesktop: useMediaQuery(theme.breakpoints.between("md", "lg")),
|
|
3480
|
-
isWideDesktop: useMediaQuery(theme.breakpoints.between("lg", "xl")),
|
|
3481
|
-
isSuperWideDesktop: useMediaQuery(theme.breakpoints.up("xl"))
|
|
3454
|
+
const handleMouseLeave = () => {
|
|
3455
|
+
timeoutRef.current = setTimeout(() => {
|
|
3456
|
+
setAnchorEl(null);
|
|
3457
|
+
}, 200);
|
|
3482
3458
|
};
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
throw new Error("useAppTheme should be used within the ThemeProvider");
|
|
3492
|
-
}
|
|
3493
|
-
return context;
|
|
3494
|
-
};
|
|
3495
|
-
|
|
3496
|
-
// src/context/BreadcrumbProvider.tsx
|
|
3497
|
-
import { createContext as createContext8, useContext as useContext8, useState as useState7 } from "react";
|
|
3498
|
-
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
3499
|
-
var BreadcrumbContext = createContext8({
|
|
3500
|
-
breadcrumbTitles: {},
|
|
3501
|
-
setBreadcrumbTitle: () => {
|
|
3502
|
-
}
|
|
3503
|
-
});
|
|
3504
|
-
var BreadcrumbProvider = ({ children }) => {
|
|
3505
|
-
const [breadcrumbTitles, setBreadcrumbTitles] = useState7({});
|
|
3506
|
-
const setBreadcrumbTitle = (path, title) => {
|
|
3507
|
-
setBreadcrumbTitles((prev) => {
|
|
3508
|
-
if (prev[path] === title) return prev;
|
|
3509
|
-
return { ...prev, [path]: title };
|
|
3510
|
-
});
|
|
3459
|
+
const handleOpenDialog = (e) => {
|
|
3460
|
+
e.preventDefault();
|
|
3461
|
+
e.stopPropagation();
|
|
3462
|
+
setAnchorEl(null);
|
|
3463
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
3464
|
+
document.activeElement.blur();
|
|
3465
|
+
}
|
|
3466
|
+
setIsDialogOpen(true);
|
|
3511
3467
|
};
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3520
|
-
var ToastContext = createContext9(void 0);
|
|
3521
|
-
var ToastProvider = ({ children }) => {
|
|
3522
|
-
const [toast, setToast] = useState8(null);
|
|
3523
|
-
const showToast = useCallback3((text, type = "info") => {
|
|
3524
|
-
setToast({ text, type });
|
|
3525
|
-
}, []);
|
|
3526
|
-
const hideToast = useCallback3(() => {
|
|
3527
|
-
setToast(null);
|
|
3528
|
-
}, []);
|
|
3529
|
-
const contextValue = useMemo5(() => ({
|
|
3530
|
-
showToast,
|
|
3531
|
-
hideToast
|
|
3532
|
-
}), [showToast, hideToast]);
|
|
3533
|
-
return /* @__PURE__ */ jsxs4(ToastContext.Provider, { value: contextValue, children: [
|
|
3534
|
-
children,
|
|
3535
|
-
/* @__PURE__ */ jsx12(
|
|
3536
|
-
Snackbar,
|
|
3468
|
+
const handleLaunchAction = (_) => {
|
|
3469
|
+
setAnchorEl(null);
|
|
3470
|
+
};
|
|
3471
|
+
const open = Boolean(anchorEl);
|
|
3472
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
3473
|
+
/* @__PURE__ */ jsx11(
|
|
3474
|
+
Box2,
|
|
3537
3475
|
{
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3476
|
+
component: "span",
|
|
3477
|
+
onMouseEnter: handleMouseEnter,
|
|
3478
|
+
onMouseLeave: handleMouseLeave,
|
|
3479
|
+
onClick: (e) => {
|
|
3480
|
+
e.preventDefault();
|
|
3481
|
+
e.stopPropagation();
|
|
3482
|
+
},
|
|
3483
|
+
sx: {
|
|
3484
|
+
display: "inline-block",
|
|
3485
|
+
position: "relative",
|
|
3486
|
+
borderRadius: "4px",
|
|
3487
|
+
padding: "0 3px",
|
|
3488
|
+
margin: "0 1px",
|
|
3489
|
+
border: "1px dashed #f59e0b",
|
|
3490
|
+
backgroundColor: "rgba(254, 243, 199, 0.35)",
|
|
3491
|
+
cursor: "pointer",
|
|
3492
|
+
transition: "all 0.2s ease-in-out",
|
|
3493
|
+
userSelect: "none",
|
|
3494
|
+
"&:hover": {
|
|
3495
|
+
backgroundColor: "#fef08a",
|
|
3496
|
+
borderColor: "#d97706",
|
|
3497
|
+
boxShadow: "0 0 0 2px rgba(245, 158, 11, 0.35)",
|
|
3498
|
+
transform: "scale(1.02)"
|
|
3499
|
+
}
|
|
3500
|
+
},
|
|
3501
|
+
children: translatedValue
|
|
3502
|
+
}
|
|
3503
|
+
),
|
|
3504
|
+
/* @__PURE__ */ jsx11(
|
|
3505
|
+
Popper,
|
|
3506
|
+
{
|
|
3507
|
+
open,
|
|
3508
|
+
anchorEl,
|
|
3509
|
+
placement: "bottom-start",
|
|
3510
|
+
style: { zIndex: 1300 },
|
|
3511
|
+
children: /* @__PURE__ */ jsx11(
|
|
3512
|
+
Paper2,
|
|
3544
3513
|
{
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3514
|
+
elevation: 4,
|
|
3515
|
+
onMouseEnter: () => {
|
|
3516
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
3517
|
+
},
|
|
3518
|
+
onMouseLeave: handleMouseLeave,
|
|
3519
|
+
sx: {
|
|
3520
|
+
mt: 0.5,
|
|
3521
|
+
borderRadius: 2,
|
|
3522
|
+
minWidth: 140,
|
|
3523
|
+
boxShadow: "0px 10px 15px -3px rgba(0,0,0,0.1)"
|
|
3524
|
+
},
|
|
3525
|
+
children: /* @__PURE__ */ jsxs4(MenuList, { dense: true, sx: { py: 0.5 }, children: [
|
|
3526
|
+
/* @__PURE__ */ jsxs4(MenuItem, { onClick: handleOpenDialog, sx: { py: 0.8, px: 1.5 }, children: [
|
|
3527
|
+
/* @__PURE__ */ jsx11(ListItemIcon, { sx: { minWidth: "28px !important" }, children: /* @__PURE__ */ jsx11(TranslateIcon2, { fontSize: "small", sx: { color: "#d97706" } }) }),
|
|
3528
|
+
/* @__PURE__ */ jsx11(
|
|
3529
|
+
ListItemText,
|
|
3530
|
+
{
|
|
3531
|
+
primary: "Localization",
|
|
3532
|
+
slotProps: {
|
|
3533
|
+
primary: {
|
|
3534
|
+
sx: {
|
|
3535
|
+
fontSize: "0.825rem",
|
|
3536
|
+
fontWeight: 600,
|
|
3537
|
+
color: "text.primary"
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
)
|
|
3543
|
+
] }),
|
|
3544
|
+
/* @__PURE__ */ jsxs4(MenuItem, { onClick: handleLaunchAction, sx: { py: 0.8, px: 1.5 }, children: [
|
|
3545
|
+
/* @__PURE__ */ jsx11(ListItemIcon, { sx: { minWidth: "28px !important" }, children: /* @__PURE__ */ jsx11(LaunchIcon, { fontSize: "small", color: "secondary" }) }),
|
|
3546
|
+
/* @__PURE__ */ jsx11(
|
|
3547
|
+
ListItemText,
|
|
3548
|
+
{
|
|
3549
|
+
primary: "Launch action",
|
|
3550
|
+
slotProps: {
|
|
3551
|
+
primary: {
|
|
3552
|
+
sx: {
|
|
3553
|
+
fontSize: "0.825rem",
|
|
3554
|
+
fontWeight: 600,
|
|
3555
|
+
color: "text.primary"
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
)
|
|
3561
|
+
] })
|
|
3562
|
+
] })
|
|
3549
3563
|
}
|
|
3550
3564
|
)
|
|
3551
3565
|
}
|
|
3552
|
-
)
|
|
3566
|
+
),
|
|
3567
|
+
/* @__PURE__ */ jsx11("span", { style: { display: "inline" }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx11(
|
|
3568
|
+
TranslationDialog,
|
|
3569
|
+
{
|
|
3570
|
+
open: isDialogOpen,
|
|
3571
|
+
onClose: () => setIsDialogOpen(false),
|
|
3572
|
+
translationKey: children,
|
|
3573
|
+
namespace,
|
|
3574
|
+
onSave: handleSaveTranslations
|
|
3575
|
+
}
|
|
3576
|
+
) })
|
|
3553
3577
|
] });
|
|
3554
3578
|
};
|
|
3555
|
-
var useToast = () => {
|
|
3556
|
-
const context = useContext9(ToastContext);
|
|
3557
|
-
if (!context) {
|
|
3558
|
-
throw new Error("useToast must be used within a ToastProvider");
|
|
3559
|
-
}
|
|
3560
|
-
return context;
|
|
3561
|
-
};
|
|
3562
3579
|
|
|
3563
3580
|
// src/context/ConfirmationProvider.tsx
|
|
3564
|
-
import {
|
|
3565
|
-
import {
|
|
3566
|
-
Button as Button2,
|
|
3567
|
-
Dialog as Dialog2,
|
|
3568
|
-
DialogActions as DialogActions2,
|
|
3569
|
-
DialogContent as DialogContent2,
|
|
3570
|
-
DialogContentText,
|
|
3571
|
-
DialogTitle as DialogTitle2,
|
|
3572
|
-
Box as Box3
|
|
3573
|
-
} from "@mui/material";
|
|
3574
|
-
import {
|
|
3575
|
-
Error as ErrorIcon,
|
|
3576
|
-
WarningAmberOutlined as WarningIcon,
|
|
3577
|
-
InfoOutlined as InfoIcon,
|
|
3578
|
-
CheckCircleOutlined as SuccessIcon
|
|
3579
|
-
} from "@mui/icons-material";
|
|
3580
|
-
import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3581
|
+
import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3581
3582
|
var ConfirmContext = createContext10(null);
|
|
3582
3583
|
var SEVERITY_ICONS = {
|
|
3583
|
-
error: /* @__PURE__ */
|
|
3584
|
-
warning: /* @__PURE__ */
|
|
3585
|
-
info: /* @__PURE__ */
|
|
3586
|
-
success: /* @__PURE__ */
|
|
3584
|
+
error: /* @__PURE__ */ jsx12(ErrorIcon, { color: "error", sx: { fontSize: 40 } }),
|
|
3585
|
+
warning: /* @__PURE__ */ jsx12(WarningIcon, { color: "warning", sx: { fontSize: 40 } }),
|
|
3586
|
+
info: /* @__PURE__ */ jsx12(InfoIcon, { color: "info", sx: { fontSize: 40 } }),
|
|
3587
|
+
success: /* @__PURE__ */ jsx12(SuccessIcon, { color: "success", sx: { fontSize: 40 } })
|
|
3587
3588
|
};
|
|
3588
3589
|
var ConfirmationProvider = ({ children }) => {
|
|
3589
3590
|
const [open, setOpen] = useState9(false);
|
|
@@ -3617,36 +3618,36 @@ var ConfirmationProvider = ({ children }) => {
|
|
|
3617
3618
|
children: [
|
|
3618
3619
|
/* @__PURE__ */ jsxs5(DialogTitle2, { sx: { display: "flex", alignItems: "center", gap: 1.5, pb: 1 }, children: [
|
|
3619
3620
|
SEVERITY_ICONS[severityType],
|
|
3620
|
-
/* @__PURE__ */
|
|
3621
|
+
/* @__PURE__ */ jsx12(Box3, { component: "span", sx: { fontWeight: 600 }, children: options?.title || /* @__PURE__ */ jsx12(TranslateText, { children: "Confirmation" }) })
|
|
3621
3622
|
] }),
|
|
3622
|
-
/* @__PURE__ */
|
|
3623
|
+
/* @__PURE__ */ jsx12(DialogContent2, { sx: { py: 2 }, dividers: true, children: /* @__PURE__ */ jsx12(DialogContentText, { color: "text.secondary", children: options?.message }) }),
|
|
3623
3624
|
/* @__PURE__ */ jsxs5(DialogActions2, { sx: { pt: 2, px: 2, justifyContent: "flex-end", gap: 1 }, children: [
|
|
3624
|
-
(options?.showCancelButton ?? true) && /* @__PURE__ */
|
|
3625
|
+
(options?.showCancelButton ?? true) && /* @__PURE__ */ jsx12(
|
|
3625
3626
|
Button2,
|
|
3626
3627
|
{
|
|
3627
3628
|
onClick: () => handleClose("cancel"),
|
|
3628
3629
|
color: "inherit",
|
|
3629
3630
|
variant: "text",
|
|
3630
|
-
children: options?.cancelText || /* @__PURE__ */
|
|
3631
|
+
children: options?.cancelText || /* @__PURE__ */ jsx12(TranslateText, { children: "Cancel" })
|
|
3631
3632
|
}
|
|
3632
3633
|
),
|
|
3633
|
-
options?.showNoButton && /* @__PURE__ */
|
|
3634
|
+
options?.showNoButton && /* @__PURE__ */ jsx12(
|
|
3634
3635
|
Button2,
|
|
3635
3636
|
{
|
|
3636
3637
|
onClick: () => handleClose("no"),
|
|
3637
3638
|
color: "inherit",
|
|
3638
3639
|
variant: "outlined",
|
|
3639
|
-
children: options?.noText || /* @__PURE__ */
|
|
3640
|
+
children: options?.noText || /* @__PURE__ */ jsx12(TranslateText, { children: "No" })
|
|
3640
3641
|
}
|
|
3641
3642
|
),
|
|
3642
|
-
/* @__PURE__ */
|
|
3643
|
+
/* @__PURE__ */ jsx12(
|
|
3643
3644
|
Button2,
|
|
3644
3645
|
{
|
|
3645
3646
|
onClick: () => handleClose("yes"),
|
|
3646
3647
|
color: options?.confirmColor || (severityType === "error" ? "error" : "primary"),
|
|
3647
3648
|
variant: "contained",
|
|
3648
3649
|
autoFocus: true,
|
|
3649
|
-
children: options?.confirmText || /* @__PURE__ */
|
|
3650
|
+
children: options?.confirmText || /* @__PURE__ */ jsx12(TranslateText, { children: "Yes" })
|
|
3650
3651
|
}
|
|
3651
3652
|
)
|
|
3652
3653
|
] })
|
|
@@ -3677,28 +3678,26 @@ var Providers = {
|
|
|
3677
3678
|
};
|
|
3678
3679
|
|
|
3679
3680
|
// src/UI/components/data.display/UserStatusChipComponent.tsx
|
|
3680
|
-
import {
|
|
3681
|
-
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
|
3682
|
-
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3681
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
3683
3682
|
var UserStatusChipComponent = ({ overrideNamespace, state }) => {
|
|
3684
3683
|
const confirm = useConfirmation();
|
|
3685
3684
|
const handleShowReason = async (e) => {
|
|
3686
3685
|
e.stopPropagation();
|
|
3687
3686
|
if (!state.locked_reason) return;
|
|
3688
3687
|
await confirm({
|
|
3689
|
-
title: /* @__PURE__ */
|
|
3688
|
+
title: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Lock Reason" }),
|
|
3690
3689
|
message: state.locked_reason,
|
|
3691
3690
|
type: "info",
|
|
3692
|
-
confirmText: /* @__PURE__ */
|
|
3691
|
+
confirmText: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "OK" }),
|
|
3693
3692
|
showNoButton: false,
|
|
3694
3693
|
showCancelButton: false
|
|
3695
3694
|
});
|
|
3696
3695
|
};
|
|
3697
3696
|
if (state.profile_archived) {
|
|
3698
|
-
return /* @__PURE__ */
|
|
3697
|
+
return /* @__PURE__ */ jsx13(
|
|
3699
3698
|
Chip,
|
|
3700
3699
|
{
|
|
3701
|
-
label: /* @__PURE__ */
|
|
3700
|
+
label: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Archived" }),
|
|
3702
3701
|
color: "default",
|
|
3703
3702
|
size: "small",
|
|
3704
3703
|
variant: "filled"
|
|
@@ -3706,10 +3705,10 @@ var UserStatusChipComponent = ({ overrideNamespace, state }) => {
|
|
|
3706
3705
|
);
|
|
3707
3706
|
}
|
|
3708
3707
|
if (state.profile_locked) {
|
|
3709
|
-
return /* @__PURE__ */
|
|
3708
|
+
return /* @__PURE__ */ jsx13(
|
|
3710
3709
|
Chip,
|
|
3711
3710
|
{
|
|
3712
|
-
label: /* @__PURE__ */
|
|
3711
|
+
label: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "System Locked" }),
|
|
3713
3712
|
color: "error",
|
|
3714
3713
|
size: "small",
|
|
3715
3714
|
variant: "filled"
|
|
@@ -3717,30 +3716,30 @@ var UserStatusChipComponent = ({ overrideNamespace, state }) => {
|
|
|
3717
3716
|
);
|
|
3718
3717
|
}
|
|
3719
3718
|
if (state.excluded) {
|
|
3720
|
-
return /* @__PURE__ */
|
|
3719
|
+
return /* @__PURE__ */ jsx13(
|
|
3721
3720
|
Chip,
|
|
3722
3721
|
{
|
|
3723
|
-
label: /* @__PURE__ */
|
|
3722
|
+
label: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Excluded" }),
|
|
3724
3723
|
size: "small"
|
|
3725
3724
|
}
|
|
3726
3725
|
);
|
|
3727
3726
|
}
|
|
3728
3727
|
if (state.locked) {
|
|
3729
|
-
return /* @__PURE__ */
|
|
3728
|
+
return /* @__PURE__ */ jsx13(
|
|
3730
3729
|
Chip,
|
|
3731
3730
|
{
|
|
3732
|
-
label: /* @__PURE__ */
|
|
3731
|
+
label: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Locked" }),
|
|
3733
3732
|
color: "error",
|
|
3734
3733
|
size: "small",
|
|
3735
3734
|
onDelete: state.locked_reason ? handleShowReason : void 0,
|
|
3736
|
-
deleteIcon: state.locked_reason ? /* @__PURE__ */
|
|
3735
|
+
deleteIcon: state.locked_reason ? /* @__PURE__ */ jsx13(IconButton2, { size: "small", component: "span", sx: { p: 0.2 }, children: /* @__PURE__ */ jsx13(InfoOutlinedIcon, { fontSize: "small" }) }) : void 0
|
|
3737
3736
|
}
|
|
3738
3737
|
);
|
|
3739
3738
|
}
|
|
3740
|
-
return /* @__PURE__ */
|
|
3739
|
+
return /* @__PURE__ */ jsx13(
|
|
3741
3740
|
Chip,
|
|
3742
3741
|
{
|
|
3743
|
-
label: /* @__PURE__ */
|
|
3742
|
+
label: /* @__PURE__ */ jsx13(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Active" }),
|
|
3744
3743
|
color: "success",
|
|
3745
3744
|
size: "small",
|
|
3746
3745
|
variant: "outlined"
|
|
@@ -3750,32 +3749,32 @@ var UserStatusChipComponent = ({ overrideNamespace, state }) => {
|
|
|
3750
3749
|
|
|
3751
3750
|
// src/UI/components/data.display/UserRoleChipComponent.tsx
|
|
3752
3751
|
import { Chip as Chip2 } from "@mui/material";
|
|
3753
|
-
import { jsx as
|
|
3752
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3754
3753
|
var UserRoleChipComponent = ({ overrideNamespace, role }) => {
|
|
3755
3754
|
switch (role) {
|
|
3756
3755
|
case "superadmin":
|
|
3757
|
-
return /* @__PURE__ */
|
|
3756
|
+
return /* @__PURE__ */ jsx14(
|
|
3758
3757
|
Chip2,
|
|
3759
3758
|
{
|
|
3760
|
-
label: /* @__PURE__ */
|
|
3759
|
+
label: /* @__PURE__ */ jsx14(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Superadministrator" }),
|
|
3761
3760
|
color: "error",
|
|
3762
3761
|
size: "small"
|
|
3763
3762
|
}
|
|
3764
3763
|
);
|
|
3765
3764
|
case "admin":
|
|
3766
|
-
return /* @__PURE__ */
|
|
3765
|
+
return /* @__PURE__ */ jsx14(
|
|
3767
3766
|
Chip2,
|
|
3768
3767
|
{
|
|
3769
|
-
label: /* @__PURE__ */
|
|
3768
|
+
label: /* @__PURE__ */ jsx14(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "Administrator" }),
|
|
3770
3769
|
color: "primary",
|
|
3771
3770
|
size: "small"
|
|
3772
3771
|
}
|
|
3773
3772
|
);
|
|
3774
3773
|
default:
|
|
3775
|
-
return /* @__PURE__ */
|
|
3774
|
+
return /* @__PURE__ */ jsx14(
|
|
3776
3775
|
Chip2,
|
|
3777
3776
|
{
|
|
3778
|
-
label: /* @__PURE__ */
|
|
3777
|
+
label: /* @__PURE__ */ jsx14(TranslateText, { overrideNamespace: overrideNamespace || "system_widgets", children: "User" }),
|
|
3779
3778
|
variant: "outlined",
|
|
3780
3779
|
size: "small"
|
|
3781
3780
|
}
|
|
@@ -3788,6 +3787,41 @@ import { useState as useState10 } from "react";
|
|
|
3788
3787
|
import { IconButton as IconButton3 } from "@mui/material";
|
|
3789
3788
|
import CheckIcon from "@mui/icons-material/Check";
|
|
3790
3789
|
import CopyIcon from "@mui/icons-material/ContentCopy";
|
|
3790
|
+
|
|
3791
|
+
// src/UI/components/data.display/TranslatableTooltipComponent.tsx
|
|
3792
|
+
import { Tooltip as Tooltip2 } from "@mui/material";
|
|
3793
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
3794
|
+
var TranslatableTooltipComponent = ({
|
|
3795
|
+
overrideNamespace,
|
|
3796
|
+
title,
|
|
3797
|
+
disableSpan,
|
|
3798
|
+
children,
|
|
3799
|
+
...props
|
|
3800
|
+
}) => {
|
|
3801
|
+
const { isTranslationMode } = useTranslationEdit();
|
|
3802
|
+
return /* @__PURE__ */ jsx15(
|
|
3803
|
+
Tooltip2,
|
|
3804
|
+
{
|
|
3805
|
+
title: typeof title === "string" ? /* @__PURE__ */ jsx15(TranslateText, { overrideNamespace, children: title }) : title,
|
|
3806
|
+
slotProps: {
|
|
3807
|
+
popper: {
|
|
3808
|
+
sx: isTranslationMode ? {
|
|
3809
|
+
pointerEvents: "auto",
|
|
3810
|
+
// Разрешаем клики внутри тултипа
|
|
3811
|
+
"& .MuiTooltip-tooltip": {
|
|
3812
|
+
pointerEvents: "auto"
|
|
3813
|
+
}
|
|
3814
|
+
} : void 0
|
|
3815
|
+
}
|
|
3816
|
+
},
|
|
3817
|
+
leaveDelay: isTranslationMode ? 1e3 : 100,
|
|
3818
|
+
...props,
|
|
3819
|
+
children: disableSpan ? children : /* @__PURE__ */ jsx15("span", { children })
|
|
3820
|
+
}
|
|
3821
|
+
);
|
|
3822
|
+
};
|
|
3823
|
+
|
|
3824
|
+
// src/UI/components/buttons/CopyClipboardButtonComponent.tsx
|
|
3791
3825
|
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
3792
3826
|
var CopyClipboardButtonComponent = ({
|
|
3793
3827
|
overrideNamespace,
|
|
@@ -3831,11 +3865,367 @@ var CopyClipboardButtonComponent = ({
|
|
|
3831
3865
|
) }) });
|
|
3832
3866
|
};
|
|
3833
3867
|
|
|
3868
|
+
// src/context/UserSettingsProvider.tsx
|
|
3869
|
+
import { createContext as createContext11, useContext as useContext11, useEffect as useEffect7, useState as useState11, useCallback as useCallback5 } from "react";
|
|
3870
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
3871
|
+
var UserSettingsContext = createContext11(void 0);
|
|
3872
|
+
var useUserSettings = () => {
|
|
3873
|
+
const context = useContext11(UserSettingsContext);
|
|
3874
|
+
if (!context) {
|
|
3875
|
+
throw new Error("useUserSettings should be used within the UserSettingsProvider");
|
|
3876
|
+
}
|
|
3877
|
+
return context;
|
|
3878
|
+
};
|
|
3879
|
+
|
|
3880
|
+
// src/UI/components/data.display/UserPopoverComponent.tsx
|
|
3881
|
+
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3882
|
+
var useUserStatus = (supabase, userId, open) => {
|
|
3883
|
+
const [lastSeenAt, setLastSeenAt] = useState12(null);
|
|
3884
|
+
const [loading, setLoading] = useState12(true);
|
|
3885
|
+
useEffect8(() => {
|
|
3886
|
+
if (!userId || !open) {
|
|
3887
|
+
setLastSeenAt(null);
|
|
3888
|
+
setLoading(false);
|
|
3889
|
+
return;
|
|
3890
|
+
}
|
|
3891
|
+
let isMounted = true;
|
|
3892
|
+
const loadStatus = async () => {
|
|
3893
|
+
setLoading(true);
|
|
3894
|
+
const date = await getUserLastSeenAt(supabase, userId);
|
|
3895
|
+
if (isMounted) {
|
|
3896
|
+
setLastSeenAt(date);
|
|
3897
|
+
setLoading(false);
|
|
3898
|
+
}
|
|
3899
|
+
};
|
|
3900
|
+
loadStatus();
|
|
3901
|
+
const channel = supabase.channel(`user_status_popover_${userId}`).on(
|
|
3902
|
+
"postgres_changes",
|
|
3903
|
+
{
|
|
3904
|
+
event: "*",
|
|
3905
|
+
schema: "public",
|
|
3906
|
+
table: "user_sessions",
|
|
3907
|
+
filter: `user_id=eq.${userId}`
|
|
3908
|
+
},
|
|
3909
|
+
() => {
|
|
3910
|
+
loadStatus();
|
|
3911
|
+
}
|
|
3912
|
+
).subscribe();
|
|
3913
|
+
return () => {
|
|
3914
|
+
isMounted = false;
|
|
3915
|
+
supabase.removeChannel(channel);
|
|
3916
|
+
};
|
|
3917
|
+
}, [userId, open]);
|
|
3918
|
+
const isOnline = Boolean(
|
|
3919
|
+
lastSeenAt && (/* @__PURE__ */ new Date()).getTime() - new Date(lastSeenAt).getTime() < 3 * 60 * 1e3
|
|
3920
|
+
);
|
|
3921
|
+
return { lastSeenAt, isOnline, loading };
|
|
3922
|
+
};
|
|
3923
|
+
var UserPopoverComponent = ({
|
|
3924
|
+
overrideNamespace,
|
|
3925
|
+
userId,
|
|
3926
|
+
anchorEl,
|
|
3927
|
+
open,
|
|
3928
|
+
onClose
|
|
3929
|
+
}) => {
|
|
3930
|
+
const supabase = useSupabase();
|
|
3931
|
+
const { activeCompanyId, membership: currentMembership } = useAuth();
|
|
3932
|
+
const { openChatWithUser } = useUserSettings();
|
|
3933
|
+
const { formatDate, formatRelativeTime } = useLocalization();
|
|
3934
|
+
const [profileData, setProfileData] = useState12(null);
|
|
3935
|
+
const [membershipData, setMembershipData] = useState12(null);
|
|
3936
|
+
const [avatarUrl, setAvatarUrl] = useState12(null);
|
|
3937
|
+
const [coverUrl, setCoverUrl] = useState12(null);
|
|
3938
|
+
const [loading, setLoading] = useState12(true);
|
|
3939
|
+
const { lastSeenAt, isOnline } = useUserStatus(supabase, userId, open);
|
|
3940
|
+
useEffect8(() => {
|
|
3941
|
+
if (!open || !userId || !activeCompanyId) return;
|
|
3942
|
+
let isMounted = true;
|
|
3943
|
+
setLoading(true);
|
|
3944
|
+
const fetchUserData = async () => {
|
|
3945
|
+
try {
|
|
3946
|
+
const { data, error } = await supabase.rpc("get_user_avatar_and_profile", {
|
|
3947
|
+
p_company_id: activeCompanyId,
|
|
3948
|
+
//currentMembership?.id || null,
|
|
3949
|
+
p_user_id: userId
|
|
3950
|
+
}).maybeSingle();
|
|
3951
|
+
if (error) throw error;
|
|
3952
|
+
if (isMounted && data) {
|
|
3953
|
+
const rpcData = data;
|
|
3954
|
+
const profile = rpcData.profile;
|
|
3955
|
+
const membership = rpcData.membership;
|
|
3956
|
+
setProfileData(profile);
|
|
3957
|
+
setMembershipData(membership);
|
|
3958
|
+
const avatarPath = profile?.avatar_path || profile?.avatar_url;
|
|
3959
|
+
if (avatarPath && !avatarPath.startsWith("http")) {
|
|
3960
|
+
const { data: avatarSigned } = await supabase.storage.from("user-media").createSignedUrl(avatarPath, 3600);
|
|
3961
|
+
if (isMounted) setAvatarUrl(avatarSigned?.signedUrl || null);
|
|
3962
|
+
} else {
|
|
3963
|
+
if (isMounted) setAvatarUrl(avatarPath || null);
|
|
3964
|
+
}
|
|
3965
|
+
const coverPath = profile?.cover_image_path || profile?.cover_image_url;
|
|
3966
|
+
if (coverPath && !coverPath.startsWith("http")) {
|
|
3967
|
+
const { data: coverSigned } = await supabase.storage.from("user-media").createSignedUrl(coverPath, 3600);
|
|
3968
|
+
if (isMounted) setCoverUrl(coverSigned?.signedUrl || null);
|
|
3969
|
+
} else {
|
|
3970
|
+
if (isMounted) setCoverUrl(coverPath || null);
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
} catch (err) {
|
|
3974
|
+
console.error("Error loading user popover data:", err);
|
|
3975
|
+
} finally {
|
|
3976
|
+
if (isMounted) setLoading(false);
|
|
3977
|
+
}
|
|
3978
|
+
};
|
|
3979
|
+
fetchUserData();
|
|
3980
|
+
return () => {
|
|
3981
|
+
isMounted = false;
|
|
3982
|
+
};
|
|
3983
|
+
}, [open, userId, currentMembership?.id, activeCompanyId]);
|
|
3984
|
+
const getStatusChip = () => {
|
|
3985
|
+
if (!profileData) return null;
|
|
3986
|
+
return /* @__PURE__ */ jsx18(UserStatusChipComponent, { state: { profile_archived: profileData.archived, profile_locked: profileData.locked, excluded: membershipData?.excluded || false, locked: membershipData?.locked || false, locked_reason: membershipData?.locked_reason || "" } });
|
|
3987
|
+
};
|
|
3988
|
+
const getStatusTypographyColor = (isOnline2, lastSeenAt2) => {
|
|
3989
|
+
if (isOnline2) return "success";
|
|
3990
|
+
if (!lastSeenAt2) return "textSecondary";
|
|
3991
|
+
const diffInMinutes = (Date.now() - new Date(lastSeenAt2).getTime()) / (1e3 * 60);
|
|
3992
|
+
if (diffInMinutes <= 15) {
|
|
3993
|
+
return "warning";
|
|
3994
|
+
}
|
|
3995
|
+
return "textSecondary";
|
|
3996
|
+
};
|
|
3997
|
+
const getStatusIconColor = (isOnline2, lastSeenAt2) => {
|
|
3998
|
+
if (isOnline2) return "success";
|
|
3999
|
+
if (!lastSeenAt2) return "inherit";
|
|
4000
|
+
const diffInMinutes = (Date.now() - new Date(lastSeenAt2).getTime()) / (1e3 * 60);
|
|
4001
|
+
if (diffInMinutes <= 15) {
|
|
4002
|
+
return "warning";
|
|
4003
|
+
}
|
|
4004
|
+
return "inherit";
|
|
4005
|
+
};
|
|
4006
|
+
const canChat = currentMembership && membershipData && currentMembership.id !== membershipData.id;
|
|
4007
|
+
return /* @__PURE__ */ jsx18(
|
|
4008
|
+
Popover,
|
|
4009
|
+
{
|
|
4010
|
+
open,
|
|
4011
|
+
anchorEl,
|
|
4012
|
+
onClose,
|
|
4013
|
+
anchorOrigin: { vertical: "bottom", horizontal: "left" },
|
|
4014
|
+
transformOrigin: { vertical: "top", horizontal: "left" },
|
|
4015
|
+
slotProps: {
|
|
4016
|
+
paper: {
|
|
4017
|
+
sx: {
|
|
4018
|
+
width: 350,
|
|
4019
|
+
p: 0,
|
|
4020
|
+
overflow: "hidden",
|
|
4021
|
+
borderRadius: 0,
|
|
4022
|
+
boxShadow: 4
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
},
|
|
4026
|
+
children: loading ? /* @__PURE__ */ jsx18(Box4, { sx: { display: "flex", justifyContent: "center", py: 4 }, children: /* @__PURE__ */ jsx18(CircularProgress2, { size: 28 }) }) : profileData ? /* @__PURE__ */ jsxs6(Box4, { sx: { display: "flex", flexDirection: "column" }, children: [
|
|
4027
|
+
/* @__PURE__ */ jsxs6(
|
|
4028
|
+
Box4,
|
|
4029
|
+
{
|
|
4030
|
+
sx: {
|
|
4031
|
+
position: "relative",
|
|
4032
|
+
width: "100%",
|
|
4033
|
+
height: 96,
|
|
4034
|
+
bgcolor: "action.selected"
|
|
4035
|
+
},
|
|
4036
|
+
children: [
|
|
4037
|
+
coverUrl && /* @__PURE__ */ jsx18(
|
|
4038
|
+
Box4,
|
|
4039
|
+
{
|
|
4040
|
+
component: "img",
|
|
4041
|
+
src: coverUrl,
|
|
4042
|
+
alt: "Profile background",
|
|
4043
|
+
sx: {
|
|
4044
|
+
width: "100%",
|
|
4045
|
+
height: "100%",
|
|
4046
|
+
objectFit: "cover",
|
|
4047
|
+
display: "block",
|
|
4048
|
+
m: 0,
|
|
4049
|
+
p: 0
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
),
|
|
4053
|
+
/* @__PURE__ */ jsx18(
|
|
4054
|
+
IconButton4,
|
|
4055
|
+
{
|
|
4056
|
+
size: "small",
|
|
4057
|
+
onClick: onClose,
|
|
4058
|
+
sx: {
|
|
4059
|
+
position: "absolute",
|
|
4060
|
+
top: 8,
|
|
4061
|
+
right: 8,
|
|
4062
|
+
color: "common.white",
|
|
4063
|
+
bgcolor: "rgba(0, 0, 0, 0.4)",
|
|
4064
|
+
backdropFilter: "blur(4px)",
|
|
4065
|
+
transition: "all 0.2s ease",
|
|
4066
|
+
"&:hover": {
|
|
4067
|
+
bgcolor: "rgba(0, 0, 0, 0.8)",
|
|
4068
|
+
transform: "scale(1.1)"
|
|
4069
|
+
}
|
|
4070
|
+
},
|
|
4071
|
+
children: /* @__PURE__ */ jsx18(CloseIcon, { fontSize: "small" })
|
|
4072
|
+
}
|
|
4073
|
+
)
|
|
4074
|
+
]
|
|
4075
|
+
}
|
|
4076
|
+
),
|
|
4077
|
+
/* @__PURE__ */ jsxs6(Box4, { sx: { p: 2, pt: 0 }, children: [
|
|
4078
|
+
/* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1.5, sx: { alignItems: "flex-end", mt: "-18px", mb: 1 }, children: [
|
|
4079
|
+
/* @__PURE__ */ jsx18(
|
|
4080
|
+
Badge,
|
|
4081
|
+
{
|
|
4082
|
+
overlap: "circular",
|
|
4083
|
+
anchorOrigin: { vertical: "bottom", horizontal: "right" },
|
|
4084
|
+
variant: "dot",
|
|
4085
|
+
sx: {
|
|
4086
|
+
"& .MuiBadge-badge": {
|
|
4087
|
+
backgroundColor: isOnline ? "#44b700" : "#bdbdbd",
|
|
4088
|
+
color: isOnline ? "#44b700" : "#bdbdbd",
|
|
4089
|
+
boxShadow: `0 0 0 2px #fff`,
|
|
4090
|
+
width: 12,
|
|
4091
|
+
height: 12,
|
|
4092
|
+
borderRadius: "50%",
|
|
4093
|
+
"&::after": isOnline ? {
|
|
4094
|
+
position: "absolute",
|
|
4095
|
+
top: 0,
|
|
4096
|
+
left: 0,
|
|
4097
|
+
width: "100%",
|
|
4098
|
+
height: "100%",
|
|
4099
|
+
borderRadius: "50%",
|
|
4100
|
+
animation: "ripple 1.2s infinite ease-in-out",
|
|
4101
|
+
border: "1px solid currentColor",
|
|
4102
|
+
content: '""'
|
|
4103
|
+
} : {}
|
|
4104
|
+
},
|
|
4105
|
+
"@keyframes ripple": {
|
|
4106
|
+
"0%": {
|
|
4107
|
+
transform: "scale(.8)",
|
|
4108
|
+
opacity: 1
|
|
4109
|
+
},
|
|
4110
|
+
"100%": {
|
|
4111
|
+
transform: "scale(2.4)",
|
|
4112
|
+
opacity: 0
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
},
|
|
4116
|
+
children: /* @__PURE__ */ jsx18(
|
|
4117
|
+
Avatar,
|
|
4118
|
+
{
|
|
4119
|
+
src: avatarUrl || coverUrl || void 0,
|
|
4120
|
+
sx: {
|
|
4121
|
+
width: 65,
|
|
4122
|
+
height: 65,
|
|
4123
|
+
border: "3px solid",
|
|
4124
|
+
borderColor: "background.paper",
|
|
4125
|
+
boxShadow: 2,
|
|
4126
|
+
bgcolor: "primary.main",
|
|
4127
|
+
fontSize: 22,
|
|
4128
|
+
fontWeight: 600
|
|
4129
|
+
},
|
|
4130
|
+
children: profileData.display_name?.[0]?.toUpperCase() || "?"
|
|
4131
|
+
}
|
|
4132
|
+
)
|
|
4133
|
+
}
|
|
4134
|
+
),
|
|
4135
|
+
/* @__PURE__ */ jsxs6(Box4, { sx: { overflow: "hidden", pb: 0.5 }, children: [
|
|
4136
|
+
/* @__PURE__ */ jsx18(Typography2, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.2 }, noWrap: true, children: profileData.display_name || `${profileData.first_name || ""} ${profileData.last_name || ""}` }),
|
|
4137
|
+
membershipData?.employee_post && /* @__PURE__ */ jsx18(Typography2, { variant: "body2", color: "text.secondary", noWrap: true, children: membershipData.employee_post })
|
|
4138
|
+
] })
|
|
4139
|
+
] }),
|
|
4140
|
+
/* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { my: 1, flexWrap: "wrap", gap: 0.5 }, children: [
|
|
4141
|
+
getStatusChip(),
|
|
4142
|
+
/* @__PURE__ */ jsx18(UserRoleChipComponent, { role: membershipData?.role || "user" })
|
|
4143
|
+
] }),
|
|
4144
|
+
/* @__PURE__ */ jsx18(Divider, { sx: { my: 1.5 } }),
|
|
4145
|
+
/* @__PURE__ */ jsxs6(Stack, { spacing: 1, sx: { fontSize: "0.875rem", mb: 1.5 }, children: [
|
|
4146
|
+
/* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, color: "text.secondary", children: [
|
|
4147
|
+
/* @__PURE__ */ jsx18(AccessTimeIcon, { fontSize: "small", color: getStatusIconColor(isOnline, lastSeenAt) }),
|
|
4148
|
+
/* @__PURE__ */ jsx18(Typography2, { variant: "body2", color: getStatusTypographyColor(isOnline, lastSeenAt), children: isOnline ? /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Online" }) : lastSeenAt ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
4149
|
+
/* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Last seen" }),
|
|
4150
|
+
":",
|
|
4151
|
+
" ",
|
|
4152
|
+
/* @__PURE__ */ jsx18("strong", { children: formatRelativeTime(lastSeenAt) })
|
|
4153
|
+
] }) : /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Offline" }) })
|
|
4154
|
+
] }),
|
|
4155
|
+
profileData.email && /* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, color: "text.secondary", children: [
|
|
4156
|
+
/* @__PURE__ */ jsx18(EmailIcon, { fontSize: "small" }),
|
|
4157
|
+
/* @__PURE__ */ jsx18(Typography2, { variant: "body2", color: "text.primary", noWrap: true, sx: { flexGrow: 1 }, children: profileData.email }),
|
|
4158
|
+
/* @__PURE__ */ jsx18(CopyClipboardButtonComponent, { textToCopy: profileData.email })
|
|
4159
|
+
] }),
|
|
4160
|
+
membershipData?.employee_post && /* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, color: "text.secondary", children: [
|
|
4161
|
+
/* @__PURE__ */ jsx18(WorkIcon, { fontSize: "small" }),
|
|
4162
|
+
/* @__PURE__ */ jsx18(Typography2, { variant: "body2", color: "text.primary", noWrap: true, sx: { flexGrow: 1 }, children: membershipData.employee_post }),
|
|
4163
|
+
/* @__PURE__ */ jsx18(CopyClipboardButtonComponent, { textToCopy: membershipData.employee_post })
|
|
4164
|
+
] }),
|
|
4165
|
+
profileData.phone && /* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, color: "text.secondary", children: [
|
|
4166
|
+
/* @__PURE__ */ jsx18(PhoneIcon, { fontSize: "small" }),
|
|
4167
|
+
/* @__PURE__ */ jsx18(Typography2, { variant: "body2", color: "text.primary", children: profileData.phone })
|
|
4168
|
+
] }),
|
|
4169
|
+
profileData.birthday && /* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, color: "text.secondary", children: [
|
|
4170
|
+
/* @__PURE__ */ jsx18(CakeIcon, { fontSize: "small" }),
|
|
4171
|
+
/* @__PURE__ */ jsx18(Typography2, { variant: "body2", color: "text.primary", children: formatDate(profileData.birthday) })
|
|
4172
|
+
] }),
|
|
4173
|
+
membershipData?.created_at && /* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, color: "text.secondary", children: [
|
|
4174
|
+
/* @__PURE__ */ jsx18(CalendarIcon, { fontSize: "small" }),
|
|
4175
|
+
/* @__PURE__ */ jsxs6(Typography2, { variant: "body2", color: "text.primary", children: [
|
|
4176
|
+
/* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "In company from" }),
|
|
4177
|
+
": ",
|
|
4178
|
+
formatDate(membershipData.created_at)
|
|
4179
|
+
] })
|
|
4180
|
+
] }),
|
|
4181
|
+
profileData.description && /* @__PURE__ */ jsx18(
|
|
4182
|
+
Typography2,
|
|
4183
|
+
{
|
|
4184
|
+
variant: "caption",
|
|
4185
|
+
color: "text.secondary",
|
|
4186
|
+
sx: {
|
|
4187
|
+
display: "-webkit-box",
|
|
4188
|
+
WebkitLineClamp: 2,
|
|
4189
|
+
WebkitBoxOrient: "vertical",
|
|
4190
|
+
overflow: "hidden",
|
|
4191
|
+
mt: 0.5
|
|
4192
|
+
},
|
|
4193
|
+
children: profileData.description
|
|
4194
|
+
}
|
|
4195
|
+
)
|
|
4196
|
+
] }),
|
|
4197
|
+
/* @__PURE__ */ jsx18(Divider, { sx: { my: 1 } }),
|
|
4198
|
+
/* @__PURE__ */ jsxs6(Stack, { direction: "row", spacing: 1, sx: { justifyContent: "space-around", pt: 0.5 }, children: [
|
|
4199
|
+
/* @__PURE__ */ jsx18(TranslatableTooltipComponent, { title: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Send a notification" }), children: /* @__PURE__ */ jsx18(IconButton4, { size: "small", color: "primary", children: /* @__PURE__ */ jsx18(NotificationIcon, { fontSize: "small" }) }) }),
|
|
4200
|
+
/* @__PURE__ */ jsx18(TranslatableTooltipComponent, { title: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Set a task" }), children: /* @__PURE__ */ jsx18(IconButton4, { size: "small", color: "primary", children: /* @__PURE__ */ jsx18(TaskIcon, { fontSize: "small" }) }) }),
|
|
4201
|
+
/* @__PURE__ */ jsx18(TranslatableTooltipComponent, { title: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Start a chat" }), children: /* @__PURE__ */ jsx18(
|
|
4202
|
+
IconButton4,
|
|
4203
|
+
{
|
|
4204
|
+
size: "small",
|
|
4205
|
+
color: "primary",
|
|
4206
|
+
disabled: !canChat,
|
|
4207
|
+
onClick: () => membershipData && openChatWithUser(membershipData.id),
|
|
4208
|
+
children: /* @__PURE__ */ jsx18(ChatIcon, { fontSize: "small" })
|
|
4209
|
+
}
|
|
4210
|
+
) })
|
|
4211
|
+
] })
|
|
4212
|
+
] })
|
|
4213
|
+
] }) : /* @__PURE__ */ jsx18(Box4, { sx: { p: 2 }, children: /* @__PURE__ */ jsx18(
|
|
4214
|
+
Typography2,
|
|
4215
|
+
{
|
|
4216
|
+
color: "error",
|
|
4217
|
+
variant: "body2",
|
|
4218
|
+
children: /* @__PURE__ */ jsx18(TranslateText, { overrideNamespace: overrideNamespace || "form_components", children: "Failed to load user data" })
|
|
4219
|
+
}
|
|
4220
|
+
) })
|
|
4221
|
+
}
|
|
4222
|
+
);
|
|
4223
|
+
};
|
|
4224
|
+
var UserPopoverComponent_default = UserPopoverComponent;
|
|
4225
|
+
|
|
3834
4226
|
export {
|
|
3835
|
-
getUserLastSeenAt,
|
|
3836
4227
|
SupabaseProvider,
|
|
3837
4228
|
useSupabase,
|
|
3838
|
-
useAuth,
|
|
3839
4229
|
TranslationEditProvider,
|
|
3840
4230
|
useTranslationEdit,
|
|
3841
4231
|
translationService,
|
|
@@ -3859,5 +4249,7 @@ export {
|
|
|
3859
4249
|
Providers,
|
|
3860
4250
|
UserStatusChipComponent,
|
|
3861
4251
|
UserRoleChipComponent,
|
|
3862
|
-
CopyClipboardButtonComponent
|
|
4252
|
+
CopyClipboardButtonComponent,
|
|
4253
|
+
UserPopoverComponent,
|
|
4254
|
+
UserPopoverComponent_default
|
|
3863
4255
|
};
|