Skip to content
Open
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
102 changes: 76 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,96 @@
# Getting Started with Create React App
# Survey Viewer App

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
Survey Viewer is an online application to view/run a Survey from a json Survey Definition used by [Influenzanet Survey Engine](https://github.com/influenzanet/survey-engine.ts)

## Available Scripts
## Installing

In the project directory, you can run:
This application is built as a Single Page Application with React Framework.

### `yarn start`
To install the application, clone this repo and run yarn to install the dependencies

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
```bash
yarn
````

The page will reload if you make edits.\
You will also see any lint errors in the console.
To run in in dev environment
```bash
yarn start
```

### `yarn test`
To build the app as a standalone website:
```bash
yarn build
```

Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
The deployable website will be in 'build' directory

### `yarn build`
## Survey Definition Source

Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
2 ways are proposed to view a survey :

The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
- Upload a JSON file containing the Survey Definition
- Use a Survey Provider Service : a list of available survey will be loaded from the service and downloaded directly from the service

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
An implementation for survey provider service is available in [Grippenet repository](https://github.com/grippenet/survey-provider-service). It serves survey list from files in a directory.

### `yarn eject`
To use the service provider, the application must be compiled with some environment variables

**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
- REACT_APP_SURVEY_URL: URL of the survey service
- REACT_APP_CSP_CONNECT_URLS: the URL also must be added to this variable to enable the app to connect to the survey service

If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Example
```bash
REACT_APP_SURVEY_URL=https://your.survey.service
# You may add
REACT_APP_CSP_CONNECT_URLS=$REACT_APP_SURVEY_URL ...
```

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
# Customize

You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
Warning: These customizations are useable only after the application is built. So you need to use either dev server (`yarn start`) or to rebuild the static website (`yarn build`)

## Learn More
## Add a custom Response Component

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To register a customComponent, you have to modify the script 'localConfig.ts' and make the function registerCustomComponents return the list of your component

To learn React, check out the [React documentation](https://reactjs.org/).
```ts
export const registerCustomComponents = () : CustomSurveyResponseComponent[] | undefined => {
return [
{
name: 'awesomeComponent', // Name of your component as it will be used in `role` field of the survey definition
component: MyAwesomeComponent
}
];
}
```

## Add Flags

You can tell to the survey viewer which flags are handled by your platform (and for each what are the expected values). The Survey Context editor will propose the known list and enable more friendly editor than json editor.

You had to customize the

```ts
export const registerParticipantFlags = () : ParticipantFlags => {
return {
'myflagkey': {
label: 'The label to tell the user what is the purpose of this flag'
values: [
{
value: '1' // The value the flag can have
label: 'A friendly label to explain what is this value'
}
]
},
// A real example (the 'prev' flag used in Influenzanet platform)
'prev': {
label:"Has ongoing symptoms",
values: [
{value:"0", label:"Does not have ongoing symptoms"}
{value: "1", label:"Has ongoing symptoms"}
],
},
};
}

```
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
"@types/node": "^16.11.12",
"@types/react": "^17.0.40",
"@types/react-dom": "^17.0.13",
"bootstrap": "^5.2.2",
"case-web-ui": "^1.14.0",
"bootstrap": "^5.2.3",
"case-web-ui": "^1.14.7",
"clsx": "^1.2.1",
"date-fns": "^2.29.3",
"react": "^17.0.2",
"react-bootstrap": "^2.5.0",
"react-bootstrap": "^2.7.3",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"survey-engine": "1.2.0",
"survey-engine": "1.2.1",
"typescript": "^4.5.5",
"web-vitals": "^1.0.1"
},
Expand Down
139 changes: 35 additions & 104 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
import React, { useState } from 'react';
import { Survey, SurveyContext, SurveySingleItemResponse } from 'survey-engine/data_types';
import { Survey } from 'survey-engine/data_types';
import Navbar from './components/NavbarComp';
import SimulationSetup, { defaultSimulatorUIConfig, defaultSurveyContext } from './components/SimulationSetup';
import SurveyLoader from './components/SurveyLoader';
import SurveyMenu from './components/SurveyMenu';
import SurveyServiceLoader from './components/SurveyServiceLoader';
import SurveySimulator, { SimulatorUIConfig } from './components/SurveySimulator';
import { registerCustomComponents, registerParticipantFlags } from './localConfig';

interface AppState {
selectedLanguage?: string;
languageCodes?: string[];
selectedAppLanguage?: string; // Language of the Application (for future translation)
surveyKey?: string;
survey?: Survey;
surveyContext: SurveyContext;
prefillsFile?: File;
prefillValues?: SurveySingleItemResponse[],
screen: Screens;
simulatorUIConfig: SimulatorUIConfig;
}

type Screens = 'loader' | 'menu' | 'simulation-setup' | 'simulator';
// Application translations, currently only english is provided.
const availableAppLanguages : string[] = ['en'];

const customResponseComponents = registerCustomComponents();
const participantFlags = registerParticipantFlags();

type Screens = 'loader' | 'menu';

const initialState: AppState = {
screen: 'loader',
simulatorUIConfig: { ...defaultSimulatorUIConfig },
surveyContext: { ...defaultSurveyContext },
}

const surveyProviderUrl = process.env.REACT_APP_SURVEY_URL ?? "";

console.log("Using provider "+ surveyProviderUrl);

// Default languages to show in the survey. If several use the first available in the survey.
// You can customize the default survey languages by definiing REACT_APP_DEFAULT_SURVEY_LANGUAGES env variable
const defaultSurveyLanguages : string[] = (()=>{
let defaultLang: string[] = ['en'];
const envLang = process.env.REACT_APP_DEFAULT_SURVEY_LANGUAGES ?? "";
if(envLang) {
defaultLang = envLang.split(',').map(v => v.trim());
}
return defaultLang;
})();


const App: React.FC = () => {
const [appState, setAppState] = useState<AppState>({
...initialState
Expand All @@ -42,20 +52,14 @@ const App: React.FC = () => {
return;
}

const languageCodes = surveyObject.props?.name?.map(o => o.code);
if (!languageCodes || languageCodes.length < 1) {
alert('Languages cannot be extracted');
return;
}

const surveyDef = surveyObject.surveyDefinition;

const surveyKey = surveyDef.key;

setAppState({
...initialState,
selectedLanguage: languageCodes[0],
languageCodes: languageCodes,
selectedAppLanguage: availableAppLanguages[0],
surveyKey: surveyKey,
screen: 'menu',
survey: surveyObject
Expand Down Expand Up @@ -83,118 +87,45 @@ const App: React.FC = () => {
}}>
<div className="row flex-grow-1">
<div className="col-12">
<SurveyLoader onSurveyLoaded={onLoadSurvey} />
{
{
surveyProviderUrl ? <SurveyServiceLoader onSurveyLoaded={onLoadSurvey} surveyProviderUrl={surveyProviderUrl} /> : null
}
}
<SurveyLoader onSurveyLoaded={onLoadSurvey}/>
</div>
</div>
</div>

case 'menu':
if (!appState.selectedLanguage || !appState.survey) {
if (!appState.selectedAppLanguage || !appState.survey) {
reset();
return null;
}
return <SurveyMenu
selectedLangue={appState.selectedLanguage}
participantFlags={participantFlags}
defaultSurveyLanguages={defaultSurveyLanguages}
survey={appState.survey}
onOpenSimulator={() => navigateTo('simulation-setup')}
customResponseComponents={customResponseComponents}
onExit={() => {
reset()
}}
/>
case 'simulation-setup':
return <SimulationSetup
onStart={() => navigateTo('simulator')}
onExit={() => navigateTo('menu')}
prefillsFile={appState.prefillsFile}
onPrefillChanged={(prefills?: File) => {
if (prefills) {
const reader = new FileReader()

reader.onabort = () => console.log('file reading was aborted')
reader.onerror = () => console.log('file reading has failed')
reader.onload = () => {
// Do whatever you want with the file contents
const res = reader.result;
if (!res || typeof (res) !== 'string') {
console.error('TODO: handle file upload error')
return;
}
const content = JSON.parse(res);
setAppState(prev => {
return {
...prev,
prefillsFile: prefills,
prefillValues: content,

}
})
}
reader.readAsText(prefills)
} else {
setAppState(prev => {
return {
...prev,
prefillsFile: prefills,
prefillValues: []

}
})
}



}}
currentSurveyContext={appState.surveyContext}
onSurveyContextChanged={(config) => setAppState(prev => {
return {
...prev,
surveyContext: {
...config
}
}
})}
currentSimulatorUIConfig={appState.simulatorUIConfig}
onSimulatorUIConfigChanged={(config) => setAppState(prev => {
return {
...prev,
simulatorUIConfig: {
showKeys: config.showKeys,
texts: { ...config.texts }
}
}
})}
/>
case 'simulator':
return <SurveySimulator
config={appState.simulatorUIConfig}
surveyAndContext={
appState.survey ? {
survey: appState.survey,
context: appState.surveyContext
} : undefined
}
prefills={appState.prefillValues}
selectedLanguage={appState.selectedLanguage}
onExit={() => navigateTo('simulation-setup')}
/>
}
}


return (
<div className="d-flex flex-column overflow-scroll" style={{
minHeight: '100vh'
}}>
<Navbar
surveyName={appState.surveyKey}
selectedLanguage={appState.selectedLanguage}
languagecodes={appState.languageCodes}
selectedLanguage={appState.selectedAppLanguage}
languagecodes={availableAppLanguages}
onSelectLanguage={(code) => {
setAppState(prev => {
return {
...prev,
selectedLanguage: code
selectedAppLanguage: code
}
})
}}
Expand Down
Loading