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

Prettier Setup #1068

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
14 changes: 0 additions & 14 deletions .editorconfig

This file was deleted.

10 changes: 10 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Ignore www/dist, manual_lib, json
www/dist
www/manual_lib
www/json

# This is the pattern to check only www directory
# Ignore all
/*
# but don't ignore all the files in www directory
!/www
10 changes: 10 additions & 0 deletions .prettierrc
jiji14 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"bracketSameLine": true,
"endOfLine": "lf",
"semi": true
}
3 changes: 2 additions & 1 deletion package.serve.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"typescript": "^5.0.3",
"url-loader": "^4.1.1",
"webpack": "^5.0.1",
"webpack-cli": "^5.0.1"
"webpack-cli": "^5.0.1",
"prettier": "3.0.3"
},
"dependencies": {
"@react-navigation/native": "^6.1.7",
Expand Down
182 changes: 100 additions & 82 deletions www/js/survey/enketo/AddedNotesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,68 @@
Notes are added from the AddNoteButton and are derived from survey responses.
*/

import React, { useContext, useState } from "react";
import moment from "moment";
import { Modal } from "react-native"
import { Text, Button, DataTable, Dialog } from "react-native-paper";
import { LabelTabContext } from "../../diary/LabelTab";
import { getFormattedDateAbbr, isMultiDay } from "../../diary/diaryHelper";
import { Icon } from "../../components/Icon";
import EnketoModal from "./EnketoModal";
import { useTranslation } from "react-i18next";
import React, { useContext, useState } from 'react';
import moment from 'moment';
import { Modal } from 'react-native';
import { Text, Button, DataTable, Dialog } from 'react-native-paper';
import { LabelTabContext } from '../../diary/LabelTab';
import { getFormattedDateAbbr, isMultiDay } from '../../diary/diaryHelper';
import { Icon } from '../../components/Icon';
import EnketoModal from './EnketoModal';
import { useTranslation } from 'react-i18next';

type Props = {
timelineEntry: any,
additionEntries: any[],
}
timelineEntry: any;
additionEntries: any[];
};
const AddedNotesList = ({ timelineEntry, additionEntries }: Props) => {

const { t } = useTranslation();
const { repopulateTimelineEntry } = useContext(LabelTabContext);
const [confirmDeleteModalVisible, setConfirmDeleteModalVisible] = useState(false);
const [surveyModalVisible, setSurveyModalVisible] = useState(false);
const [editingEntry, setEditingEntry] = useState(null);

function setDisplayDt(entry) {
const timezone = timelineEntry.start_local_dt?.timezone
|| timelineEntry.enter_local_dt?.timezone
|| timelineEntry.end_local_dt?.timezone
|| timelineEntry.exit_local_dt?.timezone;
const timezone =
timelineEntry.start_local_dt?.timezone ||
timelineEntry.enter_local_dt?.timezone ||
timelineEntry.end_local_dt?.timezone ||
timelineEntry.exit_local_dt?.timezone;
const beginTs = entry.data.start_ts || entry.data.enter_ts;
const stopTs = entry.data.end_ts || entry.data.exit_ts;
let d;
if (isMultiDay(beginTs, stopTs)) {
const beginTsZoned = moment.parseZone(beginTs*1000).tz(timezone);
const stopTsZoned = moment.parseZone(stopTs*1000).tz(timezone);
const beginTsZoned = moment.parseZone(beginTs * 1000).tz(timezone);
const stopTsZoned = moment.parseZone(stopTs * 1000).tz(timezone);
d = getFormattedDateAbbr(beginTsZoned.toISOString(), stopTsZoned.toISOString());
}
const begin = moment.parseZone(beginTs*1000).tz(timezone).format('LT');
const stop = moment.parseZone(stopTs*1000).tz(timezone).format('LT');
return entry.displayDt = {
const begin = moment
.parseZone(beginTs * 1000)
.tz(timezone)
.format('LT');
const stop = moment
.parseZone(stopTs * 1000)
.tz(timezone)
.format('LT');
return (entry.displayDt = {
date: d,
time: begin + " - " + stop
}
time: begin + ' - ' + stop,
});
}

function deleteEntry(entry) {
console.log("Deleting entry", entry);
console.log('Deleting entry', entry);

const dataKey = entry.key || entry.metadata.key;
const data = entry.data;
const index = additionEntries.indexOf(entry);
data.status = 'DELETED';

return window['cordova'].plugins.BEMUserCache
.putMessage(dataKey, data)
.then(() => {
additionEntries.splice(index, 1);
setConfirmDeleteModalVisible(false);
setEditingEntry(null);
});
return window['cordova'].plugins.BEMUserCache.putMessage(dataKey, data).then(() => {
additionEntries.splice(index, 1);
setConfirmDeleteModalVisible(false);
setEditingEntry(null);
});
}

function confirmDeleteEntry(entry) {
Expand Down Expand Up @@ -90,66 +94,80 @@ const AddedNotesList = ({ timelineEntry, additionEntries }: Props) => {
}

const sortedEntries = additionEntries?.sort((a, b) => a.data.start_ts - b.data.start_ts);
return (<>
<DataTable>
{sortedEntries?.map((entry, index) => {
const isLastRow = (index == additionEntries.length - 1);
return (
<DataTable.Row key={index} style={styles.row(isLastRow)}>
<DataTable.Cell onPress={() => editEntry(entry)}
style={[styles.cell, {flex: 5, pointerEvents: 'auto'}]}
textStyle={{fontSize: 12, fontWeight: 'bold'}}>
<Text numberOfLines={2}>{entry.data.label}</Text>
</DataTable.Cell>
<DataTable.Cell onPress={() => editEntry(entry)}
style={[styles.cell, {flex: 4}]}
textStyle={{fontSize: 12, lineHeight: 12}}>
<Text style={{display: 'flex'}}>{entry.displayDt?.date}</Text>
<Text style={{display: 'flex'}}>{entry.displayDt?.time || setDisplayDt(entry)}</Text>
</DataTable.Cell>
<DataTable.Cell onPress={() => confirmDeleteEntry(entry)}
style={[styles.cell, {flex: 1}]}>
<Icon icon="delete" size={18} />
</DataTable.Cell>
</DataTable.Row>
)
})}
</DataTable>
<EnketoModal visible={surveyModalVisible} onDismiss={onModalDismiss}
onResponseSaved={onEditedResponse} surveyName={editingEntry?.data.name}
opts={{
timelineEntry,
prefilledSurveyResponse: editingEntry?.data.xmlResponse,
dataKey: editingEntry?.key || editingEntry?.metadata?.key,
}} />
<Modal visible={confirmDeleteModalVisible} transparent={true} onDismiss={dismissConfirmDelete}>
<Dialog visible={confirmDeleteModalVisible} onDismiss={dismissConfirmDelete}>
<Dialog.Title>{ t('diary.delete-entry-confirm') }</Dialog.Title>
<Dialog.Content>
<Text style={{fontWeight: 'bold'}}>{editingEntry?.data?.label}</Text>
<Text>{editingEntry?.displayDt?.date}</Text>
<Text>{editingEntry?.displayDt?.time}</Text>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={() => deleteEntry(editingEntry)}>Delete</Button>
<Button onPress={() => dismissConfirmDelete()}>Cancel</Button>
</Dialog.Actions>
</Dialog>
</Modal>
</>);
return (
<>
<DataTable>
{sortedEntries?.map((entry, index) => {
const isLastRow = index == additionEntries.length - 1;
return (
<DataTable.Row key={index} style={styles.row(isLastRow)}>
<DataTable.Cell
onPress={() => editEntry(entry)}
style={[styles.cell, { flex: 5, pointerEvents: 'auto' }]}
textStyle={{ fontSize: 12, fontWeight: 'bold' }}>
<Text numberOfLines={2}>{entry.data.label}</Text>
</DataTable.Cell>
<DataTable.Cell
onPress={() => editEntry(entry)}
style={[styles.cell, { flex: 4 }]}
textStyle={{ fontSize: 12, lineHeight: 12 }}>
<Text style={{ display: 'flex' }}>{entry.displayDt?.date}</Text>
<Text style={{ display: 'flex' }}>
{entry.displayDt?.time || setDisplayDt(entry)}
</Text>
</DataTable.Cell>
<DataTable.Cell
onPress={() => confirmDeleteEntry(entry)}
style={[styles.cell, { flex: 1 }]}>
<Icon icon="delete" size={18} />
</DataTable.Cell>
</DataTable.Row>
);
})}
</DataTable>
<EnketoModal
visible={surveyModalVisible}
onDismiss={onModalDismiss}
onResponseSaved={onEditedResponse}
surveyName={editingEntry?.data.name}
opts={{
timelineEntry,
prefilledSurveyResponse: editingEntry?.data.xmlResponse,
dataKey: editingEntry?.key || editingEntry?.metadata?.key,
}}
/>
<Modal
visible={confirmDeleteModalVisible}
transparent={true}
onDismiss={dismissConfirmDelete}>
<Dialog visible={confirmDeleteModalVisible} onDismiss={dismissConfirmDelete}>
<Dialog.Title>{t('diary.delete-entry-confirm')}</Dialog.Title>
<Dialog.Content>
<Text style={{ fontWeight: 'bold' }}>{editingEntry?.data?.label}</Text>
<Text>{editingEntry?.displayDt?.date}</Text>
<Text>{editingEntry?.displayDt?.time}</Text>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={() => deleteEntry(editingEntry)}>Delete</Button>
<Button onPress={() => dismissConfirmDelete()}>Cancel</Button>
</Dialog.Actions>
</Dialog>
</Modal>
</>
);
};

const styles:any = {
const styles: any = {
row: (isLastRow) => ({
minHeight: 36,
height: 36,
borderBottomWidth: (isLastRow ? 0 : 1),
borderBottomWidth: isLastRow ? 0 : 1,
borderBottomColor: 'rgba(0,0,0,0.1)',
pointerEvents: 'all',
}),
cell: {
pointerEvents: 'auto',
},
}
};

export default AddedNotesList;
74 changes: 36 additions & 38 deletions www/js/survey/enketo/enketoHelper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getAngularService } from "../../angular-react-helper";
import { getAngularService } from '../../angular-react-helper';
import { Form } from 'enketo-core';
import { XMLParser } from 'fast-xml-parser';
import i18next from 'i18next';
import { logDebug } from "../../plugin/logger";

export type PrefillFields = {[key: string]: string};
export type PrefillFields = { [key: string]: string };

export type SurveyOptions = {
undismissable?: boolean;
Expand Down Expand Up @@ -37,12 +37,10 @@ function getXmlWithPrefills(xmlModel: string, prefillFields: PrefillFields) {
* @param opts object with options like 'prefilledSurveyResponse' or 'prefillFields'
* @returns XML string of an existing or prefilled model response, or null if no response is available
*/
export function getInstanceStr(xmlModel: string, opts: SurveyOptions): string|null {
export function getInstanceStr(xmlModel: string, opts: SurveyOptions): string | null {
if (!xmlModel) return null;
if (opts.prefilledSurveyResponse)
return opts.prefilledSurveyResponse;
if (opts.prefillFields)
return getXmlWithPrefills(xmlModel, opts.prefillFields);
if (opts.prefilledSurveyResponse) return opts.prefilledSurveyResponse;
if (opts.prefillFields) return getXmlWithPrefills(xmlModel, opts.prefillFields);
return null;
}

Expand All @@ -58,39 +56,39 @@ export function saveResponse(surveyName: string, enketoForm: Form, appConfig, op
const xmlParser = new window.DOMParser();
const xmlResponse = enketoForm.getDataStr();
const xmlDoc = xmlParser.parseFromString(xmlResponse, 'text/xml');
const xml2js = new XMLParser({ignoreAttributes: false, attributeNamePrefix: 'attr'});
const xml2js = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: 'attr' });
const jsonDocResponse = xml2js.parse(xmlResponse);
return EnketoSurveyAnswer.resolveLabel(surveyName, xmlDoc).then(rsLabel => {
const data: any = {
label: rsLabel,
name: surveyName,
version: appConfig.survey_info.surveys[surveyName].version,
xmlResponse,
jsonDocResponse,
};
if (opts.timelineEntry) {
let timestamps = EnketoSurveyAnswer.resolveTimestamps(xmlDoc, opts.timelineEntry);
if (timestamps === undefined) {
// timestamps were resolved, but they are invalid
return new Error(i18next.t('survey.enketo-timestamps-invalid')); //"Timestamps are invalid. Please ensure that the start time is before the end time.");
return EnketoSurveyAnswer.resolveLabel(surveyName, xmlDoc)
.then((rsLabel) => {
const data: any = {
label: rsLabel,
name: surveyName,
version: appConfig.survey_info.surveys[surveyName].version,
xmlResponse,
jsonDocResponse,
};
if (opts.timelineEntry) {
let timestamps = EnketoSurveyAnswer.resolveTimestamps(xmlDoc, opts.timelineEntry);
if (timestamps === undefined) {
// timestamps were resolved, but they are invalid
return new Error(i18next.t('survey.enketo-timestamps-invalid')); //"Timestamps are invalid. Please ensure that the start time is before the end time.");
}
// if timestamps were not resolved from the survey, we will use the trip or place timestamps
timestamps ||= opts.timelineEntry;
data.start_ts = timestamps.start_ts || timestamps.enter_ts;
data.end_ts = timestamps.end_ts || timestamps.exit_ts;
// UUID generated using this method https://stackoverflow.com/a/66332305
data.match_id = URL.createObjectURL(new Blob([])).slice(-36);
} else {
const now = Date.now();
data.ts = now / 1000; // convert to seconds to be consistent with the server
data.fmt_time = new Date(now);
}
// if timestamps were not resolved from the survey, we will use the trip or place timestamps
timestamps ||= opts.timelineEntry;
data.start_ts = timestamps.start_ts || timestamps.enter_ts;
data.end_ts = timestamps.end_ts || timestamps.exit_ts;
// UUID generated using this method https://stackoverflow.com/a/66332305
data.match_id = URL.createObjectURL(new Blob([])).slice(-36);
} else {
const now = Date.now();
data.ts = now/1000; // convert to seconds to be consistent with the server
data.fmt_time = new Date(now);
}
// use dataKey passed into opts if available, otherwise get it from the config
const dataKey = opts.dataKey || appConfig.survey_info.surveys[surveyName].dataKey;
return window['cordova'].plugins.BEMUserCache
.putMessage(dataKey, data)
.then(() => data);
}).then(data => data);
// use dataKey passed into opts if available, otherwise get it from the config
const dataKey = opts.dataKey || appConfig.survey_info.surveys[surveyName].dataKey;
return window['cordova'].plugins.BEMUserCache.putMessage(dataKey, data).then(() => data);
})
.then((data) => data);
}

const _getMostRecent = (answers) => {
Expand Down
Loading