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

feat: add proof of mandatory vaccination (fe-only) #629

Merged
merged 14 commits into from
Mar 3, 2022
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
52 changes: 37 additions & 15 deletions iris-client-fe/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
<template>
<v-app>
<v-app-bar app color="white" flat data-test="app-bar">
<template v-if="authenticated">
<v-app-bar-nav-icon class="hidden-md-and-up" @click="showMenu = true" />
<v-menu v-model="showMenu" content-class="hidden-md-and-up">
<v-list>
<template v-for="link in links">
<v-list-item
:key="link.name"
v-if="link.meta.menu"
:to="link.path"
:exact="link.meta.menuExact"
:disabled="isLinkDisabled(link)"
:data-test="`app-bar.nav.link.${link.name}`"
>
<component v-bind:is="link.meta.menuComponent || 'span'">
<v-list-item-title>
{{ link.meta.menuName }}
</v-list-item-title>
</component>
</v-list-item>
</template>
</v-list>
</v-menu>
</template>
<v-img
alt="IRIS Logo"
class="shrink"
:class="{ shrink: true, 'hidden-sm-and-down': authenticated }"
contain
src="@/assets/logo-iris-connect.png"
transition="scale-transition"
Expand All @@ -12,27 +35,25 @@
position="0 45%"
/>
<template v-if="authenticated">
<template v-for="link in links">
<div :key="link.name" v-if="link.meta.menu">
<component
v-if="link.meta.menuComponent"
v-bind:is="link.meta.menuComponent"
:link="link"
></component>
<v-toolbar-items class="hidden-sm-and-down">
<template v-for="link in links">
<v-btn
v-else
:key="link.name"
v-if="link.meta.menu"
:to="link.path"
:exact="link.meta.menuExact"
:disabled="isLinkDisabled(link)"
text
:data-test="`app-bar.nav.link.${link.name}`"
>
{{ link.meta.menuName }}
<component v-bind:is="link.meta.menuComponent || 'span'">
{{ link.meta.menuName }}
</component>
</v-btn>
</div>
</template>
</template>
</v-toolbar-items>
<v-spacer></v-spacer>
<app-menu />
<app-settings-menu />
<user-menu
:display-name="userDisplayName"
:role="userRole"
Expand All @@ -56,20 +77,21 @@ import { routes, setInterceptRoute } from "@/router";
import UserMenu from "@/views/user-login/components/user-menu.vue";
import { RouteConfig } from "vue-router";
import { UserRole } from "@/api";
import AppMenu from "@/components/app-menu.vue";
import AppSettingsMenu from "@/components/app-settings-menu.vue";

// @todo: move user functionality to a dedicated user-module?
export default Vue.extend({
name: "App",
components: {
AppMenu,
AppSettingsMenu,
UserMenu,
},
created() {
document.title = "IRIS connect";
},
data: () => ({
links: routes,
showMenu: false,
}),
computed: {
authenticated(): boolean {
Expand Down
97 changes: 84 additions & 13 deletions iris-client-fe/src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import {
ApiResponse,
assertParamExists,
DataQuery,
RequestOptions,
} from "./common";
import { ApiResponse, assertParamExists, RequestOptions } from "./common";
import { BaseAPI } from "./base";
import { UserSession } from "@/views/user-login/user-login.store";

Expand Down Expand Up @@ -1915,12 +1910,6 @@ export interface Page<Content> {
empty?: boolean;
}

export type IrisMessageQuery = DataQuery & {
folder?: string;
};

export type PageIrisMessages = Page<IrisMessage>;

export interface IrisMessage {
id: string;
folder: string;
Expand Down Expand Up @@ -1960,6 +1949,58 @@ export interface IrisMessageHdContact {
own?: boolean;
}

export interface VRFacilityContactPerson {
firstName?: string;
lastName?: string;
eMail?: string;
phone?: string;
}

export interface VRFacility {
name?: string;
address?: Address;
contactPerson?: VRFacilityContactPerson;
}

export enum VaccinationStatus {
NOT_VACCINATED = "notVaccinated",
SUSPICIOUS_PROOF = "suspiciousProof",
}

export enum VaccinationExtendedStatus {
VACCINATED = "vaccinated",
NOT_VACCINATED = "notVaccinated",
SUSPICIOUS_PROOF = "suspiciousProof",
UNKNOWN = "unknown",
}

export interface VREmployee {
firstName?: string;
lastName?: string;
address?: Address;
vaccination?: string;
vaccinationStatus?: VaccinationStatus;
eMail?: string;
phone?: string;
dateOfBirth?: string;
sex?: Sex;
}

export type VaccinationStatusCount = {
[K in VaccinationStatus]?: number;
};

export interface VaccinationReport {
id?: string;
facility?: VRFacility;
reportedAt?: string;
vaccinationStatusCount?: VaccinationStatusCount;
}

export interface VaccinationReportDetails extends VaccinationReport {
employees?: VREmployee[];
}

/**
* IrisClientFrontendApi - object-oriented interface
* @export
Expand Down Expand Up @@ -2267,7 +2308,7 @@ export class IrisClientFrontendApi extends BaseAPI {
*/
public irisMessagesGet(
options?: RequestOptions
): ApiResponse<PageIrisMessages> {
): ApiResponse<Page<IrisMessage>> {
assertParamExists("irisMessagesGet", "folder", options?.params?.folder);
return this.apiRequest("GET", "/iris-messages", null, options);
}
Expand Down Expand Up @@ -2356,4 +2397,34 @@ export class IrisClientFrontendApi extends BaseAPI {
const path = `/iris-messages/${encodeURIComponent(messageId)}`;
return this.apiRequest("PATCH", path, { isRead: true }, options);
}

/**
*
* @summary Fetches paginated vaccination-report
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof IrisClientFrontendApi
*/
public pageVaccinationReportGet(
options?: RequestOptions
): ApiResponse<Page<VaccinationReport>> {
return this.apiRequest("GET", "/vaccination-reports", null, options);
}

/**
*
* @summary Fetches vaccination-report details
* @param {string} id for vaccination report.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof IrisClientFrontendApi
*/
public vaccinationReportDetailsGet(
id: string,
options?: RequestOptions
): ApiResponse<VaccinationReportDetails> {
assertParamExists("vaccinationReportDetailsGet", "id", id);
const path = `/vaccination-reports/${encodeURIComponent(id)}`;
return this.apiRequest("GET", path, null, options);
}
}
23 changes: 18 additions & 5 deletions iris-client-fe/src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ import globalAxios, {
CancelTokenSource,
Method,
} from "axios";
import axios from "axios";
import _castArray from "lodash/castArray";
import { join } from "@/utils/misc";
/**
*
* @export
*/
export type DataQuery = {
size: number;
page: number;
sort?: string | null;
sort?: string | string[] | null;
status?: DataRequestStatus | null;
search?: string | null;
sortOrderDesc?: boolean;
folder?: string;
};

export type RequestQuery = {
Expand Down Expand Up @@ -113,6 +114,18 @@ export const getSortAttribute = function (key: string): string {
return sortAttributes[key];
};

const mapSortAttribute = (sort: string): string => {
const sortArgs = sort.split(",");
return join([getSortAttribute(sortArgs[0]) || sortArgs[0], sortArgs[1]], ",");
};

export const mapSortAttributes = (
sort: DataQuery["sort"]
): DataQuery["sort"] => {
if (!sort) return sort;
return _castArray(sort).map(mapSortAttribute);
};

export const apiRequestBuilder =
(axiosInstance: AxiosInstance = globalAxios): ApiRequestFunction =>
<T = any, D = any>(
Expand All @@ -132,11 +145,11 @@ export const apiRequestBuilder =
};

export const cancelTokenProvider = () => {
let source: CancelTokenSource = axios.CancelToken.source();
let source: CancelTokenSource = globalAxios.CancelToken.source();
return (): CancelToken => {
try {
source.cancel("request canceled");
source = axios.CancelToken.source();
source = globalAxios.CancelToken.source();
} catch (e) {
// ignored
}
Expand Down
37 changes: 31 additions & 6 deletions iris-client-fe/src/common/normalizer.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
import { MetaData } from "@/api";
import { Page, MetaData } from "@/api";
import { Complete, normalizeData } from "@/utils/data";

export const normalizeMetaData = (
source?: MetaData,
export const normalizePage = <T>(
contentNormalizer: (source?: T, parse?: boolean) => T,
source?: Page<T>,
parse?: boolean
): MetaData => {
): Page<T> => {
return normalizeData(
source,
(normalizer) => {
const normalized: Complete<MetaData> = {
const content = normalizer("content", [], "array");
const normalized: Complete<Page<T>> = {
totalElements: normalizer("totalElements", undefined, "number"),
totalPages: normalizer("totalPages", undefined, "number"),
size: normalizer("size", undefined, "number"),
content: content.map((item) => contentNormalizer(item, parse)),
number: normalizer("number", undefined, "number"),
sort: normalizer("sort", undefined, "any"),
first: normalizer("first", undefined, "boolean"),
last: normalizer("last", undefined, "boolean"),
numberOfElements: normalizer("numberOfElements", undefined, "number"),
pageable: normalizer("pageable", undefined, "any"),
empty: normalizer("empty", undefined, "boolean"),
};
return normalized;
},
parse,
"Page"
);
};

export const normalizeMetaData = (source?: MetaData, parse?: boolean) => {
return normalizeData(
source,
(normalizer) => {
return {
createdBy: normalizer("createdBy", undefined),
createdAt: normalizer("createdAt", undefined, "dateString"),
lastModifiedBy: normalizer("lastModifiedBy", undefined),
lastModifiedAt: normalizer("lastModifiedAt", undefined, "dateString"),
};
return normalized;
},
parse,
"MetaData"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

const AppMenuProps = Vue.extend({
const AppSettingsMenuProps = Vue.extend({
inheritAttrs: false,
});
@Component
export default class AppMenu extends AppMenuProps {}
export default class AppSettingsMenu extends AppSettingsMenuProps {}
</script>
Loading