Skip to content

Commit

Permalink
SHARED:VKT(Frontend): Examiner pages WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pkoivisto committed Oct 25, 2024
1 parent 01c0d08 commit 69eac16
Show file tree
Hide file tree
Showing 37 changed files with 774 additions and 84 deletions.
68 changes: 68 additions & 0 deletions frontend/packages/shared/src/components/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import CheckBoxIcon from '@mui/icons-material/CheckBox';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import {
Autocomplete,
AutocompleteProps,
Checkbox,
createFilterOptions,
FilterOptionsState,
FormControl,
Expand Down Expand Up @@ -167,3 +170,68 @@ export const LabeledComboBox = ({
</FormControl>
);
};

type AutoCompleteMultipleComboBox = AutocompleteProps<
ComboBoxOption,
true,
false,
false
>;
export const LabeledMultipleCheckboxDropdown = ({
id,
label,
helperText,
showError,
values,
variant,
value,
onChange,
...rest
}: Omit<ComboBoxProps, 'value' | 'onChange'> &
Omit<AutoCompleteMultipleComboBox, 'options' | 'renderInput'> & {
id: string;
}) => {
const errorStyles = showError ? { color: 'error.main' } : {};
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
return (
<FormControl fullWidth error={showError}>
<label htmlFor={id}>
<Text sx={errorStyles}>
<b>{label}</b>
</Text>
</label>
<Autocomplete
id={id}
multiple
disableCloseOnSelect
options={values}
value={value}
renderOption={(props, option, { selected }) => {
const { key, ...optionProps } = props;
return (
<li key={key} {...optionProps}>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option?.label}
</li>
);
}}
renderInput={(params) => (
<TextField
{...params}
variant={variant}
error={showError}
/>
)}
onChange={onChange}
{...rest}
/>
{showError && <FormHelperText>{helperText}</FormHelperText>}
</FormControl>
);
};
1 change: 1 addition & 0 deletions frontend/packages/shared/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {
valueAsOption,
ComboBox,
LabeledComboBox,
LabeledMultipleCheckboxDropdown,
} from './ComboBox/ComboBox';
export type { AutocompleteValue } from './ComboBox/ComboBox';
export { CustomButton } from './CustomButton/CustomButton';
Expand Down
12 changes: 8 additions & 4 deletions frontend/packages/vkt/public/i18n/fi-FI/clerk.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,18 @@
"header": {
"backToOph": "Takaisin Opintopolkuun",
"logOut": "Kirjaudu ulos",
"navTabs": {
"examEvents": "Tutkintotilaisuudet"
"navigationLinks": {
"excellentLevel": "Erinomainen taito",
"goodAndSatisfactoryLevel": "Hyvä ja tyydyttävä taito"
}
}
},
"pages": {
"homepage": {
"title": "Valtionhallinnon kielitutkinnot"
"excellentLevel": {
"title": "Erinomaisen taidon kielitutkinnot"
},
"goodAndSatisfactoryLevel": {
"title": "Hyvän ja tyydyttävän taidon kielitutkinnot"
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion frontend/packages/vkt/public/i18n/fi-FI/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,12 @@
"clerkEnrollmentOverview": "Virkailija ilmoittautuminen",
"clerkExamEventCreate": "Virkailija lisää tutkintopäivä",
"clerkExamOverview": "Virkailija tutkintosivu",
"clerkHomepage": "Virkailija",
"clerkExcellentLevel": "Virkailija - erinomaisen taidon tutkinnot",
"clerkGoodAndSatisfactoryLevel": "Virkailija - hyvän ja tyydyttävän taidon tutkinnot",
"contactDetails": "Ilmoittautuminen - täytä yhteystietosi",
"educationDetails": "Ilmoittautuminen - koulutustiedot",
"examinerHomePage": "Tutkinnon vastaanottaja - hyvän ja tyydyttävän taidon tutkinnot",
"examinerDetails": "Tutkinnon vastaanottaja - omat tiedot",
"excellentLevelLanding": "Erinomaisen taidon tutkinnot",
"done": "Ilmoittautuminen - valmis",
"frontPage": "Etusivu",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const MoveModal: FC<MoveModalProps> = ({ enrollment, onCancel }) => {
});
dispatch(resetMoveEnrollment());
dispatch(resetClerkListExamEvent());
navigate(AppRoutes.ClerkHomePage, { replace: true });
navigate(AppRoutes.ClerkExcellentLevelPage, { replace: true });
}
}, [dispatch, navigate, showToast, t, moveStatus]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import {

export const ClerkExamEventGrid = () => {
// I18
const { t } = useClerkTranslation({ keyPrefix: 'vkt.pages.homepage' });
const { t } = useClerkTranslation({
keyPrefix: 'vkt.pages.excellentLevel',
});

// Redux
const { status } = useAppSelector(clerkListExamEventsSelector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const TopControls: FC = () => {
return (
<div className="columns">
<CustomButtonLink
to={AppRoutes.ClerkHomePage}
to={AppRoutes.ClerkExcellentLevelPage}
className="color-secondary-dark"
variant={Variant.Text}
startIcon={<ArrowBackIosOutlined />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
//import { useNavigate } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import { NavigationLinks } from 'shared/components';

import { useClerkTranslation, useCommonTranslation } from 'configs/i18n';
import { useAppSelector } from 'configs/redux';
import { AppRoutes } from 'enums/app';
import { clerkUserSelector } from 'redux/selectors/clerkUser';

/*const getTabForPath = (path: string) => {
if (path === AppRoutes.ClerkHomePage) {
return HeaderNavTab.ExamEvents;
} else {
return false;
}
};*/
const ExaminerNavigationLinks = () => {
const { oid } = useAppSelector(clerkUserSelector);

const AdminNavigationLinks = () => {
const { t } = useClerkTranslation({
keyPrefix: 'vkt.component.header.navigationLinks',
});
const translateCommon = useCommonTranslation();
//const navigate = useNavigate();
const excellentLevelLink = {
active: false,
href: AppRoutes.ClerkExcellentLevelPage,
label: t('excellentLevel'),
};
const goodAndSatisfactoryLevelLink = {
active: true,
href: AppRoutes.ClerkExcellentLevelPage,
href: AppRoutes.ExaminerDetailsPage.replace(/:oid/, oid),
label: t('goodAndSatisfactoryLevel'),
};

Expand All @@ -36,29 +24,27 @@ const AdminNavigationLinks = () => {
navigationAriaLabel={translateCommon(
'header.accessibility.mainNavigation',
)}
links={[excellentLevelLink, goodAndSatisfactoryLevelLink]}
links={[goodAndSatisfactoryLevelLink]}
/>
);
};

export const ClerkNavigationLinks = (): JSX.Element => {
const AdminNavigationLinks = () => {
const { t } = useClerkTranslation({
keyPrefix: 'vkt.component.header.navigationLinks',
});
const translateCommon = useCommonTranslation();
//const navigate = useNavigate();
const { isAdmin, isExaminer } = useAppSelector(clerkUserSelector);
const { pathname } = useLocation();
const excellentLevelLink = {
active: false,
active: pathname.startsWith(AppRoutes.ClerkExcellentLevelPage),
href: AppRoutes.ClerkExcellentLevelPage,
label: t('excellentLevel'),
};
// TODO Need to return different link for examiner and admin
// For examiner, the link should go to their own details
// For admin, the link should go to examiner listing
const goodAndSatisfactoryLevelLink = {
active: true,
href: AppRoutes.ClerkExcellentLevelPage,
active:
pathname.startsWith(AppRoutes.ClerkGoodAndSatisfactoryLevelPage) ||
pathname.startsWith(AppRoutes.ExaminerRoot),
href: AppRoutes.ClerkGoodAndSatisfactoryLevelPage,
label: t('goodAndSatisfactoryLevel'),
};

Expand All @@ -67,13 +53,19 @@ export const ClerkNavigationLinks = (): JSX.Element => {
navigationAriaLabel={translateCommon(
'header.accessibility.mainNavigation',
)}
links={
isAdmin
? [excellentLevelLink, goodAndSatisfactoryLevelLink]
: isExaminer
? [goodAndSatisfactoryLevelLink]
: []
}
links={[excellentLevelLink, goodAndSatisfactoryLevelLink]}
/>
);
};

export const ClerkNavigationLinks = (): JSX.Element => {
const { isAdmin, isExaminer } = useAppSelector(clerkUserSelector);

if (isAdmin) {
return <AdminNavigationLinks />;
} else if (isExaminer) {
return <ExaminerNavigationLinks />;
}

return <></>;
};
4 changes: 4 additions & 0 deletions frontend/packages/vkt/src/enums/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export enum APIEndpoints {
FeatureFlags = '/vkt/api/v1/featureFlags',
UploadPostPolicy = '/vkt/api/v1/uploadPostPolicy/:examEventId',
ClerkRefreshKoskiEducationDetails = '/vkt/api/v1/clerk/enrollment/:enrollmentId/refreshKoskiEducationDetails',
// TODO Consider using prefix /examiner instead of /tv
ExaminerDetails = '/vkt/api/v1/tv/:oid',
ExaminerDetailsInit = '/vkt/api/v1/tv/:oid/init',
}

/**
Expand Down Expand Up @@ -46,4 +49,5 @@ export enum APIError {
TicketValidationError = 'ticketValidationError',
FileUploadError = 'fileUploadError',
userAttachmentsMissing = 'userAttachmentsMissing',
ExaminerNotFound = 'examinerNotFound',
}
26 changes: 17 additions & 9 deletions frontend/packages/vkt/src/enums/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum AppConstants {
const excellentLevelRoutePrefix = '/vkt/erinomainen-taito';
const excellentLevelEnrollmentRoute =
excellentLevelRoutePrefix + '/ilmoittaudu';
const clerkExcellentLevelRoutePrefix = '/vkt/virkailija/erinomainen-taito';

export enum AppRoutes {
PublicRoot = '/vkt',
Expand All @@ -30,11 +31,22 @@ export enum AppRoutes {
PublicEnrollmentDone = excellentLevelEnrollmentRoute + '/:examEventId/valmis',
// Routes for good and satisfactory level - TODO
PublicGoodAndSatisfactoryLevelLanding = '/vkt/hyva-ja-tyydyttava-taito',
// Routes for clerk user
ClerkHomePage = '/vkt/virkailija',
ClerkExamEventCreatePage = '/vkt/virkailija/tutkintotilaisuus/luo',
ClerkExamEventOverviewPage = '/vkt/virkailija/tutkintotilaisuus/:examEventId',
ClerkEnrollmentOverviewPage = '/vkt/virkailija/tutkintotilaisuus/:examEventId/ilmoittautuminen',
ClerkRoot = '/vkt/virkailija/',
// Routes for clerk user / excellent level
ClerkExcellentLevelPage = clerkExcellentLevelRoutePrefix,
ClerkExamEventCreatePage = clerkExcellentLevelRoutePrefix +
'/tutkintotilaisuus/luo',
ClerkExamEventOverviewPage = clerkExcellentLevelRoutePrefix +
'/tutkintotilaisuus/:examEventId',
ClerkEnrollmentOverviewPage = clerkExcellentLevelRoutePrefix +
'/tutkintotilaisuus/:examEventId/ilmoittautuminen',
// Routes for clerk user / good and satisfactory level
ClerkGoodAndSatisfactoryLevelPage = '/vkt/virkailija/hyva-ja-tyydyttava-taito',
// Routes for examiner
ExaminerRoot = '/vkt/tv',
ExaminerHomePage = '/vkt/tv/:oid',
ExaminerDetailsPage = '/vkt/tv/:oid/omat-tiedot',
// Other clerk and examiner routes
ClerkLocalLogoutPage = '/vkt/cas/localLogout',
// Miscellaneous
AccessibilityStatementPage = '/vkt/saavutettavuusseloste',
Expand All @@ -57,10 +69,6 @@ export enum ExamEventToggleFilter {
Passed = 'passed',
}

export enum HeaderNavTab {
ExamEvents = 'examEvents',
}

export enum UIMode {
Edit = 'edit',
View = 'view',
Expand Down
18 changes: 13 additions & 5 deletions frontend/packages/vkt/src/hooks/useAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ export const useAuthentication = () => {
const publicUser = useAppSelector(publicUserSelector);

const activeURL = window.location.href;
const isClerkURL = activeURL.includes(AppRoutes.ClerkHomePage);
const isPublicURL = !isClerkURL;
const isClerkURL = activeURL.includes(AppRoutes.ClerkRoot);
const isExaminerURL = activeURL.includes(AppRoutes.ExaminerRoot);
const isPublicURL = !isClerkURL && !isExaminerURL;

useEffect(() => {
if (clerkUser.status === APIResponseStatus.NotStarted) {
if (isClerkURL) {
if (isClerkURL || isExaminerURL) {
dispatch(loadClerkUser());
}
}
Expand All @@ -28,11 +29,18 @@ export const useAuthentication = () => {
dispatch(loadPublicUser());
}
}
}, [clerkUser.status, publicUser.status, isClerkURL, isPublicURL, dispatch]);
}, [
clerkUser.status,
publicUser.status,
isClerkURL,
isExaminerURL,
isPublicURL,
dispatch,
]);

return {
isAuthenticated: clerkUser.isAuthenticated,
isClerkUI: isClerkURL,
isClerkUI: isClerkURL || isExaminerURL,
publicUser,
clerkUser,
};
Expand Down
9 changes: 9 additions & 0 deletions frontend/packages/vkt/src/interfaces/clerkUser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import { APIResponseStatus } from "shared/enums";

export interface ClerkUser {
oid: string;
isAdmin: boolean;
isExaminer: boolean;
}

export interface ClerkUserState extends ClerkUser {
status: APIResponseStatus;
isAuthenticated: boolean;
}
Loading

0 comments on commit 69eac16

Please sign in to comment.