Unverified Commit edea19ac authored by Philipp Berger's avatar Philipp Berger
Browse files

chore: release v1.1.14

parent 3ae6c54e
# Changelog
### 1.1.14
* **health-department:** chore: improved health department locales
* **health-department:** fix: Add more error handling in csv generation
### 1.1.13 (2021-05-26)
* **health-department:** fix: whitlisted special characters for csv
......
{
"name": "@lucaapp/web",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
{
"name": "@lucaapp/backend",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
{
"name": "@lucaapp/contact-form",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
{
"name": "@lucaapp/health-department",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
/* eslint-disable max-lines, complexity */
import React from 'react';
import moment from 'moment';
import { notification } from 'antd';
import { CSVLink } from 'react-csv';
import { useIntl } from 'react-intl';
import ReactExport from 'react-data-export';
import sanitize from 'sanitize-filename';
import { getFormattedDate, getFormattedTime } from 'utils/time';
import ReactExport from 'react-data-export';
import { sanitizeForCSV } from 'utils/sanitizer';
import { getFormattedDate, getFormattedTime } from 'utils/time';
const {
ExcelFile,
......@@ -15,6 +17,14 @@ const {
const TABLE_KEY = 'table';
const MINUTE_SECONDS = 60;
function showErrorNotification(intl) {
notification.error({
message: intl.formatMessage({
id: 'modal.contactPersonView.download.error',
}),
});
}
export const filterForTimeOverlap = (
decryptedTraces,
minTimeOverlap,
......@@ -60,24 +70,33 @@ const setUnregistredBadgeUser = intl =>
});
const getExcelDownloadDataFromTraces = (traces, intl) =>
// eslint-disable-next-line complexity
traces.map(({ userData, additionalData, checkin, checkout }) => ({
firstName: userData ? sanitizeForCSV(userData.fn) : '',
lastName: userData
? sanitizeForCSV(userData.ln)
: setUnregistredBadgeUser(intl),
phone: userData ? sanitizeForCSV(userData.pn) : '',
email: userData ? sanitizeForCSV(userData.e) : '',
street: userData ? sanitizeForCSV(userData.st) : '',
houseNumber: userData ? sanitizeForCSV(userData.hn) : '',
city: userData ? sanitizeForCSV(userData.c) : '',
postalCode: userData ? sanitizeForCSV(userData.pc) : '',
checkinDate: checkin ? getFormattedDate(checkin) : '',
checkinTime: checkin ? getFormattedTime(checkin) : '',
checkoutDate: checkout ? getFormattedDate(checkout) : '',
checkoutTime: checkout ? getFormattedTime(checkout) : '',
additionalData,
}));
traces
// eslint-disable-next-line complexity
.map(({ userData, additionalData, checkin, checkout }) => {
try {
return {
firstName: userData ? sanitizeForCSV(userData.fn) : '',
lastName: userData
? sanitizeForCSV(userData.ln)
: setUnregistredBadgeUser(intl),
phone: userData ? sanitizeForCSV(userData.pn) : '',
email: userData ? sanitizeForCSV(userData.e) : '',
street: userData ? sanitizeForCSV(userData.st) : '',
houseNumber: userData ? sanitizeForCSV(userData.hn) : '',
city: userData ? sanitizeForCSV(userData.c) : '',
postalCode: userData ? sanitizeForCSV(userData.pc) : '',
checkinDate: checkin ? getFormattedDate(checkin) : '',
checkinTime: checkin ? getFormattedTime(checkin) : '',
checkoutDate: checkout ? getFormattedDate(checkout) : '',
checkoutTime: checkout ? getFormattedTime(checkout) : '',
additionalData,
};
} catch {
showErrorNotification(intl);
return null;
}
})
.filter(entry => entry !== null);
const columnKeys = [
'firstName',
......@@ -211,44 +230,57 @@ const getCSVDownloadDataFromTraces = (traces, location, intl) => [
intl.formatMessage({ id: 'contactPersonTable.checkoutTime' }),
intl.formatMessage({ id: 'contactPersonTable.additionalData' }),
],
// eslint-disable-next-line complexity
...traces.map(({ userData, additionalData, checkin, checkout }) => [
location.name ? sanitizeForCSV(location.name) : '',
location.type
? intl.formatMessage({
id: `history.location.category.${location.type}`,
})
: '',
location.isIndoor
? intl.formatMessage({ id: 'history.label.indoor' })
: intl.formatMessage({ id: 'history.label.outdoor' }),
location.streetName ? sanitizeForCSV(location.streetName) : '',
location.streetNr ? sanitizeForCSV(location.streetNr) : '',
location.zipCode ? sanitizeForCSV(location.zipCode) : '',
location.city ? sanitizeForCSV(location.city) : '',
location.firstName ? sanitizeForCSV(location.firstName) : '',
location.lastName ? sanitizeForCSV(location.lastName) : '',
location.phone ? sanitizeForCSV(location.phone) : '',
userData ? sanitizeForCSV(userData.fn) : '',
userData ? sanitizeForCSV(userData.ln) : setUnregistredBadgeUser(intl),
userData ? sanitizeForCSV(userData.pn) : '',
userData ? sanitizeForCSV(userData.e) : '',
userData ? sanitizeForCSV(userData.st) : '',
userData ? sanitizeForCSV(userData.hn) : '',
userData ? sanitizeForCSV(userData.c) : '',
userData ? sanitizeForCSV(userData.pc) : '',
checkin ? getFormattedDate(checkin) : '',
checkin ? getFormattedTime(checkin) : '',
checkout ? getFormattedDate(checkout) : '',
checkout ? getFormattedTime(checkout) : '',
additionalData
? Object.keys(additionalData).map(key =>
sanitizeForCSV(
`${formatAdditionalDataKey(key, intl)}: ${additionalData[key]}`
)
)
: null,
]),
...traces
// eslint-disable-next-line complexity
.map(({ userData, additionalData, checkin, checkout }) => {
try {
return [
location.name ? sanitizeForCSV(location.name) : '',
location.type
? intl.formatMessage({
id: `history.location.category.${location.type}`,
})
: '',
location.isIndoor
? intl.formatMessage({ id: 'history.label.indoor' })
: intl.formatMessage({ id: 'history.label.outdoor' }),
location.streetName ? sanitizeForCSV(location.streetName) : '',
location.streetNr ? sanitizeForCSV(location.streetNr) : '',
location.zipCode ? sanitizeForCSV(location.zipCode) : '',
location.city ? sanitizeForCSV(location.city) : '',
location.firstName ? sanitizeForCSV(location.firstName) : '',
location.lastName ? sanitizeForCSV(location.lastName) : '',
location.phone ? sanitizeForCSV(location.phone) : '',
userData ? sanitizeForCSV(userData.fn) : '',
userData
? sanitizeForCSV(userData.ln)
: setUnregistredBadgeUser(intl),
userData ? sanitizeForCSV(userData.pn) : '',
userData ? sanitizeForCSV(userData.e) : '',
userData ? sanitizeForCSV(userData.st) : '',
userData ? sanitizeForCSV(userData.hn) : '',
userData ? sanitizeForCSV(userData.c) : '',
userData ? sanitizeForCSV(userData.pc) : '',
checkin ? getFormattedDate(checkin) : '',
checkin ? getFormattedTime(checkin) : '',
checkout ? getFormattedDate(checkout) : '',
checkout ? getFormattedTime(checkout) : '',
additionalData
? Object.keys(additionalData).map(key =>
sanitizeForCSV(
`${formatAdditionalDataKey(key, intl)}: ${
additionalData[key]
}`
)
)
: null,
];
} catch {
showErrorNotification(intl);
return null;
}
})
.filter(entry => entry !== null),
];
export const CSVDownload = ({ traces, location }) => {
......@@ -409,39 +441,46 @@ const getSormasDownloadDataFromTraces = (traces, location, intl) => [
'ownershipHandedOver',
'returningTraveler',
],
...traces.map(({ userData, checkin, additionalData }) => {
const entry = new Array(134);
entry[2] = 'CORONAVIRUS';
entry[3] = 'COVID-19';
entry[4] = moment().format('DD.MM.YYYY');
entry[11] = moment.unix(checkin).format('DD.MM.YYYY');
entry[12] = 'TRACING_APP';
entry[14] = 'OTHER';
entry[15] = 'luca';
entry[19] = 'UNCONFIRMED';
entry[20] = 'ACTIVE';
entry[21] = 'FOLLOW_UP';
entry[25] = `${sanitizeForCSV(location.name)} / ${sanitizeForCSV(
location.streetName
)} ${sanitizeForCSV(location.streetNr)} / ${sanitizeForCSV(
location.zipCode
)} ${sanitizeForCSV(location.state)} / ${sanitizeForCSV(
formatAdditionalData(additionalData, intl)
)}`;
entry[37] = userData ? sanitizeForCSV(userData.fn) : '';
entry[38] = userData
? sanitizeForCSV(userData.ln)
: setUnregistredBadgeUser(intl);
entry[43] = 'UNKNOWN';
entry[68] = userData ? sanitizeForCSV(userData.pn) : '';
entry[74] = userData ? sanitizeForCSV(userData.c) : '';
entry[79] = userData ? sanitizeForCSV(userData.pc) : '';
entry[80] = userData ? sanitizeForCSV(userData.st) : '';
entry[81] = userData ? sanitizeForCSV(userData.hn) : '';
entry[83] = 'HOME';
entry[88] = userData ? sanitizeForCSV(userData.e) : '';
return entry.map(field => field?.trim() || '');
}),
...traces
.map(({ userData, checkin, additionalData }) => {
try {
const entry = new Array(134);
entry[2] = 'CORONAVIRUS';
entry[3] = 'COVID-19';
entry[4] = moment().format('DD.MM.YYYY');
entry[11] = moment.unix(checkin).format('DD.MM.YYYY');
entry[12] = 'TRACING_APP';
entry[14] = 'OTHER';
entry[15] = 'luca';
entry[19] = 'UNCONFIRMED';
entry[20] = 'ACTIVE';
entry[21] = 'FOLLOW_UP';
entry[25] = `${sanitizeForCSV(location.name)} / ${sanitizeForCSV(
location.streetName
)} ${sanitizeForCSV(location.streetNr)} / ${sanitizeForCSV(
location.zipCode
)} ${sanitizeForCSV(location.state)} / ${sanitizeForCSV(
formatAdditionalData(additionalData, intl)
)}`;
entry[37] = userData ? sanitizeForCSV(userData.fn) : '';
entry[38] = userData
? sanitizeForCSV(userData.ln)
: setUnregistredBadgeUser(intl);
entry[43] = 'UNKNOWN';
entry[68] = userData ? sanitizeForCSV(userData.pn) : '';
entry[74] = userData ? sanitizeForCSV(userData.c) : '';
entry[79] = userData ? sanitizeForCSV(userData.pc) : '';
entry[80] = userData ? sanitizeForCSV(userData.st) : '';
entry[81] = userData ? sanitizeForCSV(userData.hn) : '';
entry[83] = 'HOME';
entry[88] = userData ? sanitizeForCSV(userData.e) : '';
return entry;
} catch {
showErrorNotification(intl);
return null;
}
})
.filter(entry => entry !== null),
];
export const SormasDownload = ({ traces, location }) => {
......
......@@ -54,7 +54,7 @@
"modal.trackInfection.info": "luca users can share their histrory with your health department. Therefore you need to the tan of that user which is displayed in the history of the luca app.",
"modal.trackInfection.button": "Track",
"modal.trackInfection.tan.error.required": "Please enter the users TAN",
"modal.trackInfection.tan.error.min": "This part of the user TAN can't be unter 4 character",
"modal.trackInfection.tan.error.min": "This part of the user TAN can't be under 4 character",
"modal.trackInfection.error.staticTracing": "An error occured while processing static tan",
"modal.trackInfection.error.userTracing": "An error occured while processing user tan",
"modal.trackInfection.error.emptyUserTracing": "User history is empty. The process was not created.",
......@@ -142,9 +142,9 @@
"processTable.status": "Process status",
"processTable.location": "Location",
"processTable.person": "Person",
"processTable.done": "Completed",
"processTable.done": "Approved",
"processTable.open": "Open",
"processTable.partlyDone": "Partly completed",
"processTable.partlyDone": "Partly approved",
"processTable.toggleComplete": "CLOSE PROCESS",
"processTable.toggleIncomplete": "OPEN PROCESS",
"processTable.empty": "There are currently no processes here",
......@@ -201,7 +201,7 @@
"history.label.outdoor": "Outdoor",
"license.name": "Name",
"license.version": "Version",
"license.license": "Licence",
"license.license": "License",
"license.license.full": "View all licenses",
"modal.sormas.toCase": "Open in SORMAS",
"modal.sormas.failedExport.headline": "Transfer failed!",
......@@ -220,7 +220,7 @@
"export.sormas.label": "Export to SORMAS",
"modal.sormas.credentialstep.connect.header": "Connect to SORMAS",
"contactPersonTable.selectAll": "Select all",
"modal.sormas.selectstep.selectCase.person": "SORMAS case: ",
"modal.sormas.selectstep.selectCase.person": "SORMAS case:",
"modal.sormas.credentialstep.versionFailure": "Your current SORMAS version is not supported.",
"contactPersonTable.timeOverlap": "Time overlaps for at least:",
"contactPersonTable.minutes": "Min",
......@@ -245,9 +245,9 @@
"employeeTable.selectRole.error": "Error while changing role for {employee}!",
"processTable.createdAt": "Created at",
"processTable.assignee": "Assignee",
"processTable.selectAssignee.success": "The process is successfully assigned to {assignee}.",
"processTable.selectAssignee.success": "The process was assigned to {assignee}.",
"processTable.selectAssignee.error": "An error occurred while assigning {assignee}!",
"processTable.selectAssignee.unassigned": "Unassigned",
"processTable.selectAssignee.unassigned": "Not assigned",
"processTable.selectAssignee.unassigned.success": "The process is unassigned.",
"processTable.selectAssignee.unassigned.error": "An error occurred while unassigning the process.",
"processTable.selectAssignee.notFound": "Not found",
......@@ -265,7 +265,7 @@
"sort.type.all": "All",
"sort.assignee.headline": "Assignee",
"sort.assignee.all": "All",
"sort.assignee.no": "Unassigned",
"sort.assignee.no": "Not assigned",
"sort.assignee.yes": "Assigned",
"contactPersonTable.showAll": "Show all",
"sort.status.partlyApproved": "Partly approved",
......@@ -298,5 +298,26 @@
"history.label.confirmStatus": "Approval status",
"download.sormas.customizeRegion.restricted": "These values for SORMAS exports can be adjusted by an administrator.",
"groupSearch.form.error.zipCodeLength": "Please enter 5 numbers.",
"groupSearch.form.zipCode.placeholder": "Optional zip code"
"groupSearch.form.zipCode.placeholder": "Search ZIP",
"modal.dataRequest.error.timeframe": "Please check the selected time frame for validity",
"modal.dataRequest.info.timeframe": "Choose time frame for your request",
"modal.dataRequest.form.error.required": "Required",
"modal.dataRequest.back": "back",
"modal.dataRequest.from": "From:",
"modal.dataRequest.to": "To:",
"groupSearch.info.search": "Name and address of the location",
"groupSearch.form.button.chooseTimeframe": "choose time frame",
"modal.dataRequest.time.clock": " ",
"processTable.deleteConfirm": "Do you really want to delete this trace?",
"groupSearch.form.button.search": "search",
"modal.dataRequest.date.placeholder": "Select a date",
"modal.dataRequest.time.placeholder": "Select a time",
"deleteProcess.cancel": "HARD DELETE",
"processTable.deleteProcess": "DELETE",
"processDetails.updateProcessNote": "Edit",
"processDetails.saveProcessNote": "Update",
"processDetails.cancelProcessNote": "Cancel",
"processDetails.labelNote": "Note",
"processDetails.note.sucess": "Note added/updated successfully",
"modal.contactPersonView.download.error": "An error occurred while generating the files."
}
\ No newline at end of file
import { mapValues } from 'lodash';
export const sanitizeForCSV = value => {
if (
typeof value === 'number' ||
typeof value === 'undefined' ||
typeof value === 'boolean' ||
value === null
)
if (typeof value === 'object' && value !== null)
return mapValues(value, sanitizeForCSV);
if (typeof value !== 'string') {
return value;
if (typeof value === 'object') return mapValues(value, sanitizeForCSV);
}
// sanitze general
const sanitizedStringGeneral = value
......
{
"name": "@lucaapp/locations",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
{
"name": "@lucaapp/scanner",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
{
"name": "@lucaapp/webapp",
"version": "1.1.13",
"version": "1.1.14",
"private": true,
"license": "Apache-2.0",
"author": "Culture4Life <hello@luca-app.de> (https://www.luca-app.de/)",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment