Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KB-273 moved employment module in ints place, fixes, added some icons #373

Merged
merged 2 commits into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions src/actions/menu.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,22 @@ import { cookies } from 'next/headers';
import { getUserDetails } from './auth.actions';
import { MenuItemMeta } from '@/types/menu-item-meta';
import { MODULES } from '@/lib/constants/modules';
import { ProfileArea } from '@/types/enums/profile-area';
import { Module } from '@/types/module';

const OLD_CAMPUS_URL = process.env.OLD_CAMPUS_URL;

const composeUrlToOldCampus = (profileArea: string, mode: string) => {
return `${OLD_CAMPUS_URL}/${profileArea}/index.php?mode=${mode}`;
const OLD_CAMPUS_PROFILE_AREA = {
[ProfileArea.Employee]: 'tutor',
[ProfileArea.Student]: 'student',
};

const composeUrl = (module: Module, profileArea: ProfileArea) => {
if (module.isExternal) {
return `${OLD_CAMPUS_URL}/${OLD_CAMPUS_PROFILE_AREA[profileArea]}/index.php?mode=${module.name}`;
}

return `/module/${module.name}`;
};

const getStaticMenuItems = async (): Promise<MenuItemMeta[][]> => {
Expand Down Expand Up @@ -45,12 +56,6 @@ const getStaticMenuItems = async (): Promise<MenuItemMeta[][]> => {
url: '/feedback',
isExternal: false,
},
{
name: 'employment-system',
title: t('employment-system'),
url: '/employment-system',
isExternal: false,
},
{
name: 'settings',
title: t('settings'),
Expand Down Expand Up @@ -83,14 +88,14 @@ const getModuleMenuItems = async (): Promise<MenuItemMeta[]> => {

const t = await getTranslations('global.modules');

const areaType = userDetails.studentProfile ? 'student' : 'tutor';
const profileArea = userDetails.studentProfile ? ProfileArea.Student : ProfileArea.Employee;
const availableModules = MODULES.filter((module) => jwtPayload.modules.includes(module.name));

return availableModules.map((module) => {
return {
name: module.name,
title: t(module.name),
url: composeUrlToOldCampus(areaType, module.name),
url: composeUrl(module, profileArea),
isExternal: module.isExternal,
} satisfies MenuItemMeta;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Heading1 } from '@/components/typography/headers';
import { SubLayout } from '../sub-layout';
import { SubLayout } from '../../sub-layout';
import { useTranslations } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import { Paragraph } from '@/components/typography/paragraph';
Expand All @@ -16,7 +16,7 @@ export async function generateMetadata({ params: { locale } }: any) {
};
}

export default function EmploymentSystemPage() {
export default function EmploymentPage() {
const t = useTranslations(INTL_NAMESPACE);

return (
Expand Down
25 changes: 23 additions & 2 deletions src/components/app-sidebar/menu-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
import { Gear, House, UserCircle, CircleWavyCheck, Scroll, Exam, ChatCenteredText, BagSimple } from '@/app/images';
import {
Gear,
House,
UserCircle,
CircleWavyCheck,
Exam,
ChatCenteredText,
BagSimple,
ListNumbers,
GraduationCap,
Books,
ChartBarHorizontal,
IdentificationBadge,
Scroll,
} from '@/app/images';

// TODO: Add icons for modules
export const menuIcon: Map<string, React.ReactNode> = new Map([
Expand All @@ -7,7 +21,14 @@ export const menuIcon: Map<string, React.ReactNode> = new Map([
['current-superintendence', CircleWavyCheck],
['certification-results', Exam],
['feedback', ChatCenteredText],
['employment-system', BagSimple],
['employment', BagSimple],
['settings', Gear],
['sdchoice2021admin', ListNumbers],
['sdchoice2021moderator', ListNumbers],
['sdchoice2021nmv', ListNumbers],
['vedomost', GraduationCap],
['mob', Books],
['vote', ChartBarHorizontal],
['contacts', IdentificationBadge],
['notice-board', Scroll],
]);
1 change: 1 addition & 0 deletions src/lib/constants/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ export const MODULES: Module[] = [
{ name: 'sdchoice2021admin', isExternal: true },
{ name: 'sdchoice2021moderator', isExternal: true },
{ name: 'sdchoice2021nmv', isExternal: true },
{ name: 'employment', isExternal: false },
];
12 changes: 2 additions & 10 deletions src/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,12 @@
"adminpanel": "Admin panel",
"sdchoice2021admin": "Discipline selection (Admin)",
"sdchoice2021moderator": "Discipline selection (Moderator)",
"sdchoice2021nmv": "Discipline selection (NMV)"
"sdchoice2021nmv": "Discipline selection (NMV)",
"employment": "Employment system"
},
"menu": {
"main": "Home",
"profile": "Profile",
"current-superintendence": "Current superintendence",
"certification-results": "Certification results",
"disciplines": "Disciplines",
"term": "Term",
"methodological-support": "Methodological support",
"poll": "Poll",
"curriculum": "Curriculum",
"lecturer-contacts": "Contacts",
"feedback": "Send feedback",
"settings": "Settings",
"user-manual": "User manual",
Expand All @@ -95,7 +88,6 @@
"documents": "KPI documents",
"terms-of-service": "Terms of service",
"contacts": "Contacts",
"employment-system": "Employment system",
"notice-board": "Notice board"
},
"user-category": {
Expand Down
12 changes: 2 additions & 10 deletions src/messages/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,12 @@
"adminpanel": "Адміністративна панель",
"sdchoice2021admin": "Вибір дисциплін (адм.)",
"sdchoice2021moderator": "Вибір дисциплін (мод.)",
"sdchoice2021nmv": "Вибір дисциплін (НМВ)"
"sdchoice2021nmv": "Вибір дисциплін (НМВ)",
"employment": "Система працевлаштування"
},
"menu": {
"main": "Головна",
"profile": "Профіль",
"current-superintendence": "Поточний контроль",
"certification-results": "Результати атестації",
"disciplines": "Вибір дисциплін",
"term": "Сесія",
"methodological-support": "Метод. забезпечення",
"poll": "Опитування",
"curriculum": "Навчальний план",
"lecturer-contacts": "Контакти",
"feedback": "Відправити відгук",
"settings": "Налаштування",
"user-manual": "Інструкція користувача",
Expand All @@ -95,7 +88,6 @@
"documents": "Документи КПІ",
"terms-of-service": "Правила використання",
"contacts": "Контакти",
"employment-system": "Система працевлаштування",
"notice-board": "Оголошення"
},
"user-category": {
Expand Down
29 changes: 11 additions & 18 deletions src/middleware/authorization.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,39 @@
import { NextRequest } from 'next/server';
import { MODULES_BASE_PATHS } from './contants';
import { getAuthInfo, gotoRoot, matchesAnyUrl } from './utils';
import { MODULES_BASE_PATH } from './contants';
import { getAuthInfo, gotoNotFound, matchesUrl } from './utils';
import UrlPattern from 'url-pattern';
import { getUserDetails } from '@/actions/auth.actions';
import { ProfileArea } from '@/types/enums/profile-area';
import { intlMiddleware } from './intl.middleware';

const isAuthorized = async (request: NextRequest) => {
const payload = getAuthInfo(request);
// TODO: Refactor to not use actions here
const user = await getUserDetails();

if (!payload || !user) {
if (!payload) {
return false;
}

const pattern = new UrlPattern('/:locale/:profileArea/module/:module(/*)');
const pattern = new UrlPattern('/:locale/module/:module(/*)');

const match = pattern.match(request.nextUrl.pathname);

if (!match) {
return false;
}

const { profileArea, module } = match;

if (!!user.studentProfile && profileArea !== ProfileArea.Student) {
return false;
}
const { module } = match;

return payload.modules.includes(module);
};

export const authorizationMiddleware = (request: NextRequest) => {
export const authorizationMiddleware = async (request: NextRequest) => {
// Skip authorization check for non-restricted pages
if (!matchesAnyUrl(request, MODULES_BASE_PATHS, false)) {
if (!matchesUrl(request, MODULES_BASE_PATH, false)) {
return intlMiddleware(request);
}

if (!isAuthorized(request)) {
// Do we need a dedicated "Unauthorized" page?
return gotoRoot(request);
const authorized = await isAuthorized(request);

if (!authorized) {
return gotoNotFound(request);
}

return intlMiddleware(request);
Expand Down
5 changes: 2 additions & 3 deletions src/middleware/contants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ProfileArea } from '@/types/enums/profile-area';

export const ROOT_PATH = '/';
export const NOT_FOUND_PATH = '/not-found';
export const LOGIN_PATH = '/login';
export const PUBLIC_PATHS = ['/login', '/password-reset', '/curator-search', '/complaints', '/support', '/faq'];
export const MODULES_BASE_PATHS = [`/${ProfileArea.Student}/module`, `/${ProfileArea.Employee}/module`];
export const MODULES_BASE_PATH = '/module';
export const CODE_OF_HONOR_PATH = '/accept-code-of-honor';
9 changes: 5 additions & 4 deletions src/middleware/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { LOCALES } from '@/i18n/routing';
import { NextRequest, NextResponse } from 'next/server';
import { trim } from 'radash';
import UrlPattern from 'url-pattern';
import { LOGIN_PATH, ROOT_PATH } from './contants';
import { LOGIN_PATH, NOT_FOUND_PATH, ROOT_PATH } from './contants';
import { getJWTPayload } from '@/lib/jwt';
import { CampusJwtPayload } from '@/types/campus-jwt-payload';

Expand All @@ -17,15 +17,16 @@ export const redirectWithIntl = (request: NextRequest, path: string) => {

export const gotoRoot = (request: NextRequest) => redirectWithIntl(request, ROOT_PATH);
export const gotoLogin = (request: NextRequest) => redirectWithIntl(request, LOGIN_PATH);
export const gotoNotFound = (request: NextRequest) => NextResponse.rewrite(new URL(NOT_FOUND_PATH, request.url));

export const isRoot = (request: NextRequest) => {
const pattern = new UrlPattern('(/)');

return !!pattern.match(request.nextUrl.pathname);
};

export const matchesUrl = (request: NextRequest, url: string, strict = false) => {
const pattern = new UrlPattern(`/:locale${url}${strict ? '(/*)' : ''}`);
export const matchesUrl = (request: NextRequest, url: string, strict = true) => {
const pattern = new UrlPattern(`/:locale${url}${strict ? '' : '(/*)'}`);

const match = pattern.match(request.nextUrl.pathname);

Expand All @@ -36,7 +37,7 @@ export const matchesUrl = (request: NextRequest, url: string, strict = false) =>
return LOCALES.includes(match.locale);
};

export const matchesAnyUrl = (request: NextRequest, urls: string[], strict = false) =>
export const matchesAnyUrl = (request: NextRequest, urls: string[], strict = true) =>
urls.some((url) => matchesUrl(request, url, strict));

export const getAuthInfo = (request: NextRequest) => {
Expand Down
Loading