Merge branch 'feature-announcements'
This commit is contained in:
@ -39,7 +39,7 @@ const LanguageSwitcher = () => {
|
|||||||
open={Boolean(anchorEl)}
|
open={Boolean(anchorEl)}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
{locales.map((lng) => {
|
{locales?.map((lng) => {
|
||||||
if (lng === locale) return null;
|
if (lng === locale) return null;
|
||||||
return (
|
return (
|
||||||
<MenuItem key={lng} onClick={() => changeLanguage(lng)}>
|
<MenuItem key={lng} onClick={() => changeLanguage(lng)}>
|
||||||
|
@ -10,9 +10,10 @@ interface ProtectedRouteProps {
|
|||||||
allowedRoles: UserRole[];
|
allowedRoles: UserRole[];
|
||||||
deniedMessage?: string;
|
deniedMessage?: string;
|
||||||
bypass?: boolean;
|
bypass?: boolean;
|
||||||
|
autoRedirect?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProtectedRoute = ({ children, allowedRoles, deniedMessage, bypass = false }: ProtectedRouteProps) => {
|
const ProtectedRoute = ({ children, allowedRoles, deniedMessage, bypass = false, autoRedirect = false }: ProtectedRouteProps) => {
|
||||||
const { data: session, status } = useSession()
|
const { data: session, status } = useSession()
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -20,10 +21,9 @@ const ProtectedRoute = ({ children, allowedRoles, deniedMessage, bypass = false
|
|||||||
console.log("session.role:" + session?.user?.role);
|
console.log("session.role:" + session?.user?.role);
|
||||||
if (!status || status === "unauthenticated") {
|
if (!status || status === "unauthenticated") {
|
||||||
// Redirect to the sign-in page
|
// Redirect to the sign-in page
|
||||||
if (!bypass) {
|
if (autoRedirect) {
|
||||||
signIn();
|
signIn();
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("session.role:" + session?.user?.role);
|
console.log("session.role:" + session?.user?.role);
|
||||||
@ -41,14 +41,27 @@ const ProtectedRoute = ({ children, allowedRoles, deniedMessage, bypass = false
|
|||||||
if (deniedMessage !== undefined) {
|
if (deniedMessage !== undefined) {
|
||||||
return <div>{deniedMessage}</div>;
|
return <div>{deniedMessage}</div>;
|
||||||
}
|
}
|
||||||
return <div>Нямате достъп до тази страница. Ако мислите, че това е грешка, моля, свържете се с администраторите</div>;
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-2xl font-bold mb-4 text-blue-500">{session?.user?.email},</h1>
|
||||||
|
<p className="mb-6">{`Нямате достъп до тази страница. Ако мислите, че това е грешка, моля, свържете се с администраторите`}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === "loading") {
|
if (status === "loading") {
|
||||||
return <div>Зареждане...</div>;
|
return <div>Зареждане...</div>;
|
||||||
}
|
}
|
||||||
if (!session) return <a href="/api/auth/signin">Защитено съдържание. Впишете се.. </a>
|
if (!session) {
|
||||||
return children;
|
if (deniedMessage !== undefined) {
|
||||||
|
return <div>{deniedMessage}</div>;
|
||||||
|
}
|
||||||
|
return <a href="/api/auth/signin">Защитено съдържание. Впишете се.. </a>
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProtectedRoute;
|
export default ProtectedRoute;
|
||||||
|
@ -105,19 +105,22 @@ export default function Sidebar({ isSidebarOpen, toggleSidebar }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchLocations = async () => {
|
const fetchLocations = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.get('/api/data/locations'); // Adjust the API endpoint as needed
|
if (session) { // Don't fetch locations if the user is not authenticated
|
||||||
const locationsData = response.data
|
|
||||||
.filter(location => location.isActive === true)
|
const response = await axiosInstance.get('/api/data/locations'); // Adjust the API endpoint as needed
|
||||||
.map(location => ({
|
const locationsData = response.data
|
||||||
text: location.name,
|
.filter(location => location.isActive === true)
|
||||||
url: `/cart/locations/${location.id}`,
|
.map(location => ({
|
||||||
}));
|
text: location.name,
|
||||||
// Find the "Locations" menu item and populate its children with locationsData
|
url: `/cart/locations/${location.id}`,
|
||||||
const menuIndex = sidemenu.findIndex(item => item.id === "locations");
|
}));
|
||||||
if (menuIndex !== -1) {
|
// Find the "Locations" menu item and populate its children with locationsData
|
||||||
sidemenu[menuIndex].children = locationsData;
|
const menuIndex = sidemenu.findIndex(item => item.id === "locations");
|
||||||
|
if (menuIndex !== -1) {
|
||||||
|
sidemenu[menuIndex].children = locationsData;
|
||||||
|
}
|
||||||
|
//setLocations(locationsData); // Optional, if you need to use locations elsewhere
|
||||||
}
|
}
|
||||||
//setLocations(locationsData); // Optional, if you need to use locations elsewhere
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching locations:", error);
|
console.error("Error fetching locations:", error);
|
||||||
}
|
}
|
||||||
@ -174,16 +177,17 @@ function UserSection({ session }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SignInButton() {
|
function SignInButton() {
|
||||||
const t = useTranslations('common');
|
// const t = useTranslations('common');
|
||||||
return (
|
return (
|
||||||
<div className="items-center py-2 font-bold" onClick={() => signIn()}>
|
<div className="items-center py-2 font-bold" onClick={() => signIn()}>
|
||||||
<button>{t('login')}</button>
|
<button>вход</button>
|
||||||
|
{/* <button>{t('login')}</button> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserDetails({ session }) {
|
function UserDetails({ session }) {
|
||||||
const t = useTranslations('common');
|
// const t = useTranslations('common');
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<hr className="m-0" />
|
<hr className="m-0" />
|
||||||
@ -194,7 +198,10 @@ function UserDetails({ session }) {
|
|||||||
<div className="ml-3 overflow-hidden">
|
<div className="ml-3 overflow-hidden">
|
||||||
<p className="mx-1 mt-1 text-sm font-medium text-gray-800 dark:text-gray-200">{session.user.name}</p>
|
<p className="mx-1 mt-1 text-sm font-medium text-gray-800 dark:text-gray-200">{session.user.name}</p>
|
||||||
<p className="mx-1 mt-1 text-sm font-medium text-gray-600 dark:text-gray-400">{session.user.role}</p>
|
<p className="mx-1 mt-1 text-sm font-medium text-gray-600 dark:text-gray-400">{session.user.role}</p>
|
||||||
<a href="/api/auth/signout" className={styles.button} onClick={(e) => { e.preventDefault(); signOut(); }}>{t('logout')}</a>
|
<a href="/api/auth/signout" className={styles.button} onClick={(e) => { e.preventDefault(); signOut(); }}>
|
||||||
|
{/* {t('logout')} */}
|
||||||
|
изход
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"contacts": "Контакти",
|
"contacts": "Контакти",
|
||||||
"greeting": "Здравей",
|
"greeting": "Здравей",
|
||||||
"farewell": "Довиждане",
|
"farewell": "Довиждане",
|
||||||
"changeTo": "-",
|
"changeTo": "",
|
||||||
"BG": "Български",
|
"BG": "Български",
|
||||||
"EN": "Английски",
|
"EN": "Английски",
|
||||||
"RU": "Руски",
|
"RU": "Руски",
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
|
"appNameLong": "Специално свидетелстване на обществени места в София",
|
||||||
|
"contacts": "Контакти",
|
||||||
"greeting": "Здравей",
|
"greeting": "Здравей",
|
||||||
"farewell": "Довиждане",
|
"farewell": "Довиждане",
|
||||||
"changeTo": "",
|
"changeTo": "",
|
||||||
"BG": "Български",
|
"BG": "български",
|
||||||
"EN": "Английски",
|
"EN": "английски",
|
||||||
"RU": "Руски",
|
"RU": "руски",
|
||||||
"login": "Вход",
|
"login": "Вход",
|
||||||
"logout": "Изход"
|
"logout": "Изход"
|
||||||
},
|
},
|
||||||
|
@ -8,5 +8,8 @@
|
|||||||
"RU": "Russian",
|
"RU": "Russian",
|
||||||
"login": "login",
|
"login": "login",
|
||||||
"logout": "logout"
|
"logout": "logout"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"dashboard": "Dashboard"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"common": {
|
|
||||||
"greeting": "Hello",
|
|
||||||
"farewell": "Goodbye",
|
|
||||||
"changeTo": "Change to",
|
|
||||||
"BG": "Bulgarian",
|
|
||||||
"EN": "English",
|
|
||||||
"RU": "Russian",
|
|
||||||
"login": "login",
|
|
||||||
"logout": "logout"
|
|
||||||
},
|
|
||||||
"menu": {
|
|
||||||
"dashboard": "Dashboard"
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,8 @@
|
|||||||
"BG": "[RU] Български",
|
"BG": "[RU] Български",
|
||||||
"EN": "[RU] Английски",
|
"EN": "[RU] Английски",
|
||||||
"RU": "[RU] Руски",
|
"RU": "[RU] Руски",
|
||||||
"login": "вход"
|
"login": "вход",
|
||||||
|
"contacts": "Контакти"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"dashboard": "Начало"
|
"dashboard": "Начало"
|
||||||
|
15
content/i18n/ru.modified.json
Normal file
15
content/i18n/ru.modified.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"greeting": "Здравей",
|
||||||
|
"farewell": "Довиждане",
|
||||||
|
"changeTo": "Смени на",
|
||||||
|
"BG": "болгарский",
|
||||||
|
"EN": "английский",
|
||||||
|
"RU": "русский",
|
||||||
|
"login": "вход",
|
||||||
|
"contacts": "Контакти te"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"dashboard": "Начало"
|
||||||
|
}
|
||||||
|
}
|
@ -56,6 +56,6 @@ module.exports = withPWA({
|
|||||||
// using https://next-intl-docs.vercel.app/docs/getting-started/pages-router
|
// using https://next-intl-docs.vercel.app/docs/getting-started/pages-router
|
||||||
locales: ['bg', 'en', 'ru'],
|
locales: ['bg', 'en', 'ru'],
|
||||||
defaultLocale: 'bg',
|
defaultLocale: 'bg',
|
||||||
autoDetect: false,
|
localeDetection: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
276
package-lock.json
generated
276
package-lock.json
generated
@ -25,6 +25,8 @@
|
|||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"axios-jwt": "^4.0.2",
|
"axios-jwt": "^4.0.2",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"docx": "^8.5.0",
|
"docx": "^8.5.0",
|
||||||
"docx-templates": "^4.11.4",
|
"docx-templates": "^4.11.4",
|
||||||
@ -2861,6 +2863,69 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.0",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"nopt": "^5.0.0",
|
||||||
|
"npmlog": "^5.0.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"semver": "^7.3.5",
|
||||||
|
"tar": "^6.1.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "6",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
"node_modules/@mui/base": {
|
"node_modules/@mui/base": {
|
||||||
"version": "5.0.0-beta.36",
|
"version": "5.0.0-beta.36",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.36.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.36.tgz",
|
||||||
@ -4990,6 +5055,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/aproba": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
|
||||||
|
},
|
||||||
"node_modules/archiver": {
|
"node_modules/archiver": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
|
||||||
@ -5054,6 +5124,18 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/are-we-there-yet": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
@ -5396,6 +5478,19 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/bcrypt": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||||
|
"node-addon-api": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bcrypt-pbkdf": {
|
"node_modules/bcrypt-pbkdf": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
@ -5404,6 +5499,11 @@
|
|||||||
"tweetnacl": "^0.14.3"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bcryptjs": {
|
||||||
|
"version": "2.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||||
|
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||||
|
},
|
||||||
"node_modules/bidi-js": {
|
"node_modules/bidi-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||||
@ -5841,6 +5941,14 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chownr": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chrome-trace-event": {
|
"node_modules/chrome-trace-event": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
|
||||||
@ -6068,6 +6176,14 @@
|
|||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/color-support": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
|
||||||
|
"bin": {
|
||||||
|
"color-support": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color/node_modules/color-convert": {
|
"node_modules/color/node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@ -6197,6 +6313,11 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
@ -6591,6 +6712,11 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
|
||||||
|
},
|
||||||
"node_modules/depcheck": {
|
"node_modules/depcheck": {
|
||||||
"version": "1.4.7",
|
"version": "1.4.7",
|
||||||
"resolved": "https://registry.npmjs.org/depcheck/-/depcheck-1.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/depcheck/-/depcheck-1.4.7.tgz",
|
||||||
@ -8020,6 +8146,28 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-minipass": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-minipass/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@ -8112,6 +8260,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/gapi-script/-/gapi-script-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gapi-script/-/gapi-script-1.2.0.tgz",
|
||||||
"integrity": "sha512-NKTVKiIwFdkO1j1EzcrWu/Pz7gsl1GmBmgh+qhuV2Ytls04W/Eg5aiBL91SCiBM9lU0PMu7p1hTVxhh1rPT5Lw=="
|
"integrity": "sha512-NKTVKiIwFdkO1j1EzcrWu/Pz7gsl1GmBmgh+qhuV2Ytls04W/Eg5aiBL91SCiBM9lU0PMu7p1hTVxhh1rPT5Lw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/gauge": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3 || ^2.0.0",
|
||||||
|
"color-support": "^1.1.2",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.1",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wide-align": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/gaxios": {
|
"node_modules/gaxios": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.3.0.tgz",
|
||||||
@ -8590,6 +8757,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
||||||
|
},
|
||||||
"node_modules/hasha": {
|
"node_modules/hasha": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
|
||||||
@ -10543,6 +10715,37 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minipass": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mkdirp": {
|
"node_modules/mkdirp": {
|
||||||
"version": "0.5.6",
|
"version": "0.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
@ -10889,6 +11092,11 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-addon-api": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
||||||
|
},
|
||||||
"node_modules/node-excel-export": {
|
"node_modules/node-excel-export": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/node-excel-export/-/node-excel-export-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/node-excel-export/-/node-excel-export-1.4.4.tgz",
|
||||||
@ -11089,6 +11297,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-package-data": {
|
"node_modules/normalize-package-data": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||||
@ -13784,6 +14006,17 @@
|
|||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/npmlog": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "^2.0.0",
|
||||||
|
"console-control-strings": "^1.1.0",
|
||||||
|
"gauge": "^3.0.0",
|
||||||
|
"set-blocking": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/oauth": {
|
"node_modules/oauth": {
|
||||||
"version": "0.9.15",
|
"version": "0.9.15",
|
||||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||||
@ -15755,6 +15988,11 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
|
||||||
@ -15873,8 +16111,7 @@
|
|||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/simple-swizzle": {
|
"node_modules/simple-swizzle": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
@ -16658,6 +16895,22 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"fs-minipass": "^2.0.0",
|
||||||
|
"minipass": "^5.0.0",
|
||||||
|
"minizlib": "^2.1.1",
|
||||||
|
"mkdirp": "^1.0.3",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tar-stream": {
|
"node_modules/tar-stream": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||||
@ -16673,6 +16926,17 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar/node_modules/mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/temp-dir": {
|
"node_modules/temp-dir": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
||||||
@ -17918,6 +18182,14 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wide-align": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/winston": {
|
"node_modules/winston": {
|
||||||
"version": "3.13.0",
|
"version": "3.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz",
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"axios-jwt": "^4.0.2",
|
"axios-jwt": "^4.0.2",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"docx": "^8.5.0",
|
"docx": "^8.5.0",
|
||||||
"docx-templates": "^4.11.4",
|
"docx-templates": "^4.11.4",
|
||||||
@ -115,4 +116,4 @@
|
|||||||
"depcheck": "^1.4.7",
|
"depcheck": "^1.4.7",
|
||||||
"prisma": "^5.13.0"
|
"prisma": "^5.13.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,26 +28,27 @@ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
|
|||||||
export default function App({ Component, pageProps: { session, ...pageProps }, }: AppProps<{ session: Session }>) {
|
export default function App({ Component, pageProps: { session, ...pageProps }, }: AppProps<{ session: Session }>) {
|
||||||
// console.log(pageProps);
|
// console.log(pageProps);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [locale, setLocale] = useState(router.locale || 'bg');
|
||||||
const [messages, setMessages] = useState({});
|
const [messages, setMessages] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Current locale:", router.locale);
|
console.log("Current locale:", router.locale);
|
||||||
async function loadLocaleData() {
|
async function loadLocaleData() {
|
||||||
// Replace the static import with a fetch request
|
// Replace the static import with a fetch request
|
||||||
const res = await fetch(`/api/translations/${router.locale}`);
|
const res = await fetch(`/api/translations/${locale}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const localeMessages = await res.json();
|
const localeMessages = await res.json();
|
||||||
console.log("Loaded messages for locale:", router.locale, localeMessages);
|
console.log("Loaded messages for locale:", locale, localeMessages);
|
||||||
setMessages(localeMessages);
|
setMessages(localeMessages);
|
||||||
} else {
|
} else {
|
||||||
const localeMessages = await import(`../content/i18n/${router.locale}.json`); setMessages(localeMessages.default);
|
const localeMessages = await import(`../content/i18n/${locale}.json`); setMessages(localeMessages.default);
|
||||||
}
|
}
|
||||||
console.log("Loaded locale '", router.locale, "' ",);
|
console.log("Loaded locale '", locale, "' ",);
|
||||||
//console.log("Loaded messages for locale:", router.locale, localeMessages.default);
|
//console.log("Loaded messages for locale:", router.locale, localeMessages.default);
|
||||||
|
|
||||||
}
|
}
|
||||||
loadLocaleData();
|
loadLocaleData();
|
||||||
}, [router.locale]);
|
}, [locale]);
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// async function loadLocaleData() {
|
// async function loadLocaleData() {
|
||||||
@ -101,7 +102,7 @@ export default function App({ Component, pageProps: { session, ...pageProps }, }
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextIntlClientProvider
|
<NextIntlClientProvider
|
||||||
locale={router.locale}
|
locale={'bg'}
|
||||||
timeZone="Europe/Sofia"
|
timeZone="Europe/Sofia"
|
||||||
messages={messages}
|
messages={messages}
|
||||||
>
|
>
|
||||||
@ -110,7 +111,7 @@ export default function App({ Component, pageProps: { session, ...pageProps }, }
|
|||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</LocalizationProvider>
|
</LocalizationProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider >
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import AppleProvider from "next-auth/providers/apple"
|
|||||||
import EmailProvider from "next-auth/providers/email"
|
import EmailProvider from "next-auth/providers/email"
|
||||||
import CredentialsProvider from "next-auth/providers/credentials"
|
import CredentialsProvider from "next-auth/providers/credentials"
|
||||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||||
|
import bcrypt from "bcrypt"
|
||||||
|
|
||||||
//microsoft
|
//microsoft
|
||||||
import AzureADProvider from "next-auth/providers/azure-ad";
|
import AzureADProvider from "next-auth/providers/azure-ad";
|
||||||
@ -16,6 +17,7 @@ import AzureADProvider from "next-auth/providers/azure-ad";
|
|||||||
|
|
||||||
const common = require("../../../src/helpers/common");
|
const common = require("../../../src/helpers/common");
|
||||||
import { isLoggedIn, setAuthTokens, clearAuthTokens, getAccessToken, getRefreshToken } from 'axios-jwt'
|
import { isLoggedIn, setAuthTokens, clearAuthTokens, getAccessToken, getRefreshToken } from 'axios-jwt'
|
||||||
|
import { create } from "domain"
|
||||||
|
|
||||||
|
|
||||||
console.log("appleID:", process.env.APPLE_APP_ID);
|
console.log("appleID:", process.env.APPLE_APP_ID);
|
||||||
@ -52,6 +54,7 @@ export const authOptions: NextAuthOptions = {
|
|||||||
// tenantId: process.env.AZURE_AD_TENANT_ID,
|
// tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||||
// }),
|
// }),
|
||||||
CredentialsProvider({
|
CredentialsProvider({
|
||||||
|
id: 'credentials',
|
||||||
// The name to display on the sign in form (e.g. 'Sign in with...')
|
// The name to display on the sign in form (e.g. 'Sign in with...')
|
||||||
name: 'Credentials',
|
name: 'Credentials',
|
||||||
credentials: {
|
credentials: {
|
||||||
@ -80,17 +83,45 @@ export const authOptions: NextAuthOptions = {
|
|||||||
{ id: "3", name: "popov", email: "popov@example.com", password: "popov123", role: "ADMIN" }
|
{ id: "3", name: "popov", email: "popov@example.com", password: "popov123", role: "ADMIN" }
|
||||||
];
|
];
|
||||||
|
|
||||||
// Check if a user with the given username and password exists
|
|
||||||
const user = users.find(user =>
|
const user = users.find(user =>
|
||||||
user.name === credentials.username && user.password === credentials.password
|
user.name === credentials.username && user.password === credentials.password
|
||||||
);
|
);
|
||||||
|
|
||||||
// If a matching user is found, return the user data, otherwise return null
|
|
||||||
if (user) {
|
if (user) {
|
||||||
return user; //{ id: user.id, name: user.name, email: user.email };
|
return user;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const prisma = common.getPrismaClient();
|
||||||
|
const user = await prisma.user.findUnique({ where: { email: credentials.username } });
|
||||||
|
if (user) {
|
||||||
|
const match = await bcrypt.compare(credentials?.password, user.passwordHashLocalAccount);
|
||||||
|
if (match) {
|
||||||
|
console.log("User authenticated successfully.");
|
||||||
|
//create access token
|
||||||
|
user.accessToken = await getAccessToken();
|
||||||
|
|
||||||
return null;
|
return user;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Password mismatch.");
|
||||||
|
throw new Error('невалидна парола');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("Не можем да намерим твоя имейл '" + credentials?.username + "' в участниците в ССОМ. Моля свържи се с нас за да те регистрираме ако искаш да ползваш този имейл.");
|
||||||
|
// console.log("Creating new user in the database...");
|
||||||
|
// const passHash = await bcrypt.hash(credentials.password, 10);
|
||||||
|
// const newUser = await prisma.user.create({
|
||||||
|
// data: {
|
||||||
|
// name: credentials.username,
|
||||||
|
// email: credentials.username,
|
||||||
|
// passwordHashLocalAccount: passHash
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// console.log("New user created in the database.");
|
||||||
|
// return newUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
/*
|
/*
|
||||||
@ -132,35 +163,35 @@ export const authOptions: NextAuthOptions = {
|
|||||||
var prisma = common.getPrismaClient();
|
var prisma = common.getPrismaClient();
|
||||||
|
|
||||||
console.log("[nextauth] signIn:", account.provider, user.email)
|
console.log("[nextauth] signIn:", account.provider, user.email)
|
||||||
if (account.provider === 'google') {
|
//if (account.provider === 'google' ) {
|
||||||
try {
|
try {
|
||||||
// Check user in your database and assign roles
|
// Check user in your database and assign roles
|
||||||
const dbUser = await prisma.publisher.findUnique({
|
const dbUser = await prisma.publisher.findUnique({
|
||||||
where: { email: user.email }
|
where: { email: user.email }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dbUser) {
|
||||||
|
// Assign roles from your database to the session
|
||||||
|
user.role = dbUser.role;
|
||||||
|
user.id = dbUser.id;
|
||||||
|
//user.permissions = dbUser.permissions;
|
||||||
|
const session = { ...user };
|
||||||
|
|
||||||
|
await prisma.publisher.update({
|
||||||
|
where: { id: dbUser.id },
|
||||||
|
data: { lastLogin: new Date() }
|
||||||
});
|
});
|
||||||
|
return true; // Sign-in successful
|
||||||
if (dbUser) {
|
} else {
|
||||||
// Assign roles from your database to the session
|
// Optionally create a new user in your DB
|
||||||
user.role = dbUser.role;
|
// Or return false to deny access
|
||||||
user.id = dbUser.id;
|
//Let's customize the error message to give a better user experience
|
||||||
//user.permissions = dbUser.permissions;
|
throw new Error(`Твоят имейл '${user.email}' не е регистриран в системата. Моля свържи се с нас за да те регистрираме ако искаш да ползваш този имейл.`);
|
||||||
const session = { ...user };
|
|
||||||
|
|
||||||
await prisma.publisher.update({
|
|
||||||
where: { id: dbUser.id },
|
|
||||||
data: { lastLogin: new Date() }
|
|
||||||
});
|
|
||||||
return true; // Sign-in successful
|
|
||||||
} else {
|
|
||||||
// Optionally create a new user in your DB
|
|
||||||
// Or return false to deny access
|
|
||||||
//Let's customize the error message to give a better user experience
|
|
||||||
throw new Error(`Твоят имейл '${user.email}' не е регистриран в системата. Моля свържи се с нас за да те регистрираме ако искаш да ползваш този имейл.`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
return true; // Allow other providers or default behavior
|
return true; // Allow other providers or default behavior
|
||||||
},
|
},
|
||||||
@ -207,6 +238,13 @@ export const authOptions: NextAuthOptions = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
pages: {
|
||||||
|
signIn: "/auth/signin",
|
||||||
|
signOut: "/auth/signout",
|
||||||
|
error: "/message", // Error code passed in query string as ?error=
|
||||||
|
verifyRequest: "/auth/verify-request", // (used for check email message)
|
||||||
|
newUser: null // If set, new users will be directed here on first sign in
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAuth(authOptions)
|
export default NextAuth(authOptions)
|
@ -33,6 +33,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
switch (targetTable) {
|
switch (targetTable) {
|
||||||
case 'publishers':
|
case 'publishers':
|
||||||
|
case 'availabilities':
|
||||||
const targetId = req.query.nextcrud[1];
|
const targetId = req.query.nextcrud[1];
|
||||||
logger.info('[nextCrud] ' + targetTable + ': ' + targetId + ' DELETED by ' + session.user.email);
|
logger.info('[nextCrud] ' + targetTable + ': ' + targetId + ' DELETED by ' + session.user.email);
|
||||||
break;
|
break;
|
||||||
|
@ -34,7 +34,8 @@ export default async function handler(req, res) {
|
|||||||
// Retrieve and validate the JWT token
|
// Retrieve and validate the JWT token
|
||||||
|
|
||||||
//response is a special action that does not require a token
|
//response is a special action that does not require a token
|
||||||
if (action == "email_response") {
|
//PUBLIC
|
||||||
|
if (action == "email_response" || action == "account") {
|
||||||
switch (emailaction) {
|
switch (emailaction) {
|
||||||
case "coverMeAccept":
|
case "coverMeAccept":
|
||||||
//validate shiftId and assignmentId
|
//validate shiftId and assignmentId
|
||||||
@ -201,6 +202,83 @@ export default async function handler(req, res) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "resetPassword":
|
||||||
|
// Send password reset form to the user
|
||||||
|
//parse the request body
|
||||||
|
|
||||||
|
let email = req.body.email || req.query.email;
|
||||||
|
let actualUser = await prisma.publisher.findUnique({
|
||||||
|
where: {
|
||||||
|
email: email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!actualUser) {
|
||||||
|
return res.status(200).json({ message: "Няма потребител с този имейл" });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let requestGuid = req.query.guid;
|
||||||
|
if (!requestGuid) {
|
||||||
|
console.log("User: " + email + " requested a password reset");
|
||||||
|
let requestGuid = uuidv4();
|
||||||
|
//save the request in the database as EventLog
|
||||||
|
let eventLog = await prisma.eventLog.create({
|
||||||
|
data: {
|
||||||
|
date: new Date(),
|
||||||
|
publisher: { connect: { id: actualUser.id } },
|
||||||
|
type: EventLogType.PasswordResetRequested,
|
||||||
|
content: JSON.stringify({ guid: requestGuid })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logger.info("User: " + email + " requested a password reset. EventLogId: " + eventLog.id + "");
|
||||||
|
|
||||||
|
let model = {
|
||||||
|
email: email,
|
||||||
|
firstName: actualUser.firstName,
|
||||||
|
lastName: actualUser.lastName,
|
||||||
|
resetUrl: process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=resetPassword&guid=" + requestGuid + "&email=" + email,
|
||||||
|
sentDate: common.getDateFormated(new Date())
|
||||||
|
};
|
||||||
|
emailHelper.SendEmailHandlebars(to, "resetPassword", model);
|
||||||
|
res.status(200).json({ message: "Password reset request sent" });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//1. validate the guid
|
||||||
|
let eventLog = await prisma.eventLog.findFirst({
|
||||||
|
where: {//can we query "{ guid: requestGuid }"?
|
||||||
|
type: EventLogType.PasswordResetRequested,
|
||||||
|
publisherId: actualUser.id,
|
||||||
|
date: {
|
||||||
|
gt: new Date(new Date().getTime() - 24 * 60 * 60 * 1000) //24 hours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!eventLog) {
|
||||||
|
return res.status(400).json({ message: "Invalid or expired password reset request" });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let eventLog = await prisma.eventLog.update({
|
||||||
|
where: {
|
||||||
|
id: parseInt(requestGuid)
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: EventLogType.PasswordResetEmailConfirmed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//2. redirect to the password reset page
|
||||||
|
const messagePageUrl = `/auth/reset-password?email=${email}&resetToken=${requestGuid}`;
|
||||||
|
res.redirect(messagePageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
//2.login the user
|
||||||
|
|
||||||
|
//3. redirect to the password reset page
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
// //send email response to the user
|
// //send email response to the user
|
||||||
// const emailResponse = await common.sendEmail(user.email, "Email Action Processed",
|
// const emailResponse = await common.sendEmail(user.email, "Email Action Processed",
|
||||||
@ -220,6 +298,7 @@ export default async function handler(req, res) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//PRIVATE ACTIONS
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "sendCoverMeRequestByEmail":
|
case "sendCoverMeRequestByEmail":
|
||||||
// Send CoverMe request to the users
|
// Send CoverMe request to the users
|
||||||
|
135
pages/auth/reset-password.tsx
Normal file
135
pages/auth/reset-password.tsx
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import { use, useEffect, useState } from 'react';
|
||||||
|
import Layout from '../../components/layout';
|
||||||
|
import axiosInstance from "../../src/axiosSecure";
|
||||||
|
import common from '../../src/helpers/common';
|
||||||
|
import { EventLogType } from '@prisma/client';
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
export default function ResetPassword(req, res) {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [message, setMessage] = useState('');
|
||||||
|
const [resetToken, setResetToken] = useState(req.query?.resetToken || '');
|
||||||
|
const [isConfirmed, setIsConfirmed] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
if (resetToken) {
|
||||||
|
const prisma = common.getPrismaClient();
|
||||||
|
let eventLog = await prisma.eventLog.findUnique({
|
||||||
|
where: {
|
||||||
|
content: resetToken,
|
||||||
|
type: EventLogType.PasswordResetEmailConfirmed,
|
||||||
|
date: {
|
||||||
|
gt: new Date(new Date().getTime() - 24 * 60 * 60 * 1000) //24 hours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (eventLog) {
|
||||||
|
setIsConfirmed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [resetToken]);
|
||||||
|
|
||||||
|
const handleResetRequest = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Call your email API endpoint here
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.post('/api/email?action=account&emailaction=resetPassword', { email },
|
||||||
|
{ headers: { 'Content-Type': 'application/json' } });
|
||||||
|
if (response.data.message) {
|
||||||
|
setMessage(response.data.message);
|
||||||
|
} else {
|
||||||
|
if (response.ok) {
|
||||||
|
setMessage('Провери твоя имейл за инструкции как да промениш паролата си.');
|
||||||
|
} else {
|
||||||
|
if (response.error) {
|
||||||
|
setMessage(response.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setMessage(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setNewPassword = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prisma = common.getPrismaClient();
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('Няма потребител с този имейл.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const passHash = await crypto.hash(event.target.newPassword.value, 10);
|
||||||
|
await prisma.user.update({
|
||||||
|
where: {
|
||||||
|
email
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
passwordHashLocalAccount: passHash
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setMessage('Паролата беше успешно променена.');
|
||||||
|
router.push('/auth/signin');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
setMessage(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="min-h-screen flex items-center justify-center">
|
||||||
|
<div className="w-full max-w-md p-8 space-y-6 bg-white shadow-lg rounded-lg">
|
||||||
|
<h1 className="text-xl font-bold text-center">Променете паролата си</h1>
|
||||||
|
<form onSubmit={handleResetRequest} className="space-y-4">
|
||||||
|
{!isConfirmed &&
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700">имейл</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
{isConfirmed &&
|
||||||
|
<div>
|
||||||
|
<label htmlFor="newPassword" className="block text-sm font-medium text-gray-700">имейл</label>
|
||||||
|
<input
|
||||||
|
id="newPassword"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
value={newPassword}
|
||||||
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
|
<div>
|
||||||
|
<button type="submit" className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700">
|
||||||
|
Изпрати линк за промяна на паролата
|
||||||
|
</button>
|
||||||
|
<button type="button" className="mt-4 w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-blue-600 hover:text-blue-700 focus:outline-none"
|
||||||
|
onClick={() => window.location.href = '/auth/signin'}
|
||||||
|
>
|
||||||
|
страница за вход
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{message && <div className="text-center text-sm text-gray-500">{message}</div>}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
108
pages/auth/signin.tsx
Normal file
108
pages/auth/signin.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// pages/auth/signin.js
|
||||||
|
import { getCsrfToken, signIn } from 'next-auth/react';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import Layout from '../../components/layout';
|
||||||
|
|
||||||
|
export default function SignIn({ csrfToken }) {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Perform client-side validation if needed
|
||||||
|
if (!email || !password) {
|
||||||
|
setError('Всички полета са задължителни');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear any existing errors
|
||||||
|
setError('');
|
||||||
|
|
||||||
|
// Attempt to sign in
|
||||||
|
const result = await signIn('credentials', {
|
||||||
|
redirect: false,
|
||||||
|
username: email,
|
||||||
|
password,
|
||||||
|
callbackUrl: '/',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if (result.error) {
|
||||||
|
setError(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to the home page or callbackUrl on success
|
||||||
|
if (result.ok && result.url) {
|
||||||
|
router.push(result.url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="page">
|
||||||
|
<div className="signin">
|
||||||
|
<div className="min-h-screen flex flex-col items-center justify-center">
|
||||||
|
{/* SSO Providers */}
|
||||||
|
<div className="space-y-4 w-full px-4">
|
||||||
|
<button onClick={() => signIn('google', { callbackUrl: '/' })}
|
||||||
|
className="flex items-center justify-center w-full py-2 px-4 border border-gray-300 rounded shadow-sm text-sm text-gray-700 bg-white hover:bg-gray-50">
|
||||||
|
<img loading="lazy" height="24" width="24" alt="Google logo"
|
||||||
|
src="https://authjs.dev/img/providers/google.svg" className="mr-2" />
|
||||||
|
Влез чрез Google
|
||||||
|
</button>
|
||||||
|
{/* Add more buttons for other SSO providers here in similar style */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Email and Password Form */}
|
||||||
|
<form onSubmit={handleSubmit} className="mt-8 w-full max-w-xs px-4">
|
||||||
|
<input name="csrfToken" type="hidden" defaultValue={csrfToken} />
|
||||||
|
<div className="mb-4">
|
||||||
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700">имейл</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-6">
|
||||||
|
<label htmlFor="password" className="block text-sm font-medium text-gray-700">парола</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{error && <div className="text-red-500 text-sm">{error}</div>}
|
||||||
|
<button type="submit" className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700">
|
||||||
|
Влез
|
||||||
|
</button>
|
||||||
|
{/* <button
|
||||||
|
type="button"
|
||||||
|
className="mt-4 w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-blue-600 hover:text-blue-700 focus:outline-none"
|
||||||
|
onClick={() => router.push('/auth/reset-password')}>
|
||||||
|
Забравена парола?
|
||||||
|
</button> */}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gets called on every request
|
||||||
|
export async function getServerSideProps(context) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
csrfToken: await getCsrfToken(context),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -49,7 +49,19 @@ export const getServerSideProps = async (context) => {
|
|||||||
}
|
}
|
||||||
var url = process.env.NEXT_PUBLIC_PUBLIC_URL + "/api/data/publishers/" + context.query.id + "?include=availabilities,assignments,assignments.shift";
|
var url = process.env.NEXT_PUBLIC_PUBLIC_URL + "/api/data/publishers/" + context.query.id + "?include=availabilities,assignments,assignments.shift";
|
||||||
console.log("GET PUBLISHER FROM:" + url)
|
console.log("GET PUBLISHER FROM:" + url)
|
||||||
const { data: item } = await axios.get(url);
|
try {
|
||||||
|
const { data: item } = await axios.get(url);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error fetching publisher: " + error);
|
||||||
|
//redirect to message page with message "no account found". get user from session
|
||||||
|
const user = context.req.session.user;
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: '/message?message=Този имейл (' + user.email + ') не е регистриран. Моля свържете се с администратора.',
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// item.allShifts = item.assignments.map((a: Assignment[]) => a.shift);
|
// item.allShifts = item.assignments.map((a: Assignment[]) => a.shift);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ const AdminTranslations = () => {
|
|||||||
}, [locale]);
|
}, [locale]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
axiosInstance.post(`/api/translations/${locale}`, translations)
|
axiosInstance.post(`/api/translations/${locale}/modified`, translations)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.data.status === 'Updated') {
|
if (res.data.status === 'Updated') {
|
||||||
toast.success('Translations updated!');
|
toast.success('Translations updated!');
|
||||||
|
3
prisma/migrations/20240429133812_/migration.sql
Normal file
3
prisma/migrations/20240429133812_/migration.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `User`
|
||||||
|
ADD COLUMN `passwordHashLocalAccount` VARCHAR(191) NULL;
|
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Eventlog`
|
||||||
|
MODIFY `type` ENUM(
|
||||||
|
'AssignmentReplacementRequested', 'AssignmentReplacementAccepted', 'SentEmail', 'PasswordResetRequested', 'PasswordResetEmailConfirmed', 'PasswordResetCompleted'
|
||||||
|
) NOT NULL;
|
@ -263,6 +263,9 @@ enum EventLogType {
|
|||||||
AssignmentReplacementRequested
|
AssignmentReplacementRequested
|
||||||
AssignmentReplacementAccepted
|
AssignmentReplacementAccepted
|
||||||
SentEmail
|
SentEmail
|
||||||
|
PasswordResetRequested
|
||||||
|
PasswordResetEmailConfirmed
|
||||||
|
PasswordResetCompleted
|
||||||
}
|
}
|
||||||
|
|
||||||
model EventLog {
|
model EventLog {
|
||||||
@ -278,13 +281,14 @@ model EventLog {
|
|||||||
|
|
||||||
//user auth and session management
|
//user auth and session management
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String?
|
name String?
|
||||||
email String? @unique
|
email String? @unique
|
||||||
emailVerified DateTime?
|
emailVerified DateTime?
|
||||||
image String?
|
image String?
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
sessions Session[]
|
sessions Session[]
|
||||||
|
passwordHashLocalAccount String? // New field to store the hashed password
|
||||||
|
|
||||||
// Optional relation to Publisher
|
// Optional relation to Publisher
|
||||||
publisherId String? @unique
|
publisherId String? @unique
|
||||||
|
22
src/templates/emails/resetPass.hbs
Normal file
22
src/templates/emails/resetPass.hbs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{{!-- Subject: ССОМ: Нужен е заместник--}}
|
||||||
|
{{!-- Text: Plain text version of your email. If not provided, HTML tags will be stripped from the HTML version for the
|
||||||
|
text version. --}}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Промяна на парола</h3>
|
||||||
|
<p>Здравей, {{firstName}} {{lastName}}</p>
|
||||||
|
<p>
|
||||||
|
Получихме заявка за промяна на паролата на твоя акаунт. Ако това не си ти, моля игнорирай този имейл.
|
||||||
|
</p>
|
||||||
|
<p style="text-align: center;">
|
||||||
|
<a href="{{resetUrl}}"
|
||||||
|
target="_blank"
|
||||||
|
style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; display: inline-block; border-radius: 5px;">Смени
|
||||||
|
паролата си</a>
|
||||||
|
</p>
|
||||||
|
{{!-- <p>Thank you very much for considering my request.</p>
|
||||||
|
<p>Best regards,<br>{{name}}</p> --}}
|
||||||
|
</section>
|
||||||
|
<footer style="margin-top: 20px; text-align: center;">
|
||||||
|
<p>Изпратено на: {{sentDate}}</p>
|
||||||
|
</footer>
|
Reference in New Issue
Block a user