next-auth-mfelfelani72 0.0.17 → 0.0.18
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/components/Login.d.ts +13 -4
- package/dist/components/Login.js +8 -8
- package/dist/components/Register.d.ts +1 -5
- package/dist/components/Register.js +25 -60
- package/dist/components/base/LanguageSwitcher.d.ts +1 -0
- package/dist/components/base/LanguageSwitcher.js +135 -0
- package/dist/components/demo/UiLogin.d.ts +10 -2
- package/dist/components/demo/UiLogin.js +5 -11
- package/dist/components/demo/UiRegister.d.ts +12 -2
- package/dist/components/demo/UiRegister.js +5 -26
- package/dist/configs/env.d.ts +30 -0
- package/dist/configs/env.js +129 -0
- package/dist/configs/language.d.ts +32 -0
- package/dist/configs/language.js +43 -0
- package/dist/core/auth.d.ts +11 -0
- package/dist/core/auth.js +33 -0
- package/dist/hooks/useTranslation.d.ts +4 -0
- package/dist/hooks/useTranslation.js +34 -0
- package/dist/index.d.ts +4 -10
- package/dist/index.js +3 -10
- package/dist/interfaces/dictionary.d.ts +18 -0
- package/dist/{Interfaces.js → interfaces/dictionary.js} +2 -2
- package/dist/interfaces/global.d.ts +65 -0
- package/dist/interfaces/global.js +1 -0
- package/dist/{libs → libraries/auth}/loginHandler.d.ts +1 -7
- package/dist/libraries/auth/loginHandler.js +55 -0
- package/dist/libraries/cn.d.ts +1 -0
- package/dist/libraries/cn.js +12 -0
- package/dist/libraries/debounce.d.ts +10 -0
- package/dist/libraries/debounce.js +17 -0
- package/dist/libraries/detectComponentResponsive.d.ts +10 -0
- package/dist/libraries/detectComponentResponsive.js +24 -0
- package/dist/libraries/detectDeviceFromUA.d.ts +8 -0
- package/dist/libraries/detectDeviceFromUA.js +14 -0
- package/dist/libraries/fonts.d.ts +10 -0
- package/dist/libraries/fonts.js +93 -0
- package/dist/libraries/translation.d.ts +15 -0
- package/dist/libraries/translation.js +70 -0
- package/dist/locale/auth/en.json +30 -0
- package/dist/locale/auth/fa.json +34 -0
- package/dist/locale/en.d.ts +38 -0
- package/dist/locale/en.js +10 -0
- package/dist/locale/fa.d.ts +42 -0
- package/dist/locale/fa.js +10 -0
- package/dist/locale/index.d.ts +131 -0
- package/dist/locale/index.js +14 -0
- package/dist/proxy.d.ts +14 -0
- package/dist/proxy.js +58 -0
- package/dist/stores/AppStore.d.ts +53 -0
- package/dist/stores/AppStore.js +141 -0
- package/dist/stores/LangStore.d.ts +10 -0
- package/dist/stores/LangStore.js +87 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.js +1 -0
- package/dist/utilities/app/cookieUtils.d.ts +20 -0
- package/dist/utilities/app/cookieUtils.js +98 -0
- package/package.json +21 -15
- package/dist/Interfaces.d.ts +0 -47
- package/dist/libs/loginHandler.js +0 -37
- package/dist/libs/registerHandler.d.ts +0 -11
- package/dist/libs/registerHandler.js +0 -49
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import type { LoginProps } from "../types";
|
|
3
|
+
interface LoginComponentProps {
|
|
4
|
+
onSubmit: (formData: FormData) => void;
|
|
5
|
+
errors?: {
|
|
6
|
+
username?: string;
|
|
7
|
+
password?: string;
|
|
8
|
+
};
|
|
9
|
+
message?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export default function Login({ loginRoute, UiComponent, }: LoginProps & {
|
|
12
|
+
UiComponent?: React.ComponentType<LoginComponentProps>;
|
|
13
|
+
}): React.JSX.Element;
|
|
14
|
+
export {};
|
package/dist/components/Login.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* @Author: Mohammad Felfelani
|
|
3
3
|
* @Email: mfelfelani72@gmail.com
|
|
4
4
|
* @Team:
|
|
5
|
-
* @Date: 2025-
|
|
5
|
+
* @Date: 2025-12-31 06:00:17
|
|
6
6
|
* @Description:
|
|
7
7
|
*/
|
|
8
8
|
"use client";
|
|
9
|
-
import {
|
|
9
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
10
10
|
import { useState } from "react";
|
|
11
11
|
// Components
|
|
12
12
|
import UiLogin from "./demo/UiLogin";
|
|
13
|
-
export default function Login({ loginRoute,
|
|
13
|
+
export default function Login({ loginRoute, UiComponent, }) {
|
|
14
14
|
// States
|
|
15
15
|
const [errors, setErrors] = useState({});
|
|
16
16
|
const [message, setMessage] = useState(null);
|
|
@@ -19,12 +19,12 @@ export default function Login({ loginRoute, onGoogleLogin, UiComponent, dict, la
|
|
|
19
19
|
const handleSubmit = async (formData) => {
|
|
20
20
|
setMessage(null);
|
|
21
21
|
setErrors({});
|
|
22
|
-
const
|
|
22
|
+
const email = formData.get("username") || "";
|
|
23
23
|
const password = formData.get("password") || "";
|
|
24
24
|
const newErrors = {};
|
|
25
|
-
if (!
|
|
25
|
+
if (!email.trim())
|
|
26
26
|
newErrors.username = "Username is required";
|
|
27
|
-
else if (
|
|
27
|
+
else if (email.length < 3)
|
|
28
28
|
newErrors.username = "Username must be at least 3 characters";
|
|
29
29
|
if (!password)
|
|
30
30
|
newErrors.password = "Password is required";
|
|
@@ -40,7 +40,7 @@ export default function Login({ loginRoute, onGoogleLogin, UiComponent, dict, la
|
|
|
40
40
|
const res = await fetch(loginRoute, {
|
|
41
41
|
method: "POST",
|
|
42
42
|
headers: { "Content-Type": "application/json" },
|
|
43
|
-
body: JSON.stringify({ email
|
|
43
|
+
body: JSON.stringify({ email, password }),
|
|
44
44
|
});
|
|
45
45
|
const data = await res.json();
|
|
46
46
|
if (!res.ok) {
|
|
@@ -57,5 +57,5 @@ export default function Login({ loginRoute, onGoogleLogin, UiComponent, dict, la
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
const RenderUi = UiComponent || UiLogin;
|
|
60
|
-
return (
|
|
60
|
+
return (_jsx(_Fragment, { children: _jsx("div", { className: "relative z-10 w-full max-w-md px-4", children: _jsx(RenderUi, { onSubmit: handleSubmit, errors: errors, message: message }) }) }));
|
|
61
61
|
}
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { RegisterInterface, ClientRegisterInterface } from "../Interfaces";
|
|
3
|
-
export default function Register({ registerRoute, onGoogleRegister, UiComponent, dict, lang, }: RegisterInterface & {
|
|
4
|
-
UiComponent?: React.ComponentType<ClientRegisterInterface>;
|
|
5
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
export default function Register(): import("react").JSX.Element;
|
|
@@ -1,76 +1,41 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: Mohammad Felfelani
|
|
3
|
-
* @Email: mfelfelani72@gmail.com
|
|
4
|
-
* @Team:
|
|
5
|
-
* @Date: 2025-10-02 06:57:46
|
|
6
|
-
* @Description:
|
|
7
|
-
*/
|
|
8
1
|
"use client";
|
|
9
|
-
import { jsx as _jsx
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
3
|
import { useState } from "react";
|
|
11
|
-
// Components
|
|
12
4
|
import UiRegister from "./demo/UiRegister";
|
|
13
|
-
export default function Register(
|
|
14
|
-
// States
|
|
5
|
+
export default function Register() {
|
|
15
6
|
const [errors, setErrors] = useState({});
|
|
16
7
|
const [message, setMessage] = useState(null);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const handleSubmit = async (formData) => {
|
|
20
|
-
setMessage(null);
|
|
21
|
-
setErrors({});
|
|
22
|
-
const name = formData.get("name") || "";
|
|
23
|
-
const email = formData.get("email") || "";
|
|
24
|
-
const password = formData.get("password") || "";
|
|
25
|
-
const password_confirmation = formData.get("password_confirmation") || "";
|
|
8
|
+
const validate = (formData) => {
|
|
9
|
+
var _a, _b, _c, _d;
|
|
26
10
|
const newErrors = {};
|
|
27
|
-
|
|
11
|
+
const name = (_a = formData.get("name")) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
12
|
+
const email = (_b = formData.get("email")) === null || _b === void 0 ? void 0 : _b.toString().trim();
|
|
13
|
+
const password = (_c = formData.get("password")) === null || _c === void 0 ? void 0 : _c.toString();
|
|
14
|
+
const confirmPassword = (_d = formData.get("confirmPassword")) === null || _d === void 0 ? void 0 : _d.toString();
|
|
15
|
+
if (!name)
|
|
28
16
|
newErrors.name = "Name is required";
|
|
29
|
-
|
|
30
|
-
newErrors.name = "Name must be at least 2 characters";
|
|
31
|
-
if (!email.trim())
|
|
17
|
+
if (!email)
|
|
32
18
|
newErrors.email = "Email is required";
|
|
33
|
-
else if (!/\S+@\S+\.\S+/.test(email))
|
|
34
|
-
newErrors.email = "Email is invalid";
|
|
35
19
|
if (!password)
|
|
36
20
|
newErrors.password = "Password is required";
|
|
37
21
|
else if (password.length < 6)
|
|
38
22
|
newErrors.password = "Password must be at least 6 characters";
|
|
39
|
-
if (!
|
|
40
|
-
newErrors.
|
|
41
|
-
else if (password !==
|
|
42
|
-
newErrors.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
method: "POST",
|
|
52
|
-
headers: { "Content-Type": "application/json" },
|
|
53
|
-
body: JSON.stringify({
|
|
54
|
-
name,
|
|
55
|
-
email,
|
|
56
|
-
password,
|
|
57
|
-
password_confirmation,
|
|
58
|
-
}),
|
|
59
|
-
});
|
|
60
|
-
const data = await res.json();
|
|
61
|
-
if (!res.ok) {
|
|
62
|
-
setMessage((data === null || data === void 0 ? void 0 : data.message) || "Registration failed");
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
setMessage((data === null || data === void 0 ? void 0 : data.message) || "Registration successful");
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
setMessage((err === null || err === void 0 ? void 0 : err.message) || "Network error");
|
|
23
|
+
if (!confirmPassword)
|
|
24
|
+
newErrors.confirmPassword = "Confirm your password";
|
|
25
|
+
else if (password !== confirmPassword)
|
|
26
|
+
newErrors.confirmPassword = "Passwords do not match";
|
|
27
|
+
return newErrors;
|
|
28
|
+
};
|
|
29
|
+
const handleSubmit = (formData) => {
|
|
30
|
+
const validationErrors = validate(formData);
|
|
31
|
+
setErrors(validationErrors);
|
|
32
|
+
if (Object.keys(validationErrors).length === 0) {
|
|
33
|
+
setMessage(null);
|
|
34
|
+
// onSubmitServer(formData);
|
|
69
35
|
}
|
|
70
|
-
|
|
71
|
-
|
|
36
|
+
else {
|
|
37
|
+
setMessage("Please fix the errors below.");
|
|
72
38
|
}
|
|
73
39
|
};
|
|
74
|
-
|
|
75
|
-
return (_jsxs(_Fragment, { children: [_jsx(RenderUi, { onSubmit: handleSubmit, errors: errors, message: message, dict: dict, lang: lang }), loading && (_jsx("p", { className: "text-center text-gray-200 mt-2", children: "Registering..." })), onGoogleRegister && (_jsx("div", { className: "flex justify-center mt-4", children: _jsx("button", { onClick: onGoogleRegister, className: "px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition", children: "Sign up with Google" }) }))] }));
|
|
40
|
+
return (_jsx(UiRegister, { onSubmit: handleSubmit, errors: errors, message: message }));
|
|
76
41
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function LanguageSwitcher(): import("react").JSX.Element;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Author: Mohammad Felfelani
|
|
3
|
+
* @Email: mfelfelani72@gmail.com
|
|
4
|
+
* @Team:
|
|
5
|
+
* @Date: 2025-10-08 10:10:51
|
|
6
|
+
* @Description: Language Switcher with Glassmorphism Style
|
|
7
|
+
*/
|
|
8
|
+
"use client";
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { useRouter, usePathname } from "next/navigation";
|
|
11
|
+
import { useState, useRef, useEffect } from "react";
|
|
12
|
+
// Components
|
|
13
|
+
import { LanguageIcon } from "forma-ui";
|
|
14
|
+
// Zustand
|
|
15
|
+
import { useLangStore } from "@/stores/LangStore";
|
|
16
|
+
// Interfaces
|
|
17
|
+
import { languages, getNativeName, getFlag, } from "@/configs/language";
|
|
18
|
+
export default function LanguageSwitcher() {
|
|
19
|
+
// Hooks
|
|
20
|
+
const router = useRouter();
|
|
21
|
+
const pathname = usePathname();
|
|
22
|
+
// States
|
|
23
|
+
const { lang, setLang } = useLangStore();
|
|
24
|
+
const [pendingLang, setPendingLang] = useState(null);
|
|
25
|
+
// States and Refs
|
|
26
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
27
|
+
const [menuPos, setMenuPos] = useState({
|
|
28
|
+
top: 0,
|
|
29
|
+
left: 0,
|
|
30
|
+
width: 0,
|
|
31
|
+
position: "bottom",
|
|
32
|
+
});
|
|
33
|
+
const btnRef = useRef(null);
|
|
34
|
+
const menuRef = useRef(null);
|
|
35
|
+
// Functions
|
|
36
|
+
const getNewPath = (newLang) => {
|
|
37
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
38
|
+
if (segments.length > 0 && segments[0] in languages) {
|
|
39
|
+
segments[0] = newLang;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
segments.unshift(newLang);
|
|
43
|
+
}
|
|
44
|
+
return "/" + segments.join("/");
|
|
45
|
+
};
|
|
46
|
+
const handleChange = (newLang) => {
|
|
47
|
+
if (newLang === lang)
|
|
48
|
+
return;
|
|
49
|
+
const newPath = getNewPath(newLang);
|
|
50
|
+
setPendingLang(newLang);
|
|
51
|
+
setIsOpen(false);
|
|
52
|
+
router.replace(newPath);
|
|
53
|
+
};
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (pendingLang && pathname) {
|
|
56
|
+
const currentLangFromUrl = pathname.split("/").filter(Boolean)[0];
|
|
57
|
+
if (currentLangFromUrl === pendingLang) {
|
|
58
|
+
setLang(pendingLang);
|
|
59
|
+
setPendingLang(null);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}, [pathname, pendingLang, setLang]);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (isOpen && btnRef.current) {
|
|
65
|
+
const rect = btnRef.current.getBoundingClientRect();
|
|
66
|
+
const menuWidth = rect.width + 50;
|
|
67
|
+
const menuHeight = Object.keys(languages).length * 48 + 16;
|
|
68
|
+
let left = rect.left - 25;
|
|
69
|
+
let horizontalPosition = "left";
|
|
70
|
+
if (left + menuWidth > window.innerWidth) {
|
|
71
|
+
left = window.innerWidth - menuWidth - 10;
|
|
72
|
+
horizontalPosition = "right";
|
|
73
|
+
}
|
|
74
|
+
if (left < 10) {
|
|
75
|
+
left = 10;
|
|
76
|
+
}
|
|
77
|
+
let top = rect.bottom + 8;
|
|
78
|
+
let verticalPosition = "bottom";
|
|
79
|
+
if (top + menuHeight > window.innerHeight - 10) {
|
|
80
|
+
top = rect.top - menuHeight - 16;
|
|
81
|
+
verticalPosition = "top";
|
|
82
|
+
}
|
|
83
|
+
if (top < 10) {
|
|
84
|
+
top = 10;
|
|
85
|
+
}
|
|
86
|
+
setMenuPos({
|
|
87
|
+
top,
|
|
88
|
+
left,
|
|
89
|
+
width: menuWidth,
|
|
90
|
+
position: verticalPosition === "top" ? "top" : "bottom",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}, [isOpen]);
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const handleClickOutside = (event) => {
|
|
96
|
+
if (btnRef.current &&
|
|
97
|
+
!btnRef.current.contains(event.target) &&
|
|
98
|
+
menuRef.current &&
|
|
99
|
+
!menuRef.current.contains(event.target)) {
|
|
100
|
+
setIsOpen(false);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
104
|
+
return () => {
|
|
105
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
const getMenuAnimationClass = () => {
|
|
109
|
+
switch (menuPos.position) {
|
|
110
|
+
case "top":
|
|
111
|
+
return "origin-bottom scale-95";
|
|
112
|
+
case "bottom":
|
|
113
|
+
default:
|
|
114
|
+
return "origin-top scale-95";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
return (_jsxs("div", { className: "relative inline-block", children: [_jsxs("button", { ref: btnRef, onClick: () => setIsOpen((prev) => !prev), className: "flex items-center gap-2 px-3 py-1.5 rounded-lg transition-all duration-300 cursor-pointer\n bg-white/10 dark:bg-Neutral-700/40\n backdrop-blur-md backdrop-saturate-150\n border border-white/40 dark:border-Neutral-600/30\n shadow-[0_8px_32px_rgba(0,0,0,0.08)] dark:shadow-[0_8px_32px_rgba(0,0,0,0.3)]\n hover:bg-white/50 dark:hover:bg-Neutral-600/50\n hover:shadow-[0_8px_32px_rgba(0,0,0,0.12)] dark:hover:shadow-[0_8px_32px_rgba(0,0,0,0.4)]\n hover:border-white/60 dark:hover:border-Neutral-500/40", children: [_jsx("span", { className: "text-md", children: _jsx(LanguageIcon, { className: "text-Neutral-100" }) }), _jsx("svg", { className: `w-4 h-4 text-Neutral-100 transition-transform duration-300 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx("div", { ref: menuRef, className: `fixed py-2 rounded-xl transition-all duration-200 transform ${getMenuAnimationClass()}
|
|
118
|
+
bg-white/70 dark:bg-Neutral-800/50
|
|
119
|
+
backdrop-blur-xl backdrop-saturate-150
|
|
120
|
+
border border-white/50 dark:border-Neutral-600/30
|
|
121
|
+
shadow-[0_20px_60px_rgba(0,0,0,0.15)] dark:shadow-[0_20px_60px_rgba(0,0,0,0.4)]
|
|
122
|
+
before:absolute before:inset-0 before:rounded-xl before:pointer-events-none
|
|
123
|
+
before:bg-linear-to-br before:from-white/20 before:to-transparent before:opacity-50`, style: {
|
|
124
|
+
top: menuPos.top,
|
|
125
|
+
left: menuPos.left,
|
|
126
|
+
width: menuPos.width,
|
|
127
|
+
}, children: Object.keys(languages).map((l) => {
|
|
128
|
+
const currentLang = l;
|
|
129
|
+
const isActive = currentLang === lang;
|
|
130
|
+
return (_jsxs("button", { onClick: () => handleChange(currentLang), disabled: isActive, className: `w-full flex items-center gap-3 px-4 py-3 text-left transition-all duration-200
|
|
131
|
+
${isActive
|
|
132
|
+
? "text-primary-600 dark:text-primary-400 cursor-default bg-white/20 dark:bg-white/10"
|
|
133
|
+
: "text-Neutral-400 hover:text-primary-600 dark:hover:text-primary-400 hover:bg-white/30 dark:hover:bg-Neutral-700/40 cursor-pointer"}`, children: [_jsx("span", { className: "text-lg", children: getFlag(currentLang) }), _jsx("span", { className: "font-medium", children: getNativeName(currentLang) }), isActive && (_jsx("div", { className: "w-2 h-2 bg-primary-500 dark:bg-primary-400 rounded-full ml-auto shadow-[0_0_12px_rgba(99,102,241,0.4)]" }))] }, currentLang));
|
|
134
|
+
}) }))] }));
|
|
135
|
+
}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface ClientLoginProps {
|
|
2
|
+
onSubmit: (formData: FormData) => void;
|
|
3
|
+
errors?: {
|
|
4
|
+
username?: string;
|
|
5
|
+
password?: string;
|
|
6
|
+
};
|
|
7
|
+
message?: string | null;
|
|
8
|
+
}
|
|
9
|
+
export default function UiLogin({ onSubmit, errors, message, }: ClientLoginProps): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: Mohammad Felfelani
|
|
3
|
-
* @Email: mfelfelani72@gmail.com
|
|
4
|
-
* @Team:
|
|
5
|
-
* @Date: 2025-10-02 08:04:22
|
|
6
|
-
* @Description:
|
|
7
|
-
*/
|
|
8
1
|
"use client";
|
|
9
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
3
|
import { useState, useEffect } from "react";
|
|
11
4
|
import Image from "next/image";
|
|
12
5
|
import Link from "next/link";
|
|
6
|
+
import { useTranslation } from "@/hooks/useTranslation";
|
|
13
7
|
export default function UiLogin({ onSubmit, errors, message, }) {
|
|
14
|
-
|
|
8
|
+
const { t } = useTranslation();
|
|
15
9
|
const [visible, setVisible] = useState(false);
|
|
16
|
-
// Functions
|
|
17
10
|
useEffect(() => {
|
|
11
|
+
// trigger the animation on mount
|
|
18
12
|
setVisible(true);
|
|
19
13
|
}, []);
|
|
20
14
|
return (_jsx("div", { className: "fixed inset-0 overflow-hidden flex items-center justify-center bg-gradient-to-br from-purple-900 via-purple-800 to-black p-4", children: _jsxs("div", { className: `w-full max-w-md bg-white/20 backdrop-blur-md rounded-2xl shadow-xl p-8 border border-white/30
|
|
21
15
|
transform transition-all duration-700 ease-out
|
|
22
16
|
${visible ? "opacity-100 scale-100" : "opacity-0 scale-95"}
|
|
23
|
-
`, children: [_jsx("div", { className: "
|
|
17
|
+
`, children: [_jsx("div", { className: "relative w-40 h-20 mx-auto mb-8", children: _jsx(Image, { src: "/images/png/logo.png", alt: "Logo", fill: true, sizes: "auto", className: "object-contain", priority: true }) }), _jsx("h1", { className: "text-2xl font-bold text-center text-white mb-2", children: t("login") }), _jsx("p", { className: "text-center text-gray-200 mb-6 text-sm", children: "Login with your credentials to continue" }), message && (_jsx("div", { role: "alert", className: "mb-4 text-center text-red-300 font-medium", children: message })), _jsxs("form", { onSubmit: (e) => {
|
|
24
18
|
e.preventDefault();
|
|
25
19
|
const formData = new FormData(e.currentTarget);
|
|
26
20
|
onSubmit(formData);
|
|
@@ -28,5 +22,5 @@ export default function UiLogin({ onSubmit, errors, message, }) {
|
|
|
28
22
|
? "border-red-500"
|
|
29
23
|
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.username) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.username }))] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "password", className: "block text-sm font-medium text-white mb-1", children: "Password" }), _jsx("input", { id: "password", name: "password", type: "password", placeholder: "Enter your password", required: true, className: `w-full px-4 py-2 rounded-lg bg-white/20 focus:bg-white/30 border focus:outline-none focus:ring-2 text-white placeholder-gray-200 ${(errors === null || errors === void 0 ? void 0 : errors.password)
|
|
30
24
|
? "border-red-500"
|
|
31
|
-
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.password) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.password }))] }), _jsx("button", { type: "submit", className: "w-full py-2 bg-purple-700/80 text-white rounded-lg hover:bg-purple-800 transition", children: "Log In" }), _jsxs("div", { className: "flex justify-center gap-4 mt-4", children: [_jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-red-500 hover:bg-red-600 text-white transition font-bold text-lg", children: "G" }), _jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-600 hover:bg-blue-700 text-white transition font-bold text-lg", children: "F" }), _jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-400 hover:bg-blue-500 text-white transition font-bold text-lg", children: "T" })] }), _jsxs("p", { className: "text-center text-sm text-gray-200 mt-4", children: ["Don\u2019t have an account?", " ", _jsx(Link, { href: "/en/register", className: "underline hover:text-white",
|
|
25
|
+
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.password) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.password }))] }), _jsx("button", { type: "submit", className: "w-full py-2 bg-purple-700/80 text-white rounded-lg hover:bg-purple-800 transition", children: "Log In" }), _jsxs("div", { className: "flex justify-center gap-4 mt-4", children: [_jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-red-500 hover:bg-red-600 text-white transition font-bold text-lg", children: "G" }), _jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-600 hover:bg-blue-700 text-white transition font-bold text-lg", children: "F" }), _jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-400 hover:bg-blue-500 text-white transition font-bold text-lg", children: "T" })] }), _jsxs("p", { className: "text-center text-sm text-gray-200 mt-4", children: ["Don\u2019t have an account?", " ", _jsx(Link, { href: "/en/register", className: "underline hover:text-white", children: "Register" })] })] })] }) }));
|
|
32
26
|
}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface UiRegisterProps {
|
|
2
|
+
onSubmit: (formData: FormData) => void;
|
|
3
|
+
errors?: {
|
|
4
|
+
name?: string;
|
|
5
|
+
email?: string;
|
|
6
|
+
password?: string;
|
|
7
|
+
confirmPassword?: string;
|
|
8
|
+
};
|
|
9
|
+
message?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export default function UiRegister({ onSubmit, errors, message, }: UiRegisterProps): import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -1,36 +1,15 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: Mohammad Felfelani
|
|
3
|
-
* @Email: mfelfelani72@gmail.com
|
|
4
|
-
* @Team:
|
|
5
|
-
* @Date: 2025-10-02 08:04:22
|
|
6
|
-
* @Description:
|
|
7
|
-
*/
|
|
8
1
|
"use client";
|
|
9
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
3
|
import { useState, useEffect } from "react";
|
|
11
4
|
import Image from "next/image";
|
|
12
5
|
import Link from "next/link";
|
|
13
|
-
export default function UiRegister({ onSubmit, errors, message,
|
|
14
|
-
// States
|
|
6
|
+
export default function UiRegister({ onSubmit, errors, message, }) {
|
|
15
7
|
const [visible, setVisible] = useState(false);
|
|
16
|
-
// Functions
|
|
17
8
|
useEffect(() => {
|
|
18
|
-
setVisible(true);
|
|
9
|
+
setVisible(true); // simple fade+scale animation
|
|
19
10
|
}, []);
|
|
20
|
-
return (_jsx("div", { className: "fixed inset-0
|
|
21
|
-
transform transition-all duration-700 ease-out
|
|
22
|
-
${visible ? "opacity-100 scale-100" : "opacity-0 scale-95"}
|
|
23
|
-
`, children: [_jsx("div", { className: "flex justify-center mb-6", children: _jsx(Image, { src: "/logo.png", alt: "Logo", width: 80, height: 80, className: "rounded-full shadow" }) }), _jsx("h1", { className: "text-2xl font-bold text-center text-white mb-2", children: "Create Account" }), _jsx("p", { className: "text-center text-gray-200 mb-6 text-sm", children: "Sign up to get started" }), message && (_jsx("div", { role: "alert", className: "mb-4 text-center text-red-300 font-medium", children: message })), _jsxs("form", { onSubmit: (e) => {
|
|
11
|
+
return (_jsx("div", { className: "fixed inset-0 flex items-center justify-center bg-gradient-to-br from-purple-900 via-purple-800 to-black p-4", children: _jsxs("div", { className: `w-full max-w-md bg-white/20 backdrop-blur-md rounded-2xl shadow-xl p-8 border border-white/30 transform transition-all duration-700 ease-out ${visible ? "opacity-100 scale-100" : "opacity-0 scale-95"}`, children: [_jsx("div", { className: "flex justify-center mb-6", children: _jsx(Image, { src: "/logo.png", alt: "Logo", width: 80, height: 80, className: "rounded-full shadow" }) }), _jsx("h1", { className: "text-2xl font-bold text-center text-white mb-2", children: "Create Account" }), _jsx("p", { className: "text-center text-gray-200 mb-6 text-sm", children: "Sign up to get started" }), message && (_jsx("div", { role: "alert", className: "mb-4 text-center text-red-300 font-medium", children: message })), _jsxs("form", { onSubmit: (e) => {
|
|
24
12
|
e.preventDefault();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}, noValidate: true, className: "space-y-5", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "name", className: "block text-sm font-medium text-white mb-1", children: "Full Name" }), _jsx("input", { id: "name", name: "name", type: "text", placeholder: "Enter your full name", required: true, className: `w-full px-4 py-2 rounded-lg bg-white/20 focus:bg-white/30 border focus:outline-none focus:ring-2 text-white placeholder-gray-200 ${(errors === null || errors === void 0 ? void 0 : errors.name)
|
|
28
|
-
? "border-red-500"
|
|
29
|
-
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.name) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.name }))] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "email", className: "block text-sm font-medium text-white mb-1", children: "Email" }), _jsx("input", { id: "email", name: "email", type: "email", placeholder: "Enter your email", required: true, className: `w-full px-4 py-2 rounded-lg bg-white/20 focus:bg-white/30 border focus:outline-none focus:ring-2 text-white placeholder-gray-200 ${(errors === null || errors === void 0 ? void 0 : errors.email)
|
|
30
|
-
? "border-red-500"
|
|
31
|
-
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.email) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.email }))] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "password", className: "block text-sm font-medium text-white mb-1", children: "Password" }), _jsx("input", { id: "password", name: "password", type: "password", placeholder: "Enter your password", required: true, className: `w-full px-4 py-2 rounded-lg bg-white/20 focus:bg-white/30 border focus:outline-none focus:ring-2 text-white placeholder-gray-200 ${(errors === null || errors === void 0 ? void 0 : errors.password)
|
|
32
|
-
? "border-red-500"
|
|
33
|
-
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.password) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.password }))] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "password_confirmation", className: "block text-sm font-medium text-white mb-1", children: "Confirm Password" }), _jsx("input", { id: "password_confirmation", name: "password_confirmation", type: "password", placeholder: "Confirm your password", required: true, className: `w-full px-4 py-2 rounded-lg bg-white/20 focus:bg-white/30 border focus:outline-none focus:ring-2 text-white placeholder-gray-200 ${(errors === null || errors === void 0 ? void 0 : errors.password_confirmation)
|
|
34
|
-
? "border-red-500"
|
|
35
|
-
: "border-gray-300 focus:ring-purple-400"}` }), (errors === null || errors === void 0 ? void 0 : errors.password_confirmation) && (_jsx("p", { className: "mt-1 text-sm text-red-300", children: errors.password_confirmation }))] }), _jsx("button", { type: "submit", className: "w-full py-2 bg-purple-700/80 text-white rounded-lg hover:bg-purple-800 transition", children: "Sign Up" }), _jsxs("div", { className: "flex justify-center gap-4 mt-4", children: [_jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-red-500 hover:bg-red-600 text-white transition font-bold text-lg", children: "G" }), _jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-600 hover:bg-blue-700 text-white transition font-bold text-lg", children: "F" }), _jsx("button", { type: "button", className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-400 hover:bg-blue-500 text-white transition font-bold text-lg", children: "T" })] }), _jsxs("p", { className: "text-center text-sm text-gray-200 mt-4", children: ["Already have an account?", " ", _jsx(Link, { href: `/${lang}/login`, className: "underline hover:text-white", scroll: false, children: "Login" })] })] })] }) }));
|
|
13
|
+
onSubmit(new FormData(e.currentTarget));
|
|
14
|
+
}, className: "space-y-5", children: [_jsx("input", { name: "name", placeholder: "Full Name", className: "w-full px-4 py-2 rounded-lg bg-white/20 text-white placeholder-gray-200" }), _jsx("input", { name: "email", type: "email", placeholder: "Email", className: "w-full px-4 py-2 rounded-lg bg-white/20 text-white placeholder-gray-200" }), _jsx("input", { name: "password", type: "password", placeholder: "Password", className: "w-full px-4 py-2 rounded-lg bg-white/20 text-white placeholder-gray-200" }), _jsx("input", { name: "confirmPassword", type: "password", placeholder: "Confirm Password", className: "w-full px-4 py-2 rounded-lg bg-white/20 text-white placeholder-gray-200" }), _jsx("button", { type: "submit", className: "w-full py-2 bg-purple-700/80 text-white rounded-lg hover:bg-purple-800 transition", children: "Sign Up" })] }), _jsxs("div", { className: "flex justify-center gap-4 mt-4", children: [_jsx("button", { className: "flex items-center justify-center w-10 h-10 rounded-full bg-red-500 hover:bg-red-600 text-white font-bold text-lg transition", children: "G" }), _jsx("button", { className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-600 hover:bg-blue-700 text-white font-bold text-lg transition", children: "F" }), _jsx("button", { className: "flex items-center justify-center w-10 h-10 rounded-full bg-blue-400 hover:bg-blue-500 text-white font-bold text-lg transition", children: "T" })] }), _jsxs("p", { className: "text-center text-sm text-gray-200 mt-4", children: ["Already have an account?", " ", _jsx(Link, { href: "/en/login", className: "underline hover:text-white", children: "Log in" })] })] }) }));
|
|
36
15
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const serverSchema: z.ZodObject<{
|
|
3
|
+
AUTH_SECRET: z.ZodString;
|
|
4
|
+
DATABASE_URL: z.ZodString;
|
|
5
|
+
NEXTAUTH_URL: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
declare const clientSchema: z.ZodObject<{
|
|
8
|
+
NEXT_PUBLIC_APP_URL: z.ZodString;
|
|
9
|
+
NEXT_PUBLIC_API_URL: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type ServerEnv = z.infer<typeof serverSchema>;
|
|
12
|
+
export type ClientEnv = z.infer<typeof clientSchema>;
|
|
13
|
+
declare class EnvManager {
|
|
14
|
+
private _server;
|
|
15
|
+
private _client;
|
|
16
|
+
private _serverValidationErrors;
|
|
17
|
+
private _clientValidationErrors;
|
|
18
|
+
private _initialized;
|
|
19
|
+
private validate;
|
|
20
|
+
get server(): ServerEnv;
|
|
21
|
+
get client(): ClientEnv;
|
|
22
|
+
isServerValid(): boolean;
|
|
23
|
+
isClientValid(): boolean;
|
|
24
|
+
getServerErrors(): z.ZodError | null;
|
|
25
|
+
getClientErrors(): z.ZodError | null;
|
|
26
|
+
reset(): void;
|
|
27
|
+
}
|
|
28
|
+
export declare const env: EnvManager;
|
|
29
|
+
export declare function checkEnv(): boolean;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/config/env.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// Define schemas
|
|
4
|
+
const serverSchema = z.object({
|
|
5
|
+
AUTH_SECRET: z.string().min(32, "AUTH_SECRET must be at least 32 characters"),
|
|
6
|
+
DATABASE_URL: z.string().url("DATABASE_URL must be a valid URL"),
|
|
7
|
+
NEXTAUTH_URL: z.string().url().optional(),
|
|
8
|
+
});
|
|
9
|
+
const clientSchema = z.object({
|
|
10
|
+
NEXT_PUBLIC_APP_URL: z.string().url("NEXT_PUBLIC_APP_URL must be a valid URL"),
|
|
11
|
+
NEXT_PUBLIC_API_URL: z.string().url("NEXT_PUBLIC_API_URL must be a valid URL"),
|
|
12
|
+
});
|
|
13
|
+
// Helper to safely validate
|
|
14
|
+
function validateEnv(schema, env) {
|
|
15
|
+
const result = schema.safeParse(env);
|
|
16
|
+
if (result.success) {
|
|
17
|
+
return { success: true, data: result.data };
|
|
18
|
+
}
|
|
19
|
+
return { success: false, errors: result.error };
|
|
20
|
+
}
|
|
21
|
+
// Lazy validation - only runs when accessed
|
|
22
|
+
class EnvManager {
|
|
23
|
+
constructor() {
|
|
24
|
+
this._server = null;
|
|
25
|
+
this._client = null;
|
|
26
|
+
this._serverValidationErrors = null;
|
|
27
|
+
this._clientValidationErrors = null;
|
|
28
|
+
this._initialized = false;
|
|
29
|
+
}
|
|
30
|
+
validate() {
|
|
31
|
+
if (this._initialized)
|
|
32
|
+
return;
|
|
33
|
+
// Validate server env
|
|
34
|
+
const serverResult = validateEnv(serverSchema, process.env);
|
|
35
|
+
if (serverResult.success) {
|
|
36
|
+
this._server = serverResult.data;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this._serverValidationErrors = serverResult.errors;
|
|
40
|
+
}
|
|
41
|
+
// Validate client env
|
|
42
|
+
const clientResult = validateEnv(clientSchema, process.env);
|
|
43
|
+
if (clientResult.success) {
|
|
44
|
+
this._client = clientResult.data;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this._clientValidationErrors = clientResult.errors;
|
|
48
|
+
}
|
|
49
|
+
this._initialized = true;
|
|
50
|
+
}
|
|
51
|
+
get server() {
|
|
52
|
+
var _a;
|
|
53
|
+
this.validate();
|
|
54
|
+
if (this._server) {
|
|
55
|
+
return this._server;
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Missing or invalid server environment variables:\n` +
|
|
58
|
+
((_a = this._serverValidationErrors) === null || _a === void 0 ? void 0 : _a.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\n')) +
|
|
59
|
+
`\n\nPlease add these to your .env.local file.`);
|
|
60
|
+
}
|
|
61
|
+
get client() {
|
|
62
|
+
var _a;
|
|
63
|
+
this.validate();
|
|
64
|
+
if (this._client) {
|
|
65
|
+
return this._client;
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`Missing or invalid client environment variables:\n` +
|
|
68
|
+
((_a = this._clientValidationErrors) === null || _a === void 0 ? void 0 : _a.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\n')) +
|
|
69
|
+
`\n\nPlease add these to your .env.local file.`);
|
|
70
|
+
}
|
|
71
|
+
// Check if all env vars are valid without throwing
|
|
72
|
+
isServerValid() {
|
|
73
|
+
this.validate();
|
|
74
|
+
return this._server !== null;
|
|
75
|
+
}
|
|
76
|
+
isClientValid() {
|
|
77
|
+
this.validate();
|
|
78
|
+
return this._client !== null;
|
|
79
|
+
}
|
|
80
|
+
// Get validation errors without throwing
|
|
81
|
+
getServerErrors() {
|
|
82
|
+
this.validate();
|
|
83
|
+
return this._serverValidationErrors;
|
|
84
|
+
}
|
|
85
|
+
getClientErrors() {
|
|
86
|
+
this.validate();
|
|
87
|
+
return this._clientValidationErrors;
|
|
88
|
+
}
|
|
89
|
+
// Reset validation (useful for testing)
|
|
90
|
+
reset() {
|
|
91
|
+
this._initialized = false;
|
|
92
|
+
this._server = null;
|
|
93
|
+
this._client = null;
|
|
94
|
+
this._serverValidationErrors = null;
|
|
95
|
+
this._clientValidationErrors = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Singleton instance
|
|
99
|
+
export const env = new EnvManager();
|
|
100
|
+
// Helper for package consumers to check their env
|
|
101
|
+
export function checkEnv() {
|
|
102
|
+
const errors = [];
|
|
103
|
+
if (!env.isServerValid()) {
|
|
104
|
+
const serverErrors = env.getServerErrors();
|
|
105
|
+
if (serverErrors) {
|
|
106
|
+
errors.push('Server environment variables:');
|
|
107
|
+
serverErrors.issues.forEach(issue => {
|
|
108
|
+
errors.push(` - ${issue.path.join('.')}: ${issue.message}`);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!env.isClientValid()) {
|
|
113
|
+
const clientErrors = env.getClientErrors();
|
|
114
|
+
if (clientErrors) {
|
|
115
|
+
errors.push('Client environment variables:');
|
|
116
|
+
clientErrors.issues.forEach(issue => {
|
|
117
|
+
errors.push(` - ${issue.path.join('.')}: ${issue.message}`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (errors.length > 0) {
|
|
122
|
+
console.warn('⚠️ Environment variable validation issues found:');
|
|
123
|
+
console.warn(errors.join('\n'));
|
|
124
|
+
console.warn('\nPlease add the required variables to your .env.local file');
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
console.log('✅ All environment variables are valid!');
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Author: Mohammad Felfelani
|
|
3
|
+
* @Email: mfelfelani72@gmail.com
|
|
4
|
+
* @Team:
|
|
5
|
+
* @Date: 2025-10-04 07:17:02
|
|
6
|
+
* @Description:
|
|
7
|
+
*/
|
|
8
|
+
export declare const languages: {
|
|
9
|
+
readonly en: {
|
|
10
|
+
readonly dir: "ltr";
|
|
11
|
+
readonly locale: "en_US";
|
|
12
|
+
readonly schemaLocale: "en-US";
|
|
13
|
+
readonly name: "English";
|
|
14
|
+
readonly flag: "🇺🇸";
|
|
15
|
+
readonly nativeName: "English";
|
|
16
|
+
};
|
|
17
|
+
readonly fa: {
|
|
18
|
+
readonly dir: "rtl";
|
|
19
|
+
readonly locale: "fa_IR";
|
|
20
|
+
readonly schemaLocale: "fa-IR";
|
|
21
|
+
readonly name: "Persian";
|
|
22
|
+
readonly flag: "🇮🇷";
|
|
23
|
+
readonly nativeName: "فارسی";
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export type Lang = keyof typeof languages;
|
|
27
|
+
export declare function getLocale(lang: Lang): string;
|
|
28
|
+
export declare function getSchemaLocale(lang: Lang): string;
|
|
29
|
+
export declare function getDirection(lang: Lang): "ltr" | "rtl";
|
|
30
|
+
export declare function getName(lang: Lang): string;
|
|
31
|
+
export declare function getNativeName(lang: Lang): string;
|
|
32
|
+
export declare function getFlag(lang: Lang): string;
|