Skip to content

Commit

Permalink
🔧 Start work on scenario creation.
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasGilg committed Dec 13, 2024
1 parent 5f86a96 commit 0b6da69
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 98 deletions.
19 changes: 14 additions & 5 deletions frontend/src/DataContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const DataProvider = ({children}: {children: React.ReactNode}) => {

const selectedDistrict = useAppSelector((state) => state.dataSelection.district.ags);
const selectedScenario = useAppSelector((state) => state.dataSelection.scenario);
const activeScenarios = useAppSelector((state) => state.dataSelection.activeScenarios);
const scenariosState = useAppSelector((state) => state.dataSelection.scenarios);
const selectedCompartment = useAppSelector((state) => state.dataSelection.compartment);
const referenceDate = useAppSelector((state) => state.dataSelection.simulationStart);
const selectedDate = useAppSelector((state) => state.dataSelection.date);
Expand Down Expand Up @@ -126,9 +126,17 @@ export const DataProvider = ({children}: {children: React.ReactNode}) => {
}
);

const activeScenarios = useMemo(() => {
return (
Object.entries(scenariosState)
.filter(([_, value]) => value.active && value.shown)

Check failure on line 132 in frontend/src/DataContext.tsx

View workflow job for this annotation

GitHub Actions / frontend

'_' is defined but never used
.map(([key]) => key) ?? []
);
}, [scenariosState]);

const {data: scenarioCardData} = useGetMultiScenarioInfectionDataQuery(
{
pathIds: activeScenarios ?? [],
pathIds: activeScenarios,
query: {
startDate: selectedDate!,
endDate: selectedDate!,
Expand All @@ -143,7 +151,7 @@ export const DataProvider = ({children}: {children: React.ReactNode}) => {

const {data: groupFilterData} = useGetMultiScenarioInfectionDataQuery(
{
pathIds: activeScenarios ?? [],
pathIds: activeScenarios,
query: {
startDate: selectedDate!,
endDate: selectedDate!,
Expand All @@ -155,13 +163,14 @@ export const DataProvider = ({children}: {children: React.ReactNode}) => {
},
},
{
skip: !activeScenarios || !selectedDate || !selectedDistrict || Object.keys(groupFilters).length === 0,
skip:
activeScenarios.length === 0 || !selectedDate || !selectedDistrict || Object.keys(groupFilters).length === 0,
}
);

const {data: lineChartData} = useGetMultiScenarioInfectionDataQuery(
{
pathIds: activeScenarios ?? [],
pathIds: activeScenarios,
query: {
nodes: [selectedDistrict],
compartments: [selectedCompartment!],
Expand Down
164 changes: 102 additions & 62 deletions frontend/src/components/ScenarioComponents/ScenarioLibrary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,69 +18,24 @@ import Close from '@mui/icons-material/Close';
import CardTitle from './CardsComponents/MainCard/CardTitle';
import WebAssetOff from '@mui/icons-material/WebAssetOff';
import {DataContext} from '../../DataContext';

function getState(
name: string,
shownScenarios: string[] | null,
activeScenarios: string[] | null
): 'active' | 'inactive' | 'hidden' {
if (shownScenarios?.includes(name) && activeScenarios?.includes(name)) {
return 'active';
}

if (shownScenarios?.includes(name)) {
return 'inactive';
}

return 'hidden';
}

type ScenarioState = {id: string; name: string; state: 'active' | 'inactive' | 'hidden'};

function useScenarioState(): Array<ScenarioState> {
const {scenarios} = useContext(DataContext);
const shownScenarios = useAppSelector((state) => state.dataSelection.shownScenarios);
const activeScenarios = useAppSelector((state) => state.dataSelection.activeScenarios);

return useMemo(() => {
if (!scenarios) {
return [];
}

const result = [
{
id: scenarios.find((scenario) => scenario.name === 'casedata')?.id ?? '',
name: 'casedata',
state: getState('casedata', shownScenarios, activeScenarios),
},
];

for (const scenario of scenarios) {
if (scenario.name === 'casedata') {
continue;
}
result.push({
id: scenario.id,
name: scenario.name,
state: getState(scenario.name, shownScenarios, activeScenarios),
});
}

return result;
}, [scenarios, shownScenarios, activeScenarios]);
}

function useHiddenScenarios() {
const scenarios = useScenarioState();

return useMemo(() => scenarios.filter((scenario) => scenario.state === 'hidden'), [scenarios]);
}
import LibraryAddOutlined from '@mui/icons-material/LibraryAddOutlined';

export default function ScenarioLibrary(): JSX.Element {
const {t} = useTranslation();
const theme = useTheme();

const libScenarios = useHiddenScenarios();
const {scenarios} = useContext(DataContext);
const scenariosState = useAppSelector((state) => state.dataSelection.scenarios);

const hiddenScenarios = useMemo(() => {
return Object.entries(scenariosState)
.filter(([_, value]) => !value.shown)
.map(([key]) => ({
id: key,
name: scenarios?.find((scenario) => scenario.id === key)?.name ?? 'unknown',
}));
}, [scenarios, scenariosState]);

const anchorRef = useRef<HTMLButtonElement>(null);

const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -171,8 +126,9 @@ export default function ScenarioLibrary(): JSX.Element {
marginTop: theme.spacing(2),
}}
>
{libScenarios.length > 0 ? (
libScenarios.map((scenario) => <LibraryCard key={scenario.id} {...scenario} />)
<NewScenarioCard />
{hiddenScenarios.length > 0 ? (
hiddenScenarios.map((scenario) => <LibraryCard key={scenario.id} {...scenario} />)
) : (
<Box
sx={{
Expand All @@ -196,7 +152,7 @@ export default function ScenarioLibrary(): JSX.Element {
);
}

function LibraryCard(props: Readonly<ScenarioState>): JSX.Element {
function LibraryCard(props: Readonly<{id: string; name: string}>): JSX.Element {
const dispatch = useAppDispatch();
const theme = useTheme();
const {t: tBackend} = useTranslation('backend');
Expand Down Expand Up @@ -246,7 +202,7 @@ function LibraryCard(props: Readonly<ScenarioState>): JSX.Element {
background: '#EEEEEEEE',
},
}}
onClick={() => dispatch(showScenario(props.name))}
onClick={() => dispatch(showScenario(props.id))}
>
<Box
id={`card-front-${props.id}`}
Expand Down Expand Up @@ -280,3 +236,87 @@ function LibraryCard(props: Readonly<ScenarioState>): JSX.Element {
</Box>
);
}

function NewScenarioCard(): JSX.Element {
const theme = useTheme();
const {t} = useTranslation();

return (
<Box
id={`new-scenario-card-root`}
sx={{
display: 'flex',
flexDirection: 'row',
color: theme.palette.divider,
width: 'min-content',
paddingLeft: theme.spacing(3),
paddingRight: theme.spacing(3),
}}
>
<Box
id='new-scenario-card-container'
sx={{
position: 'relative',
zIndex: 0,
flexGrow: 0,
flexShrink: 0,
width: '200px',
boxSizing: 'border-box',
marginX: '2px',
marginY: theme.spacing(2),
marginBottom: 0,
}}
>
<Box
id={`new-scenario-card-main-card`}
sx={{
position: 'relative',
zIndex: 0,
boxSizing: 'border-box',
height: '244px',
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
border: `2px solid ${theme.palette.primary.light}`,
borderRadius: '3px',
background: theme.palette.background.paper,
color: theme.palette.primary.main,
cursor: 'pointer',

'&:hover': {
background: '#EEEEEEEE',
},
}}
onClick={() => undefined}
>
<Box
id={`new-scenario-card-front`}
sx={{
marginTop: '6px',
marginBottom: '6px',
height: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
<CardTitle label={t('new-scenario')} />
<Box
sx={{
fontWeight: 'bolder',
fontSize: '3rem',
color: theme.palette.primary.light,
textAlign: 'center',
flexGrow: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
aria-label={t('new-scenario')}
>
<LibraryAddOutlined color='primary' fontSize='large' />
</Box>
</Box>
</Box>
</Box>
</Box>
);
}
94 changes: 63 additions & 31 deletions frontend/src/store/DataSelectionSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import {GroupFilter} from 'types/group';
*/
export type AGS = string;

export interface ScenarioState {
active: boolean;
shown: boolean;
colors: Array<string>;
}

/**
* This contains all the state, that the user can configure directly.
*
Expand All @@ -29,9 +35,7 @@ export interface DataSelection {
scenario: string | null;
compartment: string | null;
compartmentsExpanded: boolean | null;
shownScenarios: string[] | null;
activeScenarios: string[] | null;
scenarioColors: Record<string, Array<string>> | null;
scenarios: Record<string, ScenarioState>;
simulationStart: string | null;
minDate: string | null;
maxDate: string | null;
Expand All @@ -44,9 +48,7 @@ const initialState: DataSelection = {
scenario: null,
compartment: null,
compartmentsExpanded: null,
shownScenarios: [],
activeScenarios: [],
scenarioColors: null,
scenarios: {},
simulationStart: null,
minDate: null,
maxDate: null,
Expand All @@ -61,14 +63,64 @@ export const DataSelectionSlice = createSlice({
initialState,
reducers: {
setActiveScenario(state, action: PayloadAction<{id: string; state: boolean}>) {
if (action.payload.state && !state.activeScenarios?.includes(action.payload.id)) {
state.activeScenarios = [...(state.activeScenarios ?? []), action.payload.id];
} else {
state.activeScenarios = state.activeScenarios?.filter((id) => id !== action.payload.id) ?? [];
if (!state.scenarios) {
state.scenarios = {};
}

if (!state.scenarios[action.payload.id]) {
state.scenarios[action.payload.id] = {
active: false,
shown: false,
colors: [],
};
}

state.scenarios[action.payload.id] = {...state.scenarios[action.payload.id], active: action.payload.state};
},
showScenario(state, action: PayloadAction<string>) {
if (!state.scenarios) {
state.scenarios = {};
}

if (!state.scenarios[action.payload]) {
state.scenarios[action.payload] = {
active: false,
shown: false,
colors: [],
};
}

state.scenarios[action.payload] = {...state.scenarios[action.payload], shown: true};
},
hideScenario(state, action: PayloadAction<string>) {
if (!state.scenarios) {
state.scenarios = {};
}

if (!state.scenarios[action.payload]) {
state.scenarios[action.payload] = {
active: false,
shown: false,
colors: [],
};
}

state.scenarios[action.payload] = {...state.scenarios[action.payload], shown: false};
},
setScenarioColors(state, action: PayloadAction<{id: string; colors: Array<string>}>) {
state.scenarioColors = {...state.scenarioColors, [action.payload.id]: action.payload.colors};
if (!state.scenarios) {
state.scenarios = {};
}

if (!state.scenarios[action.payload.id]) {
state.scenarios[action.payload.id] = {
active: false,
shown: false,
colors: [],
};
}

state.scenarios[action.payload.id] = {...state.scenarios[action.payload.id], colors: action.payload.colors};
},
setGroupFilters(state, action: PayloadAction<Record<string, GroupFilter>>) {
state.groupFilters = action.payload;
Expand Down Expand Up @@ -144,26 +196,6 @@ export const DataSelectionSlice = createSlice({
state.groupFilters[action.payload].isVisible = !state.groupFilters[action.payload].isVisible;
}
},
showScenario(state, action: PayloadAction<string>) {
if (!state.shownScenarios) {
state.shownScenarios = ['casedata', 'baseline'];
}

const index = state.shownScenarios.indexOf(action.payload);
if (index === -1) {
state.shownScenarios.push(action.payload);
}
},
hideScenario(state, action: PayloadAction<string>) {
if (!state.shownScenarios) {
state.shownScenarios = ['casedata', 'baseline'];
}

const index = state.shownScenarios.indexOf(action.payload);
if (index !== -1) {
state.shownScenarios.splice(index, 1);
}
},
},
});

Expand Down

0 comments on commit 0b6da69

Please sign in to comment.